Skip to content

audioio does not maintain synchonisation of A0/A1 looping some audioio.RawSample data #1992

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
kevinjwalters opened this issue Jul 10, 2019 · 18 comments

Comments

@kevinjwalters
Copy link

kevinjwalters commented Jul 10, 2019

I appear to have made some data which despite being even in length (4930 samples = 2465*2) when passed to audio.RawSample with channel_count=2 and then sent to output with play() with loop=True it will play but the A0/A1 will not stay synchronised and drift around. At 100kHz playback it takes 60 seconds to return to being very briefly in sync. I might expect this if I passed an odd number of samples, e.g. 9 could end up as 5 looping on DAC0 and 4 looping on DAC1.

Code is rather large because it contains the data as a list, see https://github.com/kevinjwalters/circuitpython-examples/blob/master/pygamer/audioio-phasebug.py. In that example there's a boolean which controls whether to replace the data values with an alternate set of rising values without changing the total number of values (samples). Doing that makes the bug go away, the bug appears to be very dependent on the data values with no obvious explanation.

This does seem rather unlikely so there's a minor chance I have made an error here...

BTW, what's the intended behaviour for audioio.RawSample if it's given a number of samples which are not exactly divisble by the channel_count?

@tannewt tannewt added the audio label Jul 11, 2019
@tannewt
Copy link
Member

tannewt commented Jul 11, 2019

I'd expect it to throw an exception when the sample count isn't divisible by channel_count. I'm not positive it does though.

I'd encourage you to dig into the C side of the audio APIs. I bet you could fix a number of these issues.

@tannewt tannewt added this to the Long term milestone Jul 11, 2019
@tannewt tannewt added the bug label Jul 11, 2019
@kevinjwalters
Copy link
Author

I'm in the middle of some application programming at the moment but will have a look when I get to end of that.

I've also discovered another problem with dacs.play(something, loop=True) ceasing output and possibly leaving output on the last sample in something. I thought it was time based but it's not, sometimes it's 20 seconds, sometimes over a 100, sometimes seems to run ok "forever". It's very unclear what the trigger is for cessation of looping! The code in question was just doing a while True: pass after the play().

@kevinjwalters
Copy link
Author

kevinjwalters commented Jul 13, 2019

I'm running with a smaller amount of data now (882 * 2 = 1764) and if I transform those values keeping length identical sometimes A0/A1 stay in sync and sometimes they don't.

"When they are in sync there might be a few samples missing too". I was wrong here, this was due to a bit of visual confusion from an in-place update on an existing "H" array for audioio.RawSample() causing the update to go out gradually as I slowly updated the array. I've intentionally started using "h" now to take advantage of its creation of a second buffer for DAC output.

@kevinjwalters
Copy link
Author

Another observation, I was looking for a workaround and I've noted for similar code/data I've found the bug appears if I use the range 0-32768 on DAC but with same data rescaled to 0-25000 the sync bug goes away.

@kevinjwalters
Copy link
Author

kevinjwalters commented Jul 24, 2019

It's possible that a bizarre workaround for this is to do a single

rawdata.append(0)

on the end of the data. This doesn't make sense as for 2 channels one would expect the array to always need to be even in length!

CORRECTION, it takes three magic 0s! This makes everything ok.

bizarre_workaround = True
if bizarre_workaround:
    rawdata.append(0)
    rawdata.append(0)
    rawdata.append(0)

kevinjwalters pushed a commit to kevinjwalters/circuitpython-examples that referenced this issue Jul 24, 2019
@kevinjwalters
Copy link
Author

kevinjwalters commented Jul 25, 2019

Reproducible on a Feather M4 too. The bizarre_workaround also fixes it on that Feather.

@kevinjwalters
Copy link
Author

Also on a Metro M4.

@kevinjwalters
Copy link
Author

Worth monitoring #1908 as this also relates to (more fatal) problems that are being attributed to DMA woes.

@jepler
Copy link

jepler commented Aug 31, 2019

Reproduced on Metro M4 express. On my scope, the one trace slowly moves with respect to the other one at a constant rate. Just to add to the findings, if I decrease the sample rate enough (24 * 1000) the problem goes away, at least for this sample data. As the original reporter states, somehow this is dependent on specific sample values, too, which really is a problem for any explanations I might have put forward. Debugging hasn't yielded any insights yet.

@kevinjwalters
Copy link
Author

Nice to know it's not just me!

@jepler
Copy link

jepler commented Aug 31, 2019

Not only is it data-dependent, but for some data combinations the order of relative movement is reversed!

@kevinjwalters
Copy link
Author

I'm not saying this is related but there's more unexplained, weird stuff going on with these DACs: Adafruit Forums: SAMD51 M4 DAC has problems getting it up

@jepler
Copy link

jepler commented Aug 31, 2019

CircuitPython does not intentionally enable the oversampling or dithering modes.

@jepler
Copy link

jepler commented Aug 31, 2019

fwiw your stair step on 0-to-65535 I can also reproduce, and the "data dependent" nature of the problem seems to be that when the "stair step" is going on at the moment the buffer flips over, the problem doesn't manifest. weirder and weirder.

@jepler
Copy link

jepler commented Aug 31, 2019

MSO1104Z_2019-08-31_12 03 07

@jepler
Copy link

jepler commented Aug 31, 2019

Smaller reproducer:

import board 
import audioio
import array
import time

X = 8191
Y = 65535
data = array.array('H', [0, 0, X, X, X, Y])
sample = audioio.RawSample(data, sample_rate=200 * 1000, channel_count=2)
a = audioio.AudioOut(board.A0, right_channel=board.A1)

a.play(sample, loop=True)

while 1:
    time.sleep(1)

Change the single Y back to X and the waveforms stay in sync. Change a left-channel X to Y and the waveforms stay in sync.

@jepler
Copy link

jepler commented Aug 31, 2019

With 3 samples per channel and a sample rate of 200kHz, the observed frequency should be (200/3) = 66.7kHz. Instead, I measure ~55.5kHz with [0,0,X,X,X,X], and 12.8kHz with [0,0,Y,Y,Y,Y].

jepler added a commit to jepler/circuitpython that referenced this issue Sep 2, 2019
This improves the behavior around adafruit#1992.  The samples stay in
sync now, though full scale changes still behave erratically.

Testing performed: On a Metro M4, with both analog channels going to
a scope, I looked for synchronization and waveform shape.

Original reproducer: Stays synchronized.

adafruit#1992 (comment) reproducer:
Stays synchronized and plays at the nominal 100kHz sample rate.
However, waveform peaks are "regularly irregular", with every other
high peak being truncated, usually.

A difference I'm aware of between this and Arduino's M4 core is that
Arduino uses CCTRL_CC100K; however, doing this makes CP worse, so
I went with the more "correct" CC12M setting.

Closes: adafruit#1992
jepler added a commit to jepler/circuitpython that referenced this issue Sep 8, 2019
This improves the behavior around adafruit#1992.  The samples stay in
sync now, though full scale changes still behave erratically.

Testing performed: On a Metro M4, with both analog channels going to
a scope, I looked for synchronization and waveform shape.

Original reproducer: Stays synchronized.

adafruit#1992 (comment) reproducer:
Stays synchronized and plays at the nominal 100kHz sample rate.
However, waveform peaks are "regularly irregular", with every other
high peak being truncated, usually.

A difference I'm aware of between this and Arduino's M4 core is that
Arduino uses CCTRL_CC100K; however, doing this makes CP worse, so
I went with the more "correct" CC12M setting.
@jepler
Copy link

jepler commented May 15, 2020

Since #2552 we have just one DMA channel, so becoming out of sync like this should be impossible. Please feel free to reopen this if you still reproduce the problem on 5.3.0.

@jepler jepler closed this as completed May 15, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants