public class AndroidAudioDevice { AudioTrack track; short[] buffer = new short[1024]; public AndroidAudioDevice( ) { int minSize =AudioTrack.getMinBufferSize( 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT ); track = new AudioTrack( AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, minSize, AudioTrack.MODE_STREAM); track.play(); } public void writeSamples(float[] samples) { fillBuffer( samples ); track.write( buffer, 0, samples.length ); } private void fillBuffer( float[] samples ) { if( buffer.length < samples.length ) buffer = new short[samples.length]; for( int i = 0; i < samples.length; i++ ) buffer[i] = (short)(samples[i] * Short.MAX_VALUE);; } }
All the code in the onset detection tutorial worked with PCM data encoded as floats in the range [-1,1]. We want to emulate this here so i wrote a little method called AndroidAudioDevice.writeSamples( ) which takes a float array of mono float PCM samples and writes it to the hardware. For this the float samples have to be converted to 16-bit signed PCM which is done in the AudioDevice.fillBuffer() method, no rocket science here. Once we have the converted PCM we simply write it to the AudioTrack via AudioTrack.write() which takes a short array (our PCM samples), an offset into the array and the number of samples to use from the offset on. Extremely simple, even more than the equivalent Java Sound class SourceDataLine.
Now there’s a couple of interesting things about AudioTrack. First off, if you don’t write to it constantly it will pause itself to not hog any further system resources. Upon the next write it will start playback again introducing a wake up lag. Another not so nice thing is that due to the internal buffer of AudioTrack which has to be filled up completely before it is send to the hardware there’s noticeable lag between the time you write your first samples and when you hear them being played back. The minimum buffer size i get on my Milestone for the configuration is 8192, sadly the documentation doesn’t tell wheter that’s bytes or samples. If it’s bytes we divide that by two and then by the sampling frequency to get the total lag introduced by the internal buffer: 8192 / 2 / 44100 = 0,092 seconds, so nearly 100ms. That’s noticeable. In case the minimum buffer size is in samples it get’s even worse. The lag will be 200ms in that case. So that’S what you have to expect when using this class. Writting a software mixer based on AudioTrack is possible as long as you don’t need low latency. Synthesizing sounds each time the screen is touched for example is a bad idea as the lag is more than noticeable. Another property of AudioTrack is that the AudioTrack.write() method blocks. If you want to use it in a game you should do all your audio mixing in a seperate thread.
Still, the class is pretty niffty and it makes it easy to port all the examples from the onset detection tutorial to Android. I tested it with the WaveDecoder class and it worked like a charm, not eating up to much system resources while doing its thing. Here’s the sine wave generator sample ported to android:
public class AudioTest extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); new Thread( new Runnable( ) { public void run( ) { final float frequency = 440; float increment = (float)(2*Math.PI) * frequency / 44100; // angular increment for each sample float angle = 0; AudioDevice device = new AndroidAudioDevice( ); float samples[] = new float[1024]; while( true ) { for( int i = 0; i < samples.length; i++ ) { samples[i] = (float)Math.sin( angle ); angle += increment; } device.writeSamples( samples ); } } } ).start(); } }
Source: apistudios
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.