Android AudioTrack does not play all samples
Asked Answered
T

2

9

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!

Timmy answered 19/1, 2016 at 10:5 Comment(0)
T
0

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.

Timmy answered 27/6, 2016 at 7:3 Comment(0)
P
3

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);
Phosphoresce answered 3/5, 2016 at 7:18 Comment(8)
I have tried out MODE_STREAM, but it's the same. My samples only play for 3 to 7 seconds.Fra
They play for a random duration? Have you tried shorter or other audio samples? And have you multiplied the amount of bytes too?Phosphoresce
Yes, I have tried other samples. They are not random duration. What is to multiplied the amount of bytes?Fra
generatedSnd.length*2?Phosphoresce
It does not work. Also, this code works sometimes, but not other times.Fra
I play the same samples several times, sometimes it will play through the end, but sometimes it will stop before through the end. If it's a 5 seconds clip, it can stop and any length like 0.5 sec, 1 sec, 4 sec, etc.Fra
Can you expand your code so that I am able to fully run the part that plays an audio sample? And maybe get a sample audio file so that I can try to fix this for you?Phosphoresce
ToneSegment class just contains two fields, duration and pitch. It's a long stack call so I can't provide standalone runnable code, but for example there will be 26 ToneSegment and each is 128ms, with pitch like 1828, 1875, 1968, 1031, 1078, 1125, 1171...and so on.Fra
T
0

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.

Timmy answered 27/6, 2016 at 7:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.