0

I am using arecord to get audio files to preform DSP on. The problem is that everytime I start to capture input from my i2s microphone, there is a 1 second pop at the beginning of the file. I am trying to solve this by using pipes. What I would like to do is change the output of a pipe without stopping it to avoid this pop and avoid having a gap in audio as well.

For example, I would like to do something like this to switch between soundfile1 and soundfile2 so I can analyze one file while the other is recording. I don't care about a pop happening when I start recording, but I can't have it happen every time I switch back and forth.

arecord -D dmic_sv -c2 -r 48000 -f S32_LE -t wav -V mono -v > soundfile1.wav

arecord -D dmic_sv -c2 -r 48000 -f S32_LE -t wav -V mono -v > soundfile2.wav

Unfortunately this just tells me "Device or resource busy"

1 Answers1

2

Run this in a shell and let it run:

arecord -D dmic_sv -c2 -r 48000 -f S32_LE -t wav -V mono -v \
| while :; do dd bs=384000 count=1 iflag=fullblock 2>/dev/null >>output; done

Each dd will send 1 second of data (48000 samples of 4 bytes (32 bits) times 2 channels, so 384000 bytes) to the output. The important thing is output will be opened for appending separately for each dd. This means when you rm output or mv output soundfile1.wav, then the next dd will write to output anew, i.e. a new output will be created.

If mv output soundfile1.wav happens within a single filesystem, no data will be lost. I mean if you concatenate soundfile1.wav and the new output then you will recreate the original stream. This is because renaming (or even deleting) a file does not bother a process that has already opened the file.

Moving to another filesystem is in fact equivalent to cp+unlink. In this case it may happen some dd adds to output between cp and unlink and this chunk is lost. So don't move output directly to another filesystem. If you need to do this, rename within a single filesystem, wait for new output to appear and only then move the renamed file to the other filesystem.

Another approach is with symlinks. Make output a symlink to some name:

ln -s soundfile2.wav output

Then run the command. The target file may not exist, it will be created. If it exists then data will be appended to it.

When you want to switch to another file, atomically update the symlink:

ln -sf soundfile3.wav output

The new file may be on another filesystem, it doesn't matter. Symlinking also allows you to stop saving data without stopping arecord. Just redirect our ever-running command to /dev/null:

ln -sf /dev/null output

This may be useful because most likely if you change the symlink again and start saving to a regular file, you will not trigger the pop.

The approaches without or with symlinks can be used together. When output is a symlink, simply remove it and the next dd will create output as a regular file. When output is a regular file and you want to replace it with a symlink without losing data, hardlink the file to a new name (e.g. ln output soundfile4.wav) and then atomically replace output with a symlink.

Notes:

  • iflag=fullblock is not portable. If your dd does not support it then try bs=8 count=48000. In any case we don't want a transition to a new file happen mid-sample. Our one sample is 8 bytes (32 bits times 2 channels). I'm not familiar enough with arecord and pipes in Linux, I can only hope the tool will never split a sample to more than one write and therefore dd bs=8 will read full samples even without iflag=fullblock. But I'm not sure. If dd ever reads less (compare this) then it will be able to end writing mid-sample and if you change to a new file then the new file will start with an incomplete sample and not play correctly. So if you can use iflag=fullblock then use it. Remember dd is a cranky tool which is hard to use correctly; iflag=fullblock reduces the crankiness.
  • You can change bs and/or count to increase or decrease granularity. E.g. bs=38400 count=1 will run 10 dd processes per second, so the entire setup will be able to switch to a new file almost instantly. On the other hand 10 processes per second may be unnecessary burden. Whatever fits you. Just keep the bs a multiple of 8.
  • When you terminate the command with Ctrl+C, the current dd can exit without writing. With bs=384000 count=1 you can lose up to about 1 second of already captured data. While recording important things, postpone Ctrl+C by a second or two.
  • @bobmcgrath Answer written anew. – Kamil Maciorowski Apr 12 '21 at 17:38
  • @KamilMaciorowski This works pretty well. The main thing I had to fix is that after the first switch it looses the wav header so all of the data is .raw format. So I just start with raw and add the header back in. The command is: sox -r 48000 -e unsigned -b 32 -c 2 ~/soundfile1.raw ~/soundfile1.wav – bob mcgrath Apr 28 '21 at 00:19