I have a feature I want to transition over to use Androids AudioTrack
instead of MediaPlayer
, due to a few well known bugs with MediaPlayer
, such as the small gap that appears between looping tracks.
I've been recommended to use AudioTrack
but haven't found to many examples of it in use. I did find a question on SO regarding AudioTrack
and used some of that code to hack together something:
public class TestActivity extends Activity implements Runnable {
Button playButton;
byte[] byteData = null;
int bufSize;
AudioTrack myAT = null;
Thread playThread = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
playButton = (Button) findViewById(R.id.testButton);
InputStream inputStream = getResources().openRawResource(R.raw.whitenoise_wav);
try {
byteData = new byte[ inputStream.available()];
} catch (IOException e) {
e.printStackTrace();
}
try {
inputStream.read(byteData);
inputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
initialize();
playButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
playThread.start();
}
});
}
void initialize() {
bufSize = android.media.AudioTrack.getMinBufferSize(44100,
AudioFormat.CHANNEL_CONFIGURATION_STEREO,
AudioFormat.ENCODING_PCM_16BIT);
myAT = new AudioTrack(AudioManager.STREAM_MUSIC,
44100, AudioFormat.CHANNEL_CONFIGURATION_STEREO,
AudioFormat.ENCODING_PCM_16BIT, bufSize,
AudioTrack.MODE_STREAM);
myAT.setVolume(.2f);
playThread = new Thread(this);
}
public void run() {
if (myAT != null) {
myAT.play();
myAT.setLoopPoints(0, byteData.length, 6);
myAT.write(byteData, 0, byteData.length);
}
}
}
So this does seem to play the entire audio track (~1:00 min) and then stops. Now the end goal here is two have 2 seperate audio tracks playing and looping at the same time. I currently have the audio tracks in the /res/raw/
directory, but I can move them to a simple assets
folder if that would be better. Is my current implementation of AudioTrack
correct? If so, how would I get it to loop?
In summation: how can you play looping audio without a gap using AudioTrack
?
Suggestions for alternative ways to get looping audio, such as third party libraries, are welcomed.
You can't loop using an AudioTrack configured with AudioTrack.MODE_STREAM
. If you use MODE_STREAM
AudioTrack needs to be filled with new samples continuously.
But you can configure it with AudioTrack.MODE_STATIC
and pass the entire buffer to be played (I mean: if you need to mix two samples, you have to pass the mixed samples).
setLoopPoints: Sets the loop points and the loop count. The loop can be infinite. Similarly to setPlaybackHeadPosition, the track must be stopped or paused for the loop points to be changed, and must use the MODE_STATIC mode.
Please note that AudioTrack plays raw PCM samples, there's no support for WAV, MP3 or other containers.
Look at this example, it seems a similar issue, solved feeding continuously the AudioTrack
.
class ToneGenerator {
int sampleRate = 8000;
double sample[] = null;
byte generatedSnd[] = null;
int m_ifreq = 400;
Thread m_PlayThread = null;
boolean m_bStop = false;
AudioTrack m_audioTrack = null;
int m_play_length = 1000;//in seconds
static public void PlayTone(int freq, int play_length) {
ToneGenerator player = new ToneGenerator();
player.m_ifreq = freq;
player.m_play_length = play_length;
player.play();
}
synchronized void stop() {
m_bStop = true;
if (m_PlayThread != null) {
try {
m_PlayThread.interrupt();
m_PlayThread.join();
m_PlayThread = null;
} catch (Exception e) {
}
}
if (m_audioTrack != null) {
m_audioTrack.stop();
m_audioTrack.release();
m_audioTrack = null;
}
}
synchronized void play() {
m_bStop = false;
m_PlayThread = new Thread() {
public void run() {
try {
int iToneStep = 0;
m_audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
sampleRate, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT, 2 * sampleRate,
AudioTrack.MODE_STREAM);
while (!m_bStop && m_play_length-- > 0) {
genTone(iToneStep++);
m_audioTrack.write(generatedSnd, 0, generatedSnd.length);
if (iToneStep == 1) {
m_audioTrack.play();
}
}
} catch (Exception e) {
Log.e("Tone", e.toString());
} catch (OutOfMemoryError e) {
Log.e("Tone", e.toString());
}
}
};
m_PlayThread.start();
}
//Generate tone data for 1 seconds
synchronized void genTone(int iStep) {
sample = new double[sampleRate];
for (int i = 0; i < sampleRate; ++i) {
sample[i] = Math.sin(2 * Math.PI * (i + iStep * sampleRate) / (sampleRate / m_ifreq));
}
// convert to 16 bit pcm sound array
// assumes the sample buffer is normalised.
generatedSnd = new byte[2 * sampleRate];
int idx = 0;
for (final double dVal : sample) {
// scale to maximum amplitude
final short val = (short) ((dVal * 32767));
// in 16 bit wav PCM, first byte is the low order byte
generatedSnd[idx++] = (byte) (val & 0x00ff);
generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With