Android Question Does Audio Streamer copy the buffer?

Michael Gasperi

Member
Licensed User
Longtime User
Does Audio Streamer copy the contents of the buffer you pass or does it actually just play the values out of the buffer itself?
 

Michael Gasperi

Member
Licensed User
Longtime User
Let's say I've got a few ms of audio values in a byte array called data.

I keep trying to pass it to streamer.Write(data) till it comes back True.

Immediately after that I start fooling around with new values in the same array data.

The values coming out of the audio channel are effected by this new fooling around.

That looks like the playback is using the values in my data array, not a copy.
 
Upvote 0

Michael Gasperi

Member
Licensed User
Longtime User
How can I trick streamer.Write(data) to do the copy thing with my data? How does it know it was passed "recording data"?
 
Upvote 0

Michael Gasperi

Member
Licensed User
Longtime User
I'm still struggling with how to feed this buffer and not get into memory trouble.

Please consider this code. It creates different frequency and amplitude sine waves on each audio channel.

I'm doing a Dim for the 4 bytes of each sample every single time I call Streamer.Write.

Sub Timer2_Tick 'Called often enough to keep play buffer full
Dim value As Double
Do While True
Dim data(4) As Byte
value = 32767 * Sin(LPhase) * LGain 'left value
data(1) = value / 256 'high byte
data(0) = value Mod 256 'low byte
value = 32767 * Sin(RPhase) * RGain ' right value
data(3) = value / 256 'high byte
data(2) = value Mod 256 'low byte
If Not(Streamer.Write(data)) Then Exit
LPhase = LPhase + DLPhase 'if success in Write, move phase forward
RPhase = RPhase + DRPhase 'if success in Write, move phase forward
Loop
End Sub

Is this a reasonable way to do it? Sometimes the app goes nuts, but I can't pinpoint if this is what is causing the problem.
 
Upvote 0

Michael Gasperi

Member
Licensed User
Longtime User
Sorry about the pasting the code the wrong way.

By "blocking the main thread" are you talking about the Do While True Loop with an Exit?
 
Upvote 0

Michael Gasperi

Member
Licensed User
Longtime User
The problem is I want to change the output values and have the output sound change almost immediately without delay

Let's say I pass Streamer.Write 3.25ms of data at a time. If I set the timer for 4ms obviously the playback is starved and there is a dead space every so often. If I set the timer to 3ms, there is 0.25ms of data left and just I added another 3.25ms. The play buffer must be really long because this keeps building up to seconds of buffered data and hence delay.

You might think the solution would be to write exactly 4ms of data and reload exactly every 4ms but I don't think you can match the times that well.
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
If you want to process the audio data, the answer is to use a separate thread for playing the audio, have a look at the example attest in the audiotrack library, the process will be similar for audiostreamer. You will not get a instant change because of the required buffer size is pretty large for audio processing.

Using a separate thread, you can use a continual loop with it's own buffer and only write to the output when there is something to write.
 
Upvote 0

Michael Gasperi

Member
Licensed User
Longtime User
Seems like you still have the problem of feeding data at the same pace it is playing without getting too far ahead or behind.

It happens that I am also Recording audio at the same time and that process periodically calls streamer_RecordBuffer to unload its data. It isn't clear how often it feels obligated to do this, but it seems to be fairly often. I've left out the stuff I'm doing with the recorded data, but here is the code that keeps feeding the playback in that routine. At least the pace of input and output should be exactly the same.

B4X:
Sub streamer_RecordBuffer (Buffer() As Byte)
    Dim Value As Double
    Dim i As Int
' Do some stuff with the recorded data then reuse the buffer for output
    For i = 0 To (Buffer.Length-1) Step 4
        Value = 32767 * Sin(LPhase) * LGain 'left value
        Buffer(i+1) = Value / 256 'high byte
        Buffer(i+0) = Value Mod 256 'low byte
        Value = 32767 * Sin(RPhase) * RGain ' right value
        Buffer(i+3) = Value / 256 'high byte
        Buffer(i+2) = Value Mod 256 'low byte
        LPhase = LPhase + DLPhase 'move phase forward
        RPhase = RPhase + DRPhase 'move phase forward   
    Next
        Streamer.Write(Buffer) 'should always work
End Sub

What causes streamer_RecordBuffer to be be called? And how often could this be?
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
I haven't really experimented with audiostreamer, but I would think that the RecordBuffer is called when the buffer is full, which you could calculate the time by reference to the samplerate and buffer size.

You can't have any influence over the timing, you just have to move the buffers between the two when the record buffer is full, and then wait for the next buffer. The problem comes when your processing takes longer than the time to fill the next buffer, then you will get jitters.

AudioStreamer uses AudioTrack under the hood, if you are manipulating the data in anyway, I would think it would be more efficient to use the AudioTrack library to access AudioRecord and AudioTrack directly, as you will remove an extra threaded process.

Attached is an example using AudioTrack, AudioRecord and a thread which just passes the recorded data to the output without any processing, you can hear the latency introduced by the process. It requires the AudioTrack and AudioRecord Libraries, and the Threading library.
 

Attachments

  • AudTest.zip
    6.3 KB · Views: 184
Upvote 0
Top