Android AudioTrack does not play all samples

I'm using AudioTrack to play a sequence of sine waves, but when I run it on my HTC M9, it plays only part of the samples, and how long it will play is random. e. g. I have 20 tones to play, but it only plays like 2 to 17.5 tones of them. And yes it even will stop in middle of a tone.

Here is my code, from another answer:

    ArrayList<double[]> samples = new ArrayList<>();
    int numSamples = 0;
    for (final ToneSegment seg : sequence) {
        int num =  seg.getDuration() * sampleRate / 1000;
        double[] sample = new double[num];
        for (int i = 0; i < num; ++i) {
            sample[i] = Math.sin(2 * Math.PI * i * seg.getPitch() / sampleRate);
        }
        samples.add(sample);
        numSamples += num;
    }

    byte generatedSnd[] = new byte[2 * numSamples];
    int idx = 0;
    for (double[] sample : samples) {
        for (final double dVal : sample) {
            final short val = (short) ((dVal * 32767));
            generatedSnd[idx++] = (byte) (val & 0x00ff);
            generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
        }
    }

    audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
            sampleRate, AudioFormat.CHANNEL_OUT_MONO,
            AudioFormat.ENCODING_PCM_16BIT, generatedSnd.length,
            AudioTrack.MODE_STATIC);
    audioTrack.write(generatedSnd, 0, generatedSnd.length);
    audioTrack.play();

Does anyone have any idea? Thanks!

Upvotes: 9

Views: 1613

Answers (2)

It's likely the garbage collection causing this problem. I was writing this code in a method returning immediately, as audioTrack object will lose the reference to it. If garbage collection happens during the playback, audioTrack will be finalized and stops playing tones. Thus this issue happens occasionally, depending on how active the GC is when playing tones.

So I need to keep the AudioTrack object reference until the playback ends. I think there are many ways to do this, but finally I use the simplest way:

class Player {
    private static AudioTrack sAudioTrack;
}

MediaPlayer also has this pitfall, and is how I found the problem since it will write log that it's finalized before released.

Upvotes: 0

jobbert
jobbert

Reputation: 3497

You are using AudioTrack.MODE_STATIC that is meant for short sounds and low-latency requirements while you should use AudioTrack.MODE_STREAM. AudioTrack.MODE_STREAM is meant for longer streams, but I do not know how long your samples are. So you can try and change AudioTrack:

audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
        sampleRate, AudioFormat.CHANNEL_OUT_MONO,
        AudioFormat.ENCODING_PCM_16BIT, generatedSnd.length,
        AudioTrack.MODE_STREAM);

Also The AudioTrack requires the buffer size in bytes, a short needs to be multiplied by two to calculate the correct amount of required bytes. You could hack it in like this :

audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
        sampleRate, AudioFormat.CHANNEL_OUT_MONO,
        AudioFormat.ENCODING_PCM_16BIT, generatedSnd.length*2,
        AudioTrack.MODE_STREAM);

Upvotes: 3

Related Questions