Any way to have Macros export as unsigned 8 bit PCM 8kHz?

I’m starting on a project to have a small microcontroller take 8 bit unsigned PCM data from FAT32 files on an SD card and send those audio samples to an onboard DAC, to be voiced out over a two-way radio system. Once I get that working I may want to try using uLaw PCM to gain a little bit better S/N. Since the radio link has limited bandwidth (3 kHz), the over-the-radio fidelity is not too important, instead intelligibility is of prime importance.

My plan is to record the voice phrases and words with good fidelity at 44100 using Audacity and then use the Macro feature to process the audio (convert stereo to mono, low pass filter at 2800 Hz, compress a little bit). All that I have working and it works well. My problem is that I don’t see a way to use Audacity Macros to convert the 41000 Hz samples to 8000 Hz samples, and then export those samples as 8 bit unsigned PCM. I assume that the rate change and the Macro export as 8 bit unsigned PCM are not supported by Audicity Macros. Is that correct?

I’ve looked at using ffmpeg to convert 44100Hz WAV to 8000 Hz PCM, but it appears to my untrained eye that that program does not support a change in audio rate.

Can anyone offer some suggestions as to how I can batch convert a couple of hundred audio files from 44100 WAV to 8000 unsigned 8 bit PCM? I know that I could do the original recording at 8000 Hz sampling, but I’d prefer that the original audio recordings be done with good fidelity (hence the 44100 Hz rate for the original recordings).

Thank you.

Perhaps worth testing with just one, or a few files before batch converting hundreds of files.

I’m assuming that you want headerless (RAW) files (rather than WAV files). That will make the task more tricky as Macros don’t have handy commands for exporting RAW. Best to work out exactly what you want first.

To batch process files to RAW, you will need to include a Nyquist script in your macro, so that you can use the “Export2” command.
“Export2” supports RAW export, but you have to include the exact file name and output directory, which must change for each export (so as to avoid overwriting the same file each time).


I think FFmpeg does support changing the audio sample rate, though SoX is probably easier to use if you want to write a stand-alone script.

Thank you Steve.

I would prefer headerless files, but I can code the MCU that would be reading the files to absorb the header, or write something in C or Perl to strip out the headers as part of the overall PCM file generation process. Thanks also for the pointer to the Nyquist Script and Export2. I will give those a try, as well as look into SoX.

Paul

If you are familiar with scripting, the I think SoX will probably be the easiest option.

Let us know how you get on.

SoX is doing what I’m looking for. Thank you! I can’t say that I fully understand the command sequence, but I am getting what I want.

I have a command line in a small batch folder that coverts a WAV file to an 8 bit 8k sample/sec u-Law output.

“c:/Program Files (x86)/sox-14-4-2/sox.exe” input.wav -r 8000 -b 8 -e u-law out.raw remix -

the u-Law out.raw file is about the right number of bytes given the length of the original sound clip, it sounds good in Audacity and a hex dump of three cycles of a 500 Hz sine wave looks about what one would expect (given the very odd sign management of the transfer function of signed linear to u-law).

I appreciate the help.

Greetings –

I’ve been working with Audicy and SoX, to get my ulaw c code encoder and decoder tested. stand-alone testing of my ulaw encoding and decoding in c appears to be working working. But when I put it all together, I’m seeing some odd idle channel issues from Audacity, some things with Audacity produced WAV files that I don’t understand.

First, I’m having trouble pinning down the coding of the ADC samples in a 16 bit WAV file when the coding mode is 1 (PCM). For WAV files, does PCM mean twos-compliment or sign-and-magnitude? In looking at the WAV file format on http://www.lightlink.com/tjweber/StripWav/Canon.html , that discussion says that the WAV 16 bit data values are coded as 2’s compliment numbers. But I’m not seeing that in the WAV file HEX dump. To me it looks like bit 15 (MSB) is the sign bit, and bits 0-14 are magnitude values.

For example, when I use Audacity’s Generate->Silence function to create a silent mono WAV file of 10 ms duration, and use a hex viewer to look at the WAV file, I’m seeing the following

WAV File HEX dump

Left column is byte offset in HEX then DECIMAL
Right column is HEX value found in WAV file at that offset (comment in parenthesis)
00-00 0x52 (ascii R as in RIFF)
01-01 0x49 (ascii I as in RIFF)
02-02 0x46 (ascii F as in RIFF)
03-03 0x46 (ascii F as in RIFF)

14-20 0x01 (format is 1, PCM)

24-36 0x64 (ascii d as in data)
25-37 0x61 (ascii a as in data)
26-38 0x74 (ascii t as in data)
27-39 0x61 (ascii a as in data)
28-40 0x3A (<- data block length, little endian, so low byte)
29-41 0x11 (<- data block length, little endian)
2A-42 0x00 (<- data block length, little endian)
2B-43 0x00 (<- data block length, little endian, so high byte) LENGTH = 0x113A (equals 4410 base 10, equals 10 ms at 44100 samples/sec)

(data starts at offset 2C-44)

2C-44 0x80 (<- ADC sample 1, little endian, so this is low byte)
2D-45 0x80 (<- ADC sample 1, little endian, so this is high byte) ADC value = 0x8080
2E-46 0x7F (<- ADC sample 2, little endian, low byte)
2F-47 0x80 (<- ADC sample 2, little endian, high byte) ADC value = 0x807F
30-48 0x7F (<- ADC sample 3, little endian, low byte)
31-49 0x80 (<- ADC sample 3, little endian, high byte) ADC value = 0x807F
32-50 0x7F (<- ADC sample 4, little endian, low byte)
33-51 0x80 (<- ADC sample 4, little endian, high byte) ADC value = 0x807F
34-52 0x7F

So if these data were 2’s compliment, then the first four ADC values would be -32640, -32641, -32641, -32641 (respectively)
which makes no sense. If these data were coded as sign and magnitude, then the first four ADC values would
be -128, -127, -127, -127 which make more sense. But if the WAV data are coded as sign and magnitude,
when why wouldn’t the hex dump word values be 0x0000 or 0x8000? In a system that’s
totally digital, with no op-amp offset voltages, why do the Audacity WAV files have this 128 unit offset?

Probably [u]dither[/u]. Disable dither and pure digital silence should be a series of zeros. :wink:

For WAV files, does PCM mean twos-compliment or sign-and-magnitude?

Yes, two’s compliment with the MSB for the sign. If you just count-up (increment ) in 16-bit integer you count to +32767 and the next value is -32768, and it starts counting up (less negative) from there until it rolls-over again at zero.

8-bit WAV files are unsigned with silence at 127 or 128 (I forget which).

10 ms of 16-bit integer mono is 441 samples = 882 bytes. :confused:


Also, as DVDdoug wrote, if you want absolute silence in a WAV file that is less than 32-bits per sample, you will need to turn off dither in:
“Edit menu > Preferences > Quality → High quality conversion”
Signed 16-bit PCM data will then look like:

00 00 00 00 00 00 00 00 ...

Thank you DVDdoug and Steve for your helpful advice.

I was unhappy to see that with dither off my problems were still there. But I did find that my original silent sample was 100ms rather than the 10ms I thought it was. So that solved part of the mystery.

But I just couldn’t make sense of the data samples. If I plotted them it was nonsense. Eventually I saw that the block byte count was half of what I thought it should be – and when I looked at the data as though it was unsigned 8 bit samples then it all made sense. I then found that I had changed Audacity’s WAV output (probably two or three days ago) to 8 bit unsigned and forgot to put it back to signed 16 bit PCM. With that set correctly to signed 16 bit PCM then the hex dump data make perfect sense (and the data are what I expected them to be). I also found that with dither ON that the 16bit WAV file extends only from -3 to +4, which in the scheme of things is a tiny amount of noise. With Dither off the sample values were all 0, as you had pointed out they should be.

So, crisis averted! Thank you both again for your help.