[hpsdr] Audio resampler

Alan Hopper alan at samsararesearch.com
Sat Jun 10 06:35:05 PDT 2017


Hi List

In listening to the teamspeak recording I thought I’d share my experience
of writing an audio resampler for my hermes lite software ( Spark sdr
http://www.ihopper.org/radio/ ) in the hope that it might be useful.  I’m
currently rewriting Spark in a form that I can open source and happen the
have just got to the resampler part.


The hermes lite has no audio hardware so all audio uses the pc sound system
so there is no escape from the clock difference problem.


The resampler is used for two functions, firstly to prevent audio clicks
from buffer over/underruns caused by clock speed differences and secondly
to minimise latency. The software monitors how close to buffer underrun the
system gets and if there is excess slack the resampler rate is adjusted to
reduce it.  This eliminates any need for audio buffer size settings.


The resampler I use is a very simple linear interpolation, I had always
meant to swap it out for something better but have never had any issues or
reports about the audio quality so have not had the motivation. It has the
advantage of very low cpu use and worst case latency of 1 sample. I suspect
it would cause problems for digimodes over vac but as Spark was designed to
avoid using vac I don’t care!


Measuring the relative clock speed caused the most problems.


One pit I fell into was requesting a buffer size from portaudio audio
different to its preferred size, if the buffer requested is smaller than
the actual audio buffer size the portaudio callback is called in bursts,
this can cause a subtle error in reading the audio clock rate as the thread
producing the audio will statistically capture a clock reading more often
between bursts which will cause the filtered frequency to read high.
Letting Portaudio choose the buffer size fixed this.


Before I solved this issue I had tried a number of ways of filtering the
very jittery clock data you get packet by packet including iir filters,
delay locked loop, Kalman, moving linear regression and moving average, I
suspect they all would have worked but as I happened to be using the moving
average at the point I fixed the above problem and it all started working,
I stuck with it.  I’m currently moving this code from c# to c++ and shall
go back and re try the other options.


Another trick was to not do any timing over the first few samples, In Spark
sdr the rest of the dsp chain keeps running if you do something like change
the audio device, this can cause a backlog of data that appears at a high
rate, for some audio device types portaudio sometimes appears to request
the first few buffers at a high rate.


For the control loop I use the measured clock ratio as a feed forward term
and then have proportional and integral terms to correct for drift of the
mean buffer offset.  A much slower loop then gently changes the target
buffer offset to minimise latency.  The final rate sent to the resampler is
passed through a simple lowpass filter.  There is further code to detect
abnormal conditions and reset everything if need be.


To measure the buffer offset from the writing  thread I estimate the
current read position from the time elapsed since the last portaudio read
event and the last known read position, this reduces the jitter of this
measurement.


Spark can connect to multiple radios at the same time so there can be
multiple clock sources, currently this is handled with a separate portaudio
connection for each receiver( sharing the audio device if need be), however
the portaudio documentation indicates this may not work for all audio
devices.  A future task is to add a mixer in so multiple streams can be
handled by a single connection.


Currently I measure the radio clock speed at the point of filling the audio
buffer and it works, it might be better to measure at the point udp packets
are received to reduce jitter.  From a software standpoint this is a little
more messy.  I do measure the radio clock at the point of receiving udp
packets for a different purpose, by comparing it against the ntp corrected
computer clock and filtering over a very much longer time I am able to
automatically frequency correct two hermes lites so wspr spots are
generally within 1Hz of each other, I may combine these two bits of code.


At John Laur’s suggestion my hermes emulator
https://github.com/ahopper/Patroclus has the option to adjust the virtual
clock speed which is useful for testing these resamplers ( this feature
only works for protocol 1 currently).


73 Alan M0NNB
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.openhpsdr.org/pipermail/hpsdr-openhpsdr.org/attachments/20170610/3c2c9969/attachment.htm>


More information about the Hpsdr mailing list