Real Time Audio Output, what am I doing wrong?


I’ve been experimenting with interrupts as others have with producing audio coming out of DAC0. If I do something simple like a ramp, I am having no problem at all. However, if I try and use the sin() function, it seems that this function is taking way too long to allow me to do 44.1kHz audio. I’m new to ARM programming and was under the assumption this is probably due to floating point math? I’m trying to determine if I should just move on to something like the Beagle Bone that has a floating point processor if I don’t want to be limited already when I am hardly doing anything but generating a sine wave.

The ultimate goal is creating a signal processor. Given I’d like to do more advanced things like convolution and such, I am thinking the DUE just might not be powerful enough.




As far as I know, the sin() function inside these libraries are probably using Taylor Series (a kind of matematical expansion) which involves a lot of calculations per sample. Plus, the absence of floating point unit in the Cortex M3 will make such calculations even slower.

I would suggest that you use a table-lookup for these function and interpolate them between samples. Use an Excel to create a table of a cycle of sine wave and then round them and copy these values into an array, like SineTable[256]. (example 256 samples)

If you need to create a sine wave which you can control its frequency, you have to implement a DDS algorithm (Digital Direct Synthesis). Look in the Analog Electronics Example about the DDS algorithm in the net.

hope that helps.

Thanks so much for the reply, I was thinking this was the case and I'd need to implement a table lookup. I will walk down this path as well as consider the BeagleBone as well. I REALLY like the DUE, but I am finding the choices for pinout are limiting what I'd like to do. For instance, I'd really have liked to use the SMC to control a 16-bit LCD, but, the pinout chosen will not allow me to do that, one of the pins isn't even brought out.

Yet, I have to say, it's still VERY easy to code which I think was their biggest goal and certainly exceeds the other boards I have looked at. With the BeagleBone or the ST solutions, I've pretty much have to build my complete tool chain myself mostly. And given the issues with RT Linux not being as RT enough for me, I'd be looking at using the Starterware stuff for the Cortex M8.

Given I am in the development part of my project, implementing my code on both will be more interesting nevertheless.

If I'm not mistaken, since BeagleBone runs off from a Linux, creating a software oscillator will sap a lot of cycles away from the OS, rendering the system very jerky and such. Sound and signal generations are typically done off-chip, or seperately. Look at the old video game consoles and arcades - they have a seperate sound module because back then, processors are 1MHz~10MHz only. Even modern systems have sound chips.

The Due works well in situations like like signal processing since it doesn't have an OS. So you can do whatever you want on it without the OS.

I have created music boxes and phase-mod synthesis algorithms for PIC32 and dsPIC, and works pretty well with table lookups. However, all of these are without the DAC and I have to use the PWM, which would have more noise compared to the DAC.

You don't have to use Excel and import the lookup values, you could just call sin() on the Due beforehand (if there's time at startup, for example), and store the values.

Regards, Jim

Like this -

See the createSineTable function which pre computes a sine table which is then used in the 44.1Khz interrupt

Duane B

Thanks to everyone for all the information, I have been studying a lot of the DDS examples I could find and I am definitely playing around with that method of generation as well.

I think it all comes down to if I want to work in integer math, or floating point. I've spent considerable amount of time looking at three solutions; (1) DUE, (2) BeagleBone, (3) STM32F4Discovery.

I also have a Mega2560 but that isn't fast enough for the audio rate stuff I want to do.

Given I already have the DUE, I am still having fun working with it. While my original plan seems to be difficult with it, I am finding I can still do a lot with it and will continue to use it for more simple specific ideas I want to do. Without a doubt, it's the easiest to program as the toolchain is just simple. While 44.1kHz output is possible with careful coding, I'm still concerned with the input ADC time in freerun mode (and how accurate it is with audio) and/or will that even be possible with trying to hit 44.1kHz. If it isn't, then I will look at just using it as more of a sound generation project and just use CV frequency (LFO rate) inputs.

The STM32F4Discovery was recommended by a coworker. Cost wise, it's like $15 and comes with a floating point processor. I've looked at the cycle-times for floating point operation and they are pretty impressive. It runs at about 2x the frequency of the DUE (although not sure the actual processing difference) and it appears the built in ADC is pretty powerful. Also has built in DAC that can theoretically do up to 96kHz. ( with built in amplifier. The Cortex chip itself has 3 ADC and 2 DAC as well apparently, and they seem to be easily able to handle 44.1k audio rate. In fact, it appears I can multiplex with them and still get 44.1khz out of them, not too bad. The tool chain is a little more work, but given I know someone what already has set this up on OSX, that's a huge advantage. Lastly it has DSP specific instructions like Multiply in one cycle which is pretty awesome.

The BeagleBone is definitely the most powerful of the 3 but the least developed on. I would not use Linux. In all my reading I've done so far, even with all the RT patches it's just not very fast or predictable. That said, the next step is to use STARTERWARE from TI instead. Of course, this will mean I will need a virtual linux image on my MAC to talk with their toolchain (or, until I can find OSX equivalent tools), but, it has the advantage of 700Mhz and of course Floating Point as well. The cape I bought does 2x audio in and 2x audio out at 96kHz 24-bit. This is more than enough for my needs. In addition, there are 7 other analog inputs that can be used for things like CV voltage that I want to read.

It's hard to say there is a winner in all this because if you look at each of these setups, there is a lot of give and take. The DUE has just been a blast to code with. Without any personal hands on experience it seems like the ST is going to really be the sweet spot of price/performance if I want to build a bunch of these, but of course, having all that power on the BeagleBone is tempting.

Duane, your RCarduino stuff is awesome. Thanks for putting the effort forth in sharing all that information. Without a doubt you helped me come up to speed MUCH faster, especially with the interrupt code you pulled together.

What about a Raspberry Pi but running Bare Metal not Linux. That runs at between 700 to 1000MHz depending on the clocking options.

The GPIO seems to be pretty lacking on the one I was playing with. Also, it's an older ARM core (unless there was a newer one they released I didn't know about) so it is a bit slower in operation. My IO requirements at the moment are a TFT LCD display, SD card (which is obviously not a problem for the pi or the beaglebone out of the box), 2 ADC in at audio rate, 4 ADC in at LFO rate, 8 gate/trig in, 2 DAC out at audio rate, 4 at LFO rate, 8 gate/trig out.

That's an exceptional amount of IO. Not all used at once mind you. I just think getting all that out of the pi would be difficult, but maybe there is just something I misunderstand.

I would choose the Due first due to the simplicity of the compiler. The STM32 ones are a bit more difficult to use, but the features in the STM32 are quite nice too, which one of these include a floating point unit.

Thanks to DuaneB also for sharing some of the DDS tips on the Arduino Due.

I did use the DDS code to try and really push the Due. At lower frequencies, the output was as expected, but at the upper frequencies, the waveform was much more stepped. That said, I was able to generate 8 sins at once and mix them together. Though I will say, the more sins I added, the noise floor kept going up significantly. Not sure what that was about.

I also agree the toolchain for the STM32F4 is not the easiest. Given I know someone who already has it all figured out, that makes a SIGNIFICANT difference. I've been installing the toolchain that I found from ARM themselves. My only concern is making sure the floating point unit is getting used by the compiler, or, if I have to access it with ASM. I mean, if I can't use the floating point unit, kinda makes using the STM32F4 pointless.

I also got the beaglebone in. Have really done nothing with it yet since i feel it will be the most work.

Things are kinda on hold as I wait for more parts to build my gain/offset circuits in terms of pushing the DUE more.

Have you used some form of interpolation between samples? On higher frequencies, it could be distorted due to truncation errors. Linear interpolation will be still OK as it's the quickest way to get rid of the noise.

Just curious, what are you trying to use the Due for? A toy piano? It sounded like you are attempting to build a synthesizer on it.

There is a small update in the DDS Code contained in the link which may improve your results.

Duane B

Duane, im doing some deep research on sound synthesis, and some basic DSP as well, and trying to stretch the Arduinos a but, as well as using other uC's( Pics and dsPics), so want to thanks you for your sharing of the info !!