Hi all,
I'm working on a networked audio project, and naturally I'm faced with issues of clock drift and jitter. To date I've been using a delay-locked loop approach (something akin to that described by Fons Adriensen here), but for improved inter-client synchronicity I'm keen to explore other options.
I created a rudimentary JackTrip client for Teensy few months ago; now I'm working on a bespoke, JUCE-based, multicast audio server and Teensy client.
While working on the JackTrip client, I found QNEthernet to be unreliable (I may have been using it incorrectly) so I stuck with NativeEthernet. However, I recently encountered AsyncUDP_Teensy41, which is based on QNEthernert and appears to work very well, so I intend to incorporate it into my project. This may help mitigate jitter, but clock drift will still be an issue.
My enquiry is twofold:
- As per this thread I've experimented with adjusting PLL4 to set Teensy's sample rate to compensate for clock drift. To do so, I just copied the code from output_i2s.cpp:406-416, adjusting `AUDIO_SAMPLE_RATE_EXACT` with a figure derived from the ratio of reads and writes to a circular buffer storing audio data from the network:
Hardcoding an unrealistically low or high value for `fs` instead, I find that my delay-locked loop drags or rushes as expected, and I don't hear any discontinuity or distortion when I call `set_audioClock`. I don't know well enough what I'm doing with that function though (or whether I should set `CCM_ANALOG_PLL_AUDIO &= CCM_ANALOG_PLL_AUDIO_POWERDOWN` first, or regarding the registers in imxrt_hw.cpp -- I can't link what's going on with the documentation I believe I should consult at https://www.nxp.com/docs/en/data-sheet/SGTL5000.pdf), so I would be very grateful if someone could help me identify how I can set appropriate values for c0, c1, and c2 to achieve an arbitrary sample rate, as so far I've only been able to use integer values for fs. Word is the audio clock can be adjusted in very fine incrementst, so I'd like to give it a try. I gather c0 should fall between 27 and 54, for example, but I don't know why.
- Ultimately, I think I should probably be using an external clock. Rather than considering word clock, or NTP/PTP, I'm wondering whether the followiing is possible:
Does anyone know whether this is possible? If so, how would I go about sharing the clock of the audio interface Teensy with the network client Teensies? Something similar to using a RTC or GPS clock (as per here)?
Please forgive the rambling enquiry. Any assistance/advice would be very much appreciated.
I'm working on a networked audio project, and naturally I'm faced with issues of clock drift and jitter. To date I've been using a delay-locked loop approach (something akin to that described by Fons Adriensen here), but for improved inter-client synchronicity I'm keen to explore other options.
I created a rudimentary JackTrip client for Teensy few months ago; now I'm working on a bespoke, JUCE-based, multicast audio server and Teensy client.
While working on the JackTrip client, I found QNEthernet to be unreliable (I may have been using it incorrectly) so I stuck with NativeEthernet. However, I recently encountered AsyncUDP_Teensy41, which is based on QNEthernert and appears to work very well, so I intend to incorporate it into my project. This may help mitigate jitter, but clock drift will still be an issue.
My enquiry is twofold:
- As per this thread I've experimented with adjusting PLL4 to set Teensy's sample rate to compensate for clock drift. To do so, I just copied the code from output_i2s.cpp:406-416, adjusting `AUDIO_SAMPLE_RATE_EXACT` with a figure derived from the ratio of reads and writes to a circular buffer storing audio data from the network:
Code:
//PLL:
int fs = AUDIO_SAMPLE_RATE_EXACT * (numWrites/numReads);
// PLL between 27*24 = 648MHz und 54*24=1296MHz
int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);
double C = ((double)fs * 256 * n1 * n2) / 24000000;
int c0 = C;
int c2 = 10000;
int c1 = C * c2 - (c0 * c2);
set_audioClock(c0, c1, c2, true);
Hardcoding an unrealistically low or high value for `fs` instead, I find that my delay-locked loop drags or rushes as expected, and I don't hear any discontinuity or distortion when I call `set_audioClock`. I don't know well enough what I'm doing with that function though (or whether I should set `CCM_ANALOG_PLL_AUDIO &= CCM_ANALOG_PLL_AUDIO_POWERDOWN` first, or regarding the registers in imxrt_hw.cpp -- I can't link what's going on with the documentation I believe I should consult at https://www.nxp.com/docs/en/data-sheet/SGTL5000.pdf), so I would be very grateful if someone could help me identify how I can set appropriate values for c0, c1, and c2 to achieve an arbitrary sample rate, as so far I've only been able to use integer values for fs. Word is the audio clock can be adjusted in very fine incrementst, so I'd like to give it a try. I gather c0 should fall between 27 and 54, for example, but I don't know why.
- Ultimately, I think I should probably be using an external clock. Rather than considering word clock, or NTP/PTP, I'm wondering whether the followiing is possible:
- Have one Teensy running as a USB audio device (i.e. use AudioOutputUSB and define USB_AUDIO);
- Use that Teensy as the audio interface for the computer acting as the networked audio server, thus timing on the server is derived from that Teensy;
- Share the audio clock from the audio interface Teensy with a collection of network client Teensies.
Does anyone know whether this is possible? If so, how would I go about sharing the clock of the audio interface Teensy with the network client Teensies? Something similar to using a RTC or GPS clock (as per here)?
Please forgive the rambling enquiry. Any assistance/advice would be very much appreciated.