Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I am writing some code that intends to take a Wave file, and write it out to and AudioTrack in mode stream. This is a minimum viable test to get AudioTrack stream mode working.

But once I write some buffer of audio to the AudioTrack, and subsequently call play(), the method getPlaybackHeadPosition() continually returns 0.

EDIT: If I ignore my available frames check, and just continually write buffers to the AudioTrack, the write method returns 0 (after the the first buffer write), indicating that it simply did not write any more audio. So it seems that the AudioTrack just doesn't want to start playing.

My code is properly priming the audiotrack. The play method is not throwing any exceptions, so I am not sure what is going wrong.

When stepping through the code, everything on my end is exactly how I anticipate it, so I am thinking somehow I have the AudioTrack configured wrong.

I am running on an emulator, but I don't think that should be an issue.

The WavFile class I am using is a vetted class that I have up and running reliably in lots of Java projects, it is tested to work well.

Observe the following log write, which is a snippet from the larger chunk of code. This log write is never hitting...

                if (headPosition > 0)
                    Log.e("headPosition is greater than zero!!");
public static void writeToAudioTrackStream(final WavFile wave) 
    Log.e("writeToAudioTrackStream");
    Thread thread = new Thread()
        public void run()
            try {
                final float[] data = wave.getData();
                int format = -1;
                if (wave.getChannel() == 1)
                    format = AudioFormat.CHANNEL_OUT_MONO;
                else if (wave.getChannel() == 2)
                    format = AudioFormat.CHANNEL_OUT_STEREO;
                    throw new RuntimeException("writeToAudioTrackStatic() - unsupported number of channels value = "+wave.getChannel());
                final int bufferSizeInFrames = 2048;
                final int bytesPerSmp = wave.getBytesPerSmp();
                final int bufferSizeInBytes = bufferSizeInFrames * bytesPerSmp * wave.getChannel();
                AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, wave.getSmpRate(),
                        format,
                        AudioFormat.ENCODING_PCM_FLOAT,
                        bufferSizeInBytes,
                        AudioTrack.MODE_STREAM);
                int index = 0;
                float[] buffer = new float[bufferSizeInFrames * wave.getChannel()];
                boolean started = false;
                int framesWritten = 0;
                while (index < data.length) {
                    // calculate the available space in the buffer
                    int headPosition = audioTrack.getPlaybackHeadPosition();
                    if (headPosition > 0)
                        Log.e("headPosition is greater than zero!!");
                    int framesInBuffer = framesWritten - headPosition;
                    int availableFrames = bufferSizeInFrames - framesInBuffer;
                    // once the buffer has no space, the prime is done, so start playing
                    if (availableFrames == 0) {
                        if (!started) {
                            audioTrack.play();
                            started = true;
                        continue;
                    int endOffset = availableFrames * wave.getChannel();
                    for (int i = 0; i < endOffset; i++)
                        buffer[i] = data[index + i];
                    int samplesWritten = audioTrack.write(buffer , 0 , endOffset , AudioTrack.WRITE_BLOCKING);
                    // could return error values
                    if (samplesWritten < 0)
                        throw new RuntimeException("AudioTrack write error.");
                    framesWritten += samplesWritten / wave.getChannel();
                    index = endOffset;
            catch (Exception e) {
                Log.e(e.toString());
    thread.start();
                What happens if you ignore the "available space" calculation, and continue to write to the audioTrack unconditionally?
– greeble31
                Feb 21, 2019 at 15:35
                Updated question in response. Log story short, the audiotrack does not write any subsequent buffers after the first one.
– Scorb
                Feb 22, 2019 at 2:31
                Just tried it myself and it worked as soon as I forced additional writes to happen regardless of the playback head position. Also, you should change this line index = endOffset; to +=.
– greeble31
                Feb 22, 2019 at 3:10
                It is difficult to illustrate in a comment, but: edit if statement to be if (availableFrames == 0 && !started), then unconditionally set availableFrames = 2048 prior to the endOffset calculation. That should be sufficient to get playback.
– greeble31
                Feb 22, 2019 at 13:42
  

For portability, an application should prime the data path to the maximum allowed by writing data until the write() method returns a short transfer count. This allows play() to start immediately, and reduces the chance of underrun.

With a strict reading, this might be seen to contradict the earlier statement:

...you can optionally prime the data path prior to calling play(), by writing up to bufferSizeInBytes...

(emphasis mine), but the intent is clear enough: You're supposed to get a short write first.

This is just to get play started. Once that takes place, you can, in fact, use getPlaybackHeadPosition() to determine when more space is available. I've used that technique successfully in my own code, on many different devices/API levels.

As an aside: You should be prepared for getPlaybackHeadPosition() to change only in large increments (if I remember correctly, it's getMinBufferSize()/2). This is the max resolution available from the system; onMarkerReached() cannot be used to do any better.

I have changed the write mode to WRITE_NON_BLOCKING. I write a full buffer, and it plays it. Then getPlayBackHeadPosition returns 238. It continues to return 238 from that point forward, and playback is again frozen. – Scorb Feb 24, 2019 at 1:39 @ScottF I'm not sure what code you are using. If I take the code that has been modified as I described in a comment, and change it to use WRITE_NON_BLOCKING, the playback head advances reliably. Can you demonstrate that, in this condition, write() is being repeatedly called with a non-zero length value, it is returning a non-zero result, and playback is still hung? – greeble31 Feb 24, 2019 at 13:25 What exactly does getPlaybackHeadPosition return in mode stream? The position in relation to the last write, or the position within the internal buffer? – Scorb Mar 4, 2019 at 3:19 I wouldn't describe it either of those ways. It returns the number of frames played since play began. This is a number that starts at 0, increases monotonically until it wraps (0x7FFFFFFF), and is never greater than the total accumulated number of frames added via write(). – greeble31 Mar 4, 2019 at 14:15 That is not what I am observing in mode stream. I have had playback head position be 100 frames, then next call be 80. – Scorb Mar 4, 2019 at 14:46

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.