AndroidAudioPlayer.java (5088B)
1 package be.tarsos.dsp.io.android; 2 3 import android.media.AudioFormat; 4 import android.media.AudioManager; 5 import android.media.AudioTrack; 6 import android.util.Log; 7 8 import be.tarsos.dsp.AudioEvent; 9 import be.tarsos.dsp.AudioProcessor; 10 import be.tarsos.dsp.io.TarsosDSPAudioFloatConverter; 11 import be.tarsos.dsp.io.TarsosDSPAudioFormat; 12 13 /** 14 * Plays audio from an {@link be.tarsos.dsp.AudioDispatcher} or {@link be.tarsos.dsp.AudioGenerator} 15 * on an Android {@link AudioTrack}. This class only supports mono, 16 bit PCM. Depending on your device, 16 * some sample rates could not be supported. This class uses the method that writes floats 17 * to {@link android.media.AudioTrack} which is only introduced in Android API Level 21. 18 * 19 * @author Alex Mikhalev 20 * @author Joren Six 21 * @see AudioTrack 22 */ 23 public class AndroidAudioPlayer implements AudioProcessor { 24 /** 25 * The default stream type to use. 26 */ 27 public static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_MUSIC; 28 private static final String TAG = "AndroidAudioPlayer"; 29 30 private final AudioTrack audioTrack; 31 32 33 /** 34 * Constructs a new AndroidAudioPlayer from an audio format, default buffer size and stream type. 35 * 36 * @param audioFormat The audio format of the stream that this AndroidAudioPlayer will process. 37 * This can only be 1 channel, PCM 16 bit. 38 * @param bufferSizeInSamples The requested buffer size in samples. 39 * @param streamType The type of audio stream that the internal AudioTrack should use. For 40 * example, {@link AudioManager#STREAM_MUSIC}. 41 * @throws IllegalArgumentException if audioFormat is not valid or if the requested buffer size is invalid. 42 * @see AudioTrack 43 */ 44 public AndroidAudioPlayer(TarsosDSPAudioFormat audioFormat, int bufferSizeInSamples, int streamType) { 45 if (audioFormat.getChannels() != 1) { 46 throw new IllegalArgumentException("TarsosDSP only supports mono audio channel count: " + audioFormat.getChannels()); 47 } 48 49 // The requested sample rate 50 int sampleRate = (int) audioFormat.getSampleRate(); 51 52 //The buffer size in bytes is twice the buffer size expressed in samples if 16bit samples are used: 53 int bufferSizeInBytes = bufferSizeInSamples * audioFormat.getSampleSizeInBits()/8; 54 55 // From the Android API about getMinBufferSize(): 56 // The total size (in bytes) of the internal buffer where audio data is read from for playback. 57 // If track's creation mode is MODE_STREAM, you can write data into this buffer in chunks less than or equal to this size, 58 // and it is typical to use chunks of 1/2 of the total size to permit double-buffering. If the track's creation mode is MODE_STATIC, 59 // this is the maximum length sample, or audio clip, that can be played by this instance. See getMinBufferSize(int, int, int) to determine 60 // the minimum required buffer size for the successful creation of an AudioTrack instance in streaming mode. Using values smaller 61 // than getMinBufferSize() will result in an initialization failure. 62 int minBufferSizeInBytes = AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT); 63 if(minBufferSizeInBytes > bufferSizeInBytes){ 64 throw new IllegalArgumentException("The buffer size should be at least " + (minBufferSizeInBytes/(audioFormat.getSampleSizeInBits()/8)) + " (samples) according to AudioTrack.getMinBufferSize()."); 65 } 66 67 //http://developer.android.com/reference/android/media/AudioTrack.html#AudioTrack(int, int, int, int, int, int) 68 audioTrack = new AudioTrack(streamType, sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSizeInBytes,AudioTrack.MODE_STREAM); 69 70 audioTrack.play(); 71 } 72 73 /** 74 * Constructs a new AndroidAudioPlayer from an audio format. 75 * 76 * @param audioFormat The audio format that this AndroidAudioPlayer will process. 77 * @see AndroidAudioPlayer#AndroidAudioPlayer(TarsosDSPAudioFormat, int, int) 78 */ 79 public AndroidAudioPlayer(TarsosDSPAudioFormat audioFormat) { 80 this(audioFormat, 4096, DEFAULT_STREAM_TYPE); 81 } 82 83 /** 84 * {@inheritDoc} 85 */ 86 @Override 87 public boolean process(AudioEvent audioEvent) { 88 int overlapInSamples = audioEvent.getOverlap(); 89 int stepSizeInSamples = audioEvent.getBufferSize() - overlapInSamples; 90 byte[] byteBuffer = audioEvent.getByteBuffer(); 91 92 //int ret = audioTrack.write(audioEvent.getFloatBuffer(),overlapInSamples,stepSizeInSamples,AudioTrack.WRITE_BLOCKING); 93 int ret = audioTrack.write(byteBuffer,overlapInSamples*2,stepSizeInSamples*2); 94 if (ret < 0) { 95 Log.e(TAG, "AudioTrack.write returned error code " + ret); 96 } 97 return true; 98 } 99 100 /** 101 * {@inheritDoc} 102 */ 103 @Override 104 public void processingFinished() { 105 audioTrack.flush(); 106 audioTrack.stop(); 107 audioTrack.release(); 108 } 109 }