📄 wavplayer.java
字号:
/* * @(#)WavPlayer.java 1.51 02/11/05 @(#) * * Copyright (c) 1996-2002 Sun Microsystems, Inc. All rights reserved. * PROPRIETARY/CONFIDENTIAL * Use is subject to license terms. */package com.sun.mmedia;import javax.microedition.media.*;import javax.microedition.media.control.*;import com.sun.mmedia.BasicPlayer;import java.io.IOException;/** * This class implements the wav (audio/x-wav) audio player. */public class WavPlayer extends BasicPlayer implements Runnable { /** * the duration of this player */ private long duration = TIME_UNKNOWN; /** * Audio format parameters: sampleRate, channel number, sample size */ private int sampleRate, channels, sampleSizeInBits; /** * byte rate and alignment */ private int bytesPerSecond, blockAlign; /** * Last time when the player is stopped, how many samples have been * played and the media time at that point */ private long lastPos, origin; /** * waveout lock obj */ private Object waveLock = new Object(); /** * play thread obj */ private Thread playThread; /** * a status flag indicating whether this player is started */ private boolean started; /** * a status flag indicating whether this player has been deallocated */ private boolean interrupted; /** * a play lock obj */ private Object playLock = new Object(); /** * In source stream, the start position of pcm data */ private long startpt; /** * where the pcm data ends in source stream */ private long endpt = Long.MAX_VALUE; /** * the pointer to native wave out data structure. */ private int peer; /** * The buffer length to read from source stream every time. */ private int bufLen; /** * the data buffer read from source stream */ private byte[] buffer; /** * a pause lock obj */ private Object pauseLock = new Object(); /** * a flag indicating if this player could be paused */ private boolean canPause = true; /** * Open the audio device in a particular format. * * @param sampleRate the sampleRate of the intended format * @param bits how many bits per sample of the intended format * @param channels mono or stereo * @return the pointer to the wave out data structure, if succeeded. * non-positive number if failed. */ private native int nOpen(int sampleRate, int bits, int channels); /** * Pass a data buffer to native code. * * @param peer the pointer to the native wave out data structure. * @param data the data buffer to be written to native wave out * @param offset the offset in data buffer. * @param len how many bytes of data to be written. * @return the acutal number of bytes has been written. */ private native int nWrite(int peer, byte[] data, int offset, int len); /** * Utility functions for wave out * * @param peer the pointer to the native wave out data structure. * @param code functionality code * @param param parameters for a particular functionality. * @return status. */ private native int nCommon(int peer, int code, int param); /** * Return the content type. * * @return the wav content type. */ public String getContentType() { chkClosed(true); return "audio/x-wav"; } /** * Parse the input, realize the player */ protected void doRealize() throws MediaException { try { readHeader(); startpt = getStrmLoc(); } catch (IOException e) { throw new MediaException(e.getMessage()); } } /** * Get the duration of the media represented by this object. * The value returned is the media's duration * when played at the default rate. * If the duration can't be determined (for example, the media object is * presenting live * video) <CODE>getDuration</CODE> returns * <CODE>TIME_UNKNWON</CODE>. * * @return A <CODE>long</CODE> object representing the duration or * TIME_UNKNWON. */ public long doGetDuration() { return duration; } /** * Parse the Wave audio file header. * */ protected void readHeader() throws IOException { if (readInt() != 0x46464952) // RIFF throw new IOException("malformed wave data"); readInt(); if (readInt() != 0x45564157) // WAVE throw new IOException("malformed wave data"); // Only the required chunks 'fmt ' and 'data' are supported. // There are no restrictions upon the order of the chunks within // a WAVE file, with the exception that the Format chunk must precede // the Data chunk. // Skip all chunks until you reach the 'fmt ' chunk while (readInt() != 0x20746D66) { // FMT int size = readInt(); skipStrm(size); } // Handle Format chunk 'fmt ', formatSize int fmtsize = readInt(); if (fmtsize < 16) throw new IOException("bad fmt chunk"); fmtsize -= 16; int encoding = readShort(); if (encoding != 0x0001) // WF_PCM throw new IOException("only supports PCM"); channels = readShort(); sampleRate = readInt(); bytesPerSecond = readInt(); blockAlign = readShort(); sampleSizeInBits = readShort(); // bytesPerSecond and blockAlign might not accurate in the file // need to calculate bytesPerSecond = sampleRate * channels * sampleSizeInBits / 8; blockAlign = channels * sampleSizeInBits / 8; // skip the rest of the format chunk if (fmtsize > 0) skipStrm(fmtsize); // Skip all chunks until you reach the 'data' chunk while (readInt() != 0x61746164) { // DATA int size = readInt(); skipStrm(size); } // Handle Format chunk 'data' long dataSize = readInt(); endpt = getStrmLoc() + dataSize; if ((channels * sampleSizeInBits / 8) == blockAlign) { duration = (dataSize*1000000L)/bytesPerSecond; } else { duration = TIME_UNKNOWN; } return; } /** * Get the resources ready. */ protected void doPrefetch() throws MediaException { // Open the audio device. synchronized (waveLock) { if (peer == 0) { peer = nOpen(sampleRate, sampleSizeInBits, channels); if (peer <= 0) { throw new MediaException("can't open audio device"); } bufLen = nCommon(peer, 9, 0); buffer = new byte[bufLen]; if (getLevel() == -1) setLevel(nCommon(peer, 8, 0)); } } if (isMuted()) { nCommon(peer, 7, 0); } else { nCommon(peer, 7, getLevel()); } } /** * Start the playback. * @return the status if the player has been successfully started. */ protected boolean doStart() { if (started) return true; started = true; // Start the playback loop. synchronized (playLock) { if (playThread == null) { playThread = new Thread(this); playThread.start(); } else playLock.notifyAll(); } nCommon(peer, 2, 0); // RESUME return true; } /** * Stop the playback loop. */ protected void doStop() { if (!started) return; started = false; synchronized (pauseLock) { while (!canPause) try { pauseLock.wait(); } catch (Exception ex) { } nCommon(peer, 1, 0); // PAUSE pauseLock.notifyAll(); } } /** * Deallocate the exclusing resource. */ protected void doDeallocate() { // Interrupt the playback loop. // If the playThread had not been started, we'll need // to explicitly close the device. if (state == PREFETCHED && playThread == null) { nCommon(peer, 5, 0); // CLOSE return; } synchronized (playLock) { interrupted = true; // Wake up the run loop if it was stopped. playLock.notifyAll(); // Wait for the playback loop to completely stop before // returning. There's a maximum wait limit set here in // case anything goes wrong. if (playThread != null) { try { playLock.wait(5000); } catch (Exception e) {} } } } /** * Close the player. */ protected void doClose() { // Deallocate would have been called before this. // So all the resources should have been released. } /** * The worker method to actually set player's media time. * * @param now The new media time in microseconds. * @return The actual media time set in microseconds. * @exception MediaException Thrown if an error occurs * while setting the media time. */ protected long doSetMediaTime(long now) throws MediaException { long ret = now; try { long pp = (bytesPerSecond * now / 1000000L / blockAlign) * blockAlign + startpt; if (getState() == STARTED) doStop(); nCommon(peer, 3, 0); // FLUSH lastPos = nCommon(peer, 6, 0); // SAMPLES_PLAYED ret = seekStrm(pp); ret = (ret-startpt) * 1000000L / bytesPerSecond; origin = ret; if (getState() == STARTED) doStart(); } catch (Exception e) { throw new MediaException(e.getMessage()); } return ret; } /** * Gets this player's current <i>media time</i> * in microseconds. * * @return The current <i>media time</i> in microseconds. */ public long doGetMediaTime() { long pos = 0; long mtime; synchronized (waveLock) { if (sampleRate == 0) return 0; pos = nCommon(peer, 6, 0); /* SAMPLES_PLAYED */ } // Media time is in micro-seconds mtime = ((pos - lastPos) * 1000000L) / sampleRate + origin; return (mtime < 0 ? 0 : mtime); } /** * The worker method to actually obtain the control. * * @param type the class name of the <code>Control</code>. * @return <code>Control</code> for the class or interface * name. */ protected Control doGetControl(String type) { if ((getState() >= REALIZED) && type.equals("javax.microedition.media.control.VolumeControl")) { return this; } return null; } /** * error string */ private String errMsg = null; /** * Read the data from the source and write them to the waveout in native. * @return the status. */ private boolean doProcess() { int len = 0, wlen = 0; try { len = readBytes(buffer, 0, bufLen); } catch (IOException ioe) { errMsg = ioe.getMessage(); return false; } if (len < 1) { synchronized (pauseLock) { canPause = false; try { while (nCommon(peer, 4, 0) != 1) // DRAIN pauseLock.wait(20); } catch (Exception ex) {} canPause = true; pauseLock.notifyAll(); } Thread.yield(); started = false; sendEvent(PlayerListener.END_OF_MEDIA, new Long(getMediaTime())); return true; } synchronized (pauseLock) { canPause = false; while ((wlen = nWrite(peer, buffer, 0, len)) == 0) { try { pauseLock.wait(16); } catch (Exception ex) {} } canPause = true; pauseLock.notifyAll(); } if (wlen == -1) return false; return true; } /** * Main process loop driving the media flow. */ public void run() { boolean statusOK = true; while (true) { while (!interrupted && started && statusOK) { statusOK = doProcess(); Thread.yield(); } synchronized (playLock) { if (interrupted || !statusOK) break; try { playLock.wait(); } catch (Exception ex) {} } } // end of while (true) // Close the audio device, exit safely out of the process loop. nCommon(peer, 3, 0); // FLUSH synchronized (waveLock) { nCommon(peer, 5, 0); // CLOSE peer = 0; interrupted = started = false; } lastPos = 0; synchronized (playLock) { playThread = null; // Notify the blocking deallocate that we are done with // the process loop. playLock.notifyAll(); } if (!statusOK) { if (stream != null) { try { stream.close(); } catch (IOException ex) {} } sendEvent(PlayerListener.ERROR, errMsg); } } /** * ==================================== * Read calls to read from SourceStream * ==================================== */ /** * Read bytes from source stream. * @param array the byte array to hold the data * @param offset the offset in the byte array * @param num the number of bytes to be read * @return the actual number of bytes has been read */ private int readBytes(byte[] array, int offset, int num) throws IOException { if (num == 0) { return 0; } long cpos = getStrmLoc(); long available = endpt - cpos; if (available <= 0) { return -1; } if (num > available) { num = (int) available; } int rem = num; int read = 0; while (rem > 0) { try { read = readStrm(array, offset, rem); } catch (IOException e) { return -1; } if (read == -1) { // End of stream if (rem == num) { return -1; } else { return num - rem; } } rem -= read; offset += read; } return num; } /** * temporary buffer */ private byte [] intArray = new byte[4]; /** * Read an integer from source stream * @return the integer read. * @throws IOExeption if there is an error */ private int readInt() throws IOException { if (readBytes(intArray, 0, 4) < 4) throw new IOException("malformed wave data"); return ((intArray[3] & 0xFF) << 24) | ((intArray[2] & 0xFF) << 16) | ((intArray[1] & 0xFF) << 8) | (intArray[0] & 0xFF); } /** * Read a short from source stream * @return the short read * @throws IOException if there is an error */ private short readShort() throws IOException { if (readBytes(intArray, 0, 2) < 2) throw new IOException("malformed wave data"); return (short) (((intArray[1] & 0xFF) << 8) | (intArray[0] & 0xFF)); } /** * ========================== * Methods for VolumeControl. * ========================== */ /** * The worker method to actually obtain the control. * * @param vol the volume level to be set. * @return the actual level has been set. */ protected int doSetLevel(int vol) { // 0 <= vol <= 100 synchronized (waveLock) { if (peer > 0) { nCommon(peer, 7, vol); // SETVOL } } return vol; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -