Android AudioTrack class for playing piano sounds-Collection of common programming errors
im trying to implement a piano app, where user can play on a virtual piano shown on the display. Because of low latency im trying to use the AudioTrack Class, which makes a lot of trouble. My Problem is to play the sounds fast. At the moment I use buttons to test it. If i push the buttons to fast, i get this exceptions:
09-11 15:06:19.089: E/AudioTrack(3729): AudioFlinger could not create track, status: -12
09-11 15:06:19.097: E/AudioTrack-JNI(3729): Error initializing AudioTrack
09-11 15:06:19.097: E/AudioTrack-Java(3729): [ android.media.AudioTrack ] Error code -20 when initializing AudioTrack.
09-11 15:06:19.097: D/AndroidRuntime(3729): Shutting down VM
09-11 15:06:19.097: W/dalvikvm(3729): threadid=1: thread exiting with uncaught exception (group=0x4001d888)
09-11 15:06:19.097: E/AndroidRuntime(3729): FATAL EXCEPTION: main
09-11 15:06:19.097: E/AndroidRuntime(3729): java.lang.IllegalStateException: play() called on uninitialized AudioTrack.
09-11 15:06:19.097: E/AndroidRuntime(3729): at android.media.AudioTrack.play(AudioTrack.java:764)
09-11 15:06:19.097: E/AndroidRuntime(3729): at com.example.audiotrackvssoundpoollayer.MainActivity.onClick(MainActivity.java:216)
09-11 15:06:19.097: E/AndroidRuntime(3729): at android.view.View.performClick(View.java:2461)
09-11 15:06:19.097: E/AndroidRuntime(3729): at android.view.View$PerformClick.run(View.java:8890)
09-11 15:06:19.097: E/AndroidRuntime(3729): at android.os.Handler.handleCallback(Handler.java:587)
09-11 15:06:19.097: E/AndroidRuntime(3729): at android.os.Handler.dispatchMessage(Handler.java:92)
09-11 15:06:19.097: E/AndroidRuntime(3729): at android.os.Looper.loop(Looper.java:123)
09-11 15:06:19.097: E/AndroidRuntime(3729): at android.app.ActivityThread.main(ActivityThread.java:4632)
09-11 15:06:19.097: E/AndroidRuntime(3729): at java.lang.reflect.Method.invokeNative(Native Method)
09-11 15:06:19.097: E/AndroidRuntime(3729): at java.lang.reflect.Method.invoke(Method.java:521)
09-11 15:06:19.097: E/AndroidRuntime(3729): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:858)
09-11 15:06:19.097: E/AndroidRuntime(3729): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
09-11 15:06:19.097: E/AndroidRuntime(3729): at dalvik.system.NativeStart.main(Native Method)
I’m using the AudioTrack in Static Mode. Every time i like to play a sound i have to create a new AudioTrack Object with new AudioTrack(….), then write(…) and play(….)
I think it isnt able to instantiate fast enough, therefore the exceptions…
Here’s the code: The sounds are stored in a hashmap in a different async task class
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button buttonA1 = (Button) findViewById(R.id.buttonA1);
buttonA1.setOnClickListener(this);
Button buttonA2 = (Button) findViewById(R.id.buttonA2);
buttonA2.setOnClickListener(this);
Button buttonA3 = (Button) findViewById(R.id.buttonA3);
buttonA3.setOnClickListener(this);
Button buttonA4 = (Button) findViewById(R.id.buttonA4);
buttonA4.setOnClickListener(this);
Button buttonA5 = (Button) findViewById(R.id.buttonA5);
buttonA5.setOnClickListener(this);
Button buttonA6 = (Button) findViewById(R.id.buttonA6);
buttonA6.setOnClickListener(this);
Button buttonA7 = (Button) findViewById(R.id.buttonA7);
buttonA7.setOnClickListener(this);
Button buttonA8 = (Button) findViewById(R.id.buttonA8);
buttonA8.setOnClickListener(this);
//***AudioTrack***
AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
float actualVolume = (float) audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
float maxVolume = (float) audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
volume = actualVolume / maxVolume;
minBufferSize = AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT);
assetManager = getResources().getAssets();
}
public void onClick(View button) {
switch(button.getId()) {
case R.id.buttonA1:
audioTrack1 = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, minBufferSize * 65, AudioTrack.MODE_STATIC);
audioTrack1.write(soundMap.get(3), 0, minBufferSize * 65);
audioTrack1.play();
break;
case R.id.buttonA2:
audioTrack2 = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, minBufferSize * 65, AudioTrack.MODE_STATIC);
audioTrack2.write(soundMap.get(3), 0, minBufferSize * 65);
audioTrack2.play();
break;
case R.id.buttonA3:
audioTrack3 = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, minBufferSize * 65, AudioTrack.MODE_STATIC);
audioTrack3.write(soundMap.get(3), 0, minBufferSize * 65);
audioTrack3.play();
break;
case R.id.buttonA4:
audioTrack4 = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, minBufferSize * 65, AudioTrack.MODE_STATIC);
audioTrack4.write(soundMap.get(3), 0, minBufferSize * 65);
audioTrack4.play();
break;
case R.id.buttonA5:
audioTrack5 = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, minBufferSize * 65, AudioTrack.MODE_STATIC);
audioTrack5.write(soundMap.get(4), 0, minBufferSize * 65);
audioTrack5.play();
break;
case R.id.buttonA6:
audioTrack6 = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, minBufferSize * 65, AudioTrack.MODE_STATIC);
audioTrack6.write(soundMap.get(5), 0, minBufferSize * 65);
audioTrack6.play();
break;
case R.id.buttonA7:
audioTrack7 = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, minBufferSize * 65, AudioTrack.MODE_STATIC);
audioTrack7.write(soundMap.get(6), 0, minBufferSize * 65);
audioTrack7.play();
break;
case R.id.buttonA8:
audioTrack8 = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, minBufferSize * 65, AudioTrack.MODE_STATIC);
audioTrack8.write(soundMap.get(7), 0, minBufferSize * 65);
audioTrack8.play();
break;
}
}
Would be great if someone could help to solve this problem, maybe with stream mode instead of static mode where the sounds are preprocessed or an empty byte buffer is written to audiotrack and on button click the hashmap sound is streamed to audiotrack.
-
I think you should take a look at SoundPool and use that. It seems to be designed specifically to do the things you want to do. It doesn’t work with dynamically generated sounds, but since you are playing a piano I assume you have the sample as a WAV-file on the SDCard anyway.
Using the playback speed you can change the pitch: playback at double speed = 1 octave up, half speed = 1 octave down. You may want to use a more refined method later on 🙂
Originally posted 2013-11-19 13:17:06.