• 11
name

A PHP Error was encountered

Severity: Notice

Message: Undefined index: userid

Filename: views/question.php

Line Number: 191

Backtrace:

File: /home/prodcxja/public_html/questions/application/views/question.php
Line: 191
Function: _error_handler

File: /home/prodcxja/public_html/questions/application/controllers/Questions.php
Line: 433
Function: view

File: /home/prodcxja/public_html/questions/index.php
Line: 315
Function: require_once

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;
                else
                    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?
      • 2
    • Updated question in response. Log story short, the audiotrack does not write any subsequent buffers after the first one.
    • 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 +=.
      • 1
    • 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.

Per the documentation,

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.

  • 1
Reply Report
    • 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.
      • 1
    • @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?
    • What exactly does getPlaybackHeadPosition return in mode stream? The position in relation to the last write, or the position within the internal buffer?
      • 1
    • 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().
      • 2
    • That is not what I am observing in mode stream. I have had playback head position be 100 frames, then next call be 80.