Android Code Snippet Timer not exact enough for sending to stream

Maybe the code is trivial, but I searched a long time in my code until I found the bug, why my stream always drops out. So maybe this will help somebody.

The Timer() is not that exact as you expect. This is not important as long as you do not calculate with this.
but...
B4X:
Sub Process_Globals
    Public Timer1 As Timer
...

Sub Activity_Create(FirstTime As Boolean)
    If FirstTime=True Then
        Timer1.Initialize("Timer1",100)
        Timer1.Enabled=True
    End If
...

Sub Timer1_Tick
    If (SongPosition=0) Then
        SendAudioSamples (2205)
    End If
    SendAudioSamples (2205)
End Sub
This code sends 10 times a second 2205 Bytes to the AudioStreamer() to keep an audiostream (22050Hz) alive. In the start moment I send the packet twice to have a little "buffer" for contingencies. I first thought this would be enough preventon. But after 20sec you can hear drop-outs and crackling, after 40sec it becomes stronger.

The reason is, because I trusted in the Timer() exactness. But a tick between two calls can last from 99msec to 102msec (avarage=100.3msec)

So you have to care about that. Better you correct the variability with DateTime():
B4X:
Sub Process_Globals
    Public Timer1 As Timer
    Private TicksCount As Long
...

Sub Activity_Create(FirstTime As Boolean)
    If FirstTime=True Then
        Timer1.Initialize("Timer1",100)
        Timer1.Enabled=True
    End If
...

Sub Timer1_Tick
    If (SongPosition=0) Then
        SendAudioSamples (2205)
        TicksCount=DateTime.now-100
    End If
    Dim TicksDiff As Long=DateTime.now-TicksCount-100
    Dim SendCount As Int=2205
    Log ("ticks-diff" & TicksDiff )
    If TicksDiff>0 Then
        TicksCount=TicksCount+101
        SendCount=2205+22
    Else
        TicksCount=TicksCount+100
    End If
    SendAudioSamples (SendCount)
End Sub
 

emexes

Expert
Licensed User
How about:
B4X:
Sub Process_Globals
    Public Timer1 As Timer
    Private TicksCount As Long
...

Sub Activity_Create(FirstTime As Boolean)
    If FirstTime=True Then
        Timer1.Initialize("Timer1",100)
        Timer1.Enabled=True
        TicksCount = DateTime.Now - 234    'send approx. 234 ms of (extra?) samples to start with
    End If
...

Sub Timer1_Tick

    'send samples in groups/multiples/blocks of 441
    '22050 per sec = 441 per 20 ms

    Dim BlocksToSend As Int = (DateTime.Now - TicksCount) / 20    'integer division should round down (not that it matters much here)

    SendAudioSamples(BlocksToSend * 441)
    TicksCount = TicksCount + BlocksToSend * 20

End Sub
although if there is any drift between the clocks at each end of the audio link, it could still get ugly, just not so often.

Does the receiving device send a signal indicating its buffer is eg 50% full?

Note that this method doesn't care what the timer period is, within reason, eg 30 ms or 300 ms should both work too. šŸ»
 
Last edited:

Midimaster

Active Member
Licensed User
The receiving device does not send any buffer state messages. It is the AudioStreamer(). And for the "real time feeling", I cannot give more than 100-200msecs into the buffer. The user would have the feeling, that the song reacts with a time delay to his changes.

At the moment this code above runs very good for me. It looks like the buffer keeps a minimum of 98msec of music over 5min of song playing.
 

emexes

Expert
Licensed User
At the moment this code above runs very good for me. It looks like the buffer keeps a minimum of 98msec of music over 5min of song playing.
Yeah, it's an interesting problem, keeping two independent devices precisely synchronized. No two crystals are ever the same, there's always a bit of clock creep.

I get the feeling that your code is sending at very slightly less than the nominal 22050 rate, due to the adjustment only sending out 22 samples for the extra millisecond, rather than 22.05 samples. But from a practical viewpoint, it's close enough that it's good enough = šŸ‘
 
Top