Hello,
(4d) Removed wodPlayer_DSPWait()
I just calculate the wait value once in wodOpen() and store it in wwo->dwSleepTime.
Won't this value change as arts becomes filled with data or plays data out?
No. The only function that calls wodPlayer_DSPWait() is the FeedDSP() -- and it only calls DSPWait() if availInQ == 0 (ie. the arts buffer is full and can't handle more data).
Looking at the old calculation in DSPWait(), you will see that arts_stream_get() will always return 0 (since the buffer will always be full when DSPWait() is called). The rest of the terms will also remain the same every call since they are only changed it wodOpen(). Hence, it makes since to do the calculation in wodOpen() once and be done with it.
int waitvalue = (wwo->dwBufferSize - arts_stream_get(wwo->play_stream, ARTS_P_BUFFER_SPACE)) / ((wwo->format.wf.nSamplesPerSec * wwo->format.wBitsPerSample * wwo->format.wf.nChannels) /1000);
It you look at the waitvalue calculation further, you will start to wonder what it is trying to calculate anyway?
If you do the calculation with units:
f (bytes) = (wwo->dwBufferSize - arts_stream_get(wwo->play_stream,ARTS_P_BUFFER_SPACE)) s (samples/sec) = wwo->format.wf.nSamplesPerSecond n (bits/sample) = wwo->format.wf.wBitsPerSample c = wwo->format.wf.nChannels.
waitvalue = f bytes / (( s (samples/sec) * n (bits/sample) * c ) * (1 sec / 1000 ms))
you get:
waitvalue = ((1000 * f) / (s * n * c)) ((bytes * ms) / bits)
notice that the units are (bytes * ms) / bits, not ms.
It turns out that it is calculating the amount of time it will take to play 1/8 of the data in the buffer -- although I don't think this is what the intention was.
I changed the sleep time to be the number of milliseconds to play one packet, times the number of packets you want to wait for before writing again:
wwo->dwSleepTime = ((1 << (wwo->packetSettings & 0xFFFF)) * 1000 * BUFFER_REFILL_THRESHOLD) / wwo->format.wf.nAvgBytesPerSec;
The way arts works, you are always going to see the amount of free space in the arts buffers as an integer multiple of the packet size. So I think it makes the most sense to for the sleep time to be some multiple of the amount of time it takes to play a single packet.
As with the old calculation, the new calculation is only dependant on things that will not be changing while the stream is playing. So I think it is safe to calculation the sleep time in wodOpen() and store it in a structure rather than call DSPWait() all the time.
ARTS_P_PACKET_SETTINGS Can you go over these changes a bit more? Maybe Tom can add some information to the documentation based on what you have to say about the change.
Internally arts uses packets to move the data around. When you call arts_writes(), you write to a single buffer, but internally, this is divided up into packets. Lets say that your buffer size is 16k. If arts divides this up into two 8k buffers, then you will only be able to write data in 8k chunks at a time. This can be problematic because you might spend two much time feeding the dsp instead of doing other important tasks. So instead you might prefer to have 16 packets that hold 1k each. Your overall buffer size is the same, but now you can feed the dsp is smaller chunks allowing your application to be more responsive. This is important if you are trying to achieve low-latency real-time operation (something arts is not that great at in the first place) -- which is exactly what is needed for something like the xten sipphone client.
With the changes to the code, you can now not only change the buffer size, but also have finer grain control over how the buffer gets divided up. I should note that this control is limited to compile time, not runtime. I did a lot of experimentation, and I believe the current settings achieve the best balance/performance.
Jeremy Shaw.