DMIC pop / click sounds between each memory block

I'm trying to continuously stream data from the dmic interface, in this case over serial though only for testing.
Things seem to be largely working except for pop/click artifacts between every block.
In this case the memory slab is setup for 4 blocks with enough memory for 100ms of audio, default settings taken from the dmic sample.
The pop is usually about 0.5-1ms long and per recording looks exactly the same each time and occurs in intervals equal to the block size. Large block size means less frequent intervals.

This smells like either samples are dropped between each block or I'm getting garbage data inserted somehow, though it does feel more like audio is missing/overwritten when a pop occurs.
Giving a really large block size like 1000ms of audio make the pop occasionally not occur.

My understanding is that the dmic driver continuously reads data as soon as the start flag is given and will choose a free memory block from the slab to write to. As long as I process a returned block quickly enough and free it then the dmic should not run out of memory or drop samples (if that is even a possible behaviour)?
I can't see an off by one error or similar in the way I grab the data either. It's a bit puzzling.
The interface is not throwing me any errors with debug log enabled.

Attached a sample recording and the project code. The python script can be used to translate the ASCII HEX string to a binary.

Using NCS 2.5.0

3527.dmic.zip


5684.silence_with_clicks.zip

  • Hi Torbjørn,

    the UART is set close to 1M Baud, that should be more than enough in theoery.

    The pop occurs even at lower baud rates without a noticable change and given the dmic reads via DMA I don't think that could cause a hickup? If its due to being too slow to free the memory blocks then I should get 4 click free blocks before seeing clicks but this is not the case.

    I only have the UART to check the validity of the samples. Printk via logging is disabled so the logging infrastructure should not interfere or be an overhead.

  • Hi Timon

    timonsku said:
    the UART is set close to 1M Baud, that should be more than enough in theoery.

    Hm, I would say it is just about enough. 

    16000 samples/sec * 5 ascii characters pr sample * 10 bits pr character (including stop and start bit) = 800kBaud

    So it is enough, yes, but not by a large factor. The question is how much overhead there is in the driver, and how much CPU time is required to process all those samples and convert them to a string, before passing them to the UART driver. 

    I would suggest running a quick test just changing num_samples in the loop to a small number like 10 and see if it makes a difference. 

    A colleague of mine recently did some work integrating a PDM mic on the nRF52832, and the code looks very similar:
    https://github.com/Noy0908/smart_remote/blob/desktop_remote/src/smart_remote/drv_mic.c#L191-L218

    By the way, how do you transfer the audio data to the PC for analysis? 
    If you still can't make it work I will try to replicate your test here, but I need to figure out the easiest way of transferring the audio data somewhere else for playback. Maybe storing to SD card on the audio DK is the best bet...

    Best regards
    Torbjørn

  • I had tested fewer samples before, even just doing a single block results in having at least one click. The only times I saw a click sometimes not appear was with increasing block size significantly but it was inconsistent with some blocks having it and some not.

    I dump the samples by connecting via USB to a PC and saving the serial output to a file/logging it and use the python script in the project zip file to turn it from ASCII into a binary file. That binary file I then open in Adobe Audition by doing a raw PCM import. Audacity would probably also work.
    For the serial logging I use "tio" on an Ubuntu machine but any serial terminal should do it including the VSCode one that Nordic ships.

    Thanks for the link, I will see if there is anything significantly different that I can try.

  • Hi Timon

    I will set aside some time tomorrow to test this on my end. I am relatively sure that the UART link is somehow part of the problem, since the DMIC code itself is very straight forward. 

    Do you have a scope or logic analyzer available in order to check the UART activity? 
    The UART would have to run close to 100% of the time for the bandwidth to be sufficient, any gaps between transactions or slowdowns would cause issues. 

    Best regards
    Torbjørn

  • Yea I can hook up a Saleae but need to check if there are usable test points. the uart is read out via the onboard debugger.
    Ultimately I want to send the audio samples via BLE GATT (BLE audio is not well enough supported by other devices and not necessary for this application, the samples will be processed by software), while it would be really helpful for development to have some other way to send out sample data to debug I would mostly care about ensuring that you can actually stream DMIC data "easily", the current issue worries me a bit that this is going to very finicky to avoid such artifacts.

    I'm sceptical that the UART is the problem here because of the following observation and assumptions.
    The dmic peripheral is provided 4 memory blocks to use for sampling. Sampling is done via DMA so user code should not be blocking the dmic peripheral from continuing to fill up the next free block with sample data. Only when all 4 blocks are exhausted and not freed by the user should the user code become a bottleneck but the clicks appear after the very first block.
    If I would miss sample data after the 4th block it would be fair to assume that we can't keep up with freeing memory blocks fast enough to provide free blocks to the dmic peripheral.

    Which leads me to assume that there is either something that needs to be done to correctly join blocks of sample data and my naive way of just appending it all is not correct or that there is something wrong in the way I setup the dmic peripheral that it maybe resets something with each block read.
    I saw a reference in the firmware for the Thingy:53 of avoiding a click sound when starting with the sampling but not specifics as to what the reason for that artifact is. I do observe that the samples need to settle for a few ms, there is a consistent observable "curve" whenever I make a recording.

    E: See here for the mentioned comment: https://github.com/edgeimpulse/firmware-nordic-thingy53/blob/main/src/sensors/ei_microphone.cpp#L314-L331

    Also here a screenshot of the attached audio data visualized to maybe give you better context. In this case little bit of the first sample got missed in my log, usually the click occurs after the first sample, here it looks like it starts shortly within the first sample. The spikes after are from the 2-4th sample. You can see the regularity of a click after exactly 100ms, which is how much sampling data fits into one block.

Related