Audio Library

Status
Not open for further replies.
Are there any object that has modulation inputs ? I thought I saw one on Github a couple days ago but it seems gone ?
 
Hi Paul

Today testing your web Node GUI and working great - many thanks !

I find little bug in DAC module - it create AnalogOutputAnalog insteed AudioOutputAnalog


Kamil
 
I'm currently working on a state variable filter, eventually to have signal modulated corner frequency (eg, a "VCF" in the analog synth world).

It turns out to be pretty easy to get these filters to work over a moderate range of frequencies and resonance (Q) settings, but quite a bit tougher to implement one that's stable and accurate over a really wide range. So far, I've got 2X oversampling and 32 bit state, but it still hits fixed point numerical precision issues at the extremes (a subtle issue which nobody publishing on the internet seems to notice since all published code is based on floating point). I'm also still struggling with how to handle the fact these filters have gain when Q is set higher.
 
Ooo, can't wait to do some filter sweeps! I'm sorry I have no insight to help with the Q & gain issues. I'm working on some demo patches today though and hope to have some audio to share.
 
Last edited:
It turns out to be pretty easy to get these filters to work over a moderate range of frequencies and resonance (Q) settings, but quite a bit tougher to implement one that's stable and accurate over a really wide range. So far, I've got 2X oversampling and 32 bit state, but it still hits fixed point numerical precision issues at the extremes (a subtle issue which nobody publishing on the internet seems to notice since all published code is based on floating point). I'm also still struggling with how to handle the fact these filters have gain when Q is set higher.

I'm sure you have already read High Precision Q31 Biquad Cascade Filter but link just in case. There is mention of precision issues and also of prescaling (of the signal, or of the filter) to account for gains > 1.0.

Also, this seems relevant:
For fixed point filters, 24-bit coefficients and memory work well for most filters, but start to become unstable below about 300 Hz at 48 kHz sample rate (or twice that at 96 kHz). Double precision is always costly on a fixed point processor, but fortunately there is a simple technique for improving stability
http://www.earlevel.com/main/2003/02/28/biquads/
and
http://www.earlevel.com/main/2003/03/02/the-digital-state-variable-filter/
 
Last edited:
Yup, looked at that stuff when I implemented the biquad filter months ago. The ARM math lib has 4 biquad implementations. That high res one with 64 bit state is the only one that actually works well over a wide range. But it's quite slow. I ended up implementing my own using 32 bit state and noise shaping, which works down to fairly low frequencies and it still pretty fast... not nearly as fast as the limited 16 and 32 bit ones in ARM's lib, but still much faster than resorting to 64 bit math.

Much work remains to be done in the GUI on the biquad and fir. They work in the audio lib, but you have to come up with the coefficients somehow. Eventually, I plan to build simple filter calculation tools for those filters, right into the GUI.

This new effort is a state variable filter, also called an oversampled Chamberlin filter. It has a totally different set of trade-offs than biquad, where the numerical precision issues are at high frequency and low Q. The huge advantage it has is fairly simple control over the corner frequency by changing a single coefficient. It's probably the only signal controlled (or "VCF") filter this audio library is likely to have.

I believe IIR filters are the toughest part of this audio library. There's a LOT of info available about algorithms, but nearly all of it falls into 2 categories: simple ad-hoc approaches that are easy to understand but usually very flawed and often neglect the stability criteria entirely, or highly theoretical analysis that's very difficult to digest and turn into a working non-Matlab implementation. Almost nobody talks about fixed point math, or even numerical precision troubles with floats. I'm currently prototyping on Linux with double precision float and 32 bit integer running side-by-side and comparing their results with the same inputs.

I have my eye on eventually supporting soundfont. The missing pieces for soundfont's synthesis model are this state variable filter and reverb (and reverb is sort-of optional).... and a ton of code to extract and convert the soundfont format to something we can use on Teensy. That'll probably also be only realistic on top of the SPI flash storage, which is also on my TO-DO list.
 
Last edited:
A SVF with direct control over cutoff frequency, and all three outputs (HP, LP, BP) would be a great building block; it could be chained to give steeper slopes.

One of the big differences in filter sounds is the distortion behaviour as it starts to go into overload; that aspect is probably best dealt with separately in a digital implementation, with some sort of nonlinear transfer function module to distort the output or predistort the input.

(I have five filters in my analog synth, 3 are SVF).
 
I'm still debating what to do about the filter's gain when resonance (Q) is higher than 0.707.

Some code I've seen scales the input automatically. Others just let you put in whatever input you like, and if the output goes over 1.0, well, so be it. Of course, we're working with fixed point output here, so that'll mean clipping. But if you're aware of the gain, and you're doing synthesis, hopefully you'd create the input at the appropriate amplitude. Maybe?
 
Paul, is it planned or possible to Re-Connect and Re-Route the AudioConnections? This would be a great advantage for experimentation.
 
Paul, is it planned or possible to Re-Connect and Re-Route the AudioConnections?

Before the GUI, I figured this would never be worth doing.

Now I'm dreaming of proper destructors on all the objects, so a sketch that listens for real-time updates from the GUI could be made someday.

Realistically, this stuff is probably quite some time away, unless of course anyone steps up and implements it and send a good pull request. I'm just one guy, and over the last couple weeks of having too much fun building audio objects, a ton of other non-audio stuff has piled up.
 
Before the GUI, I figured this would never be worth doing.

Now I'm dreaming of proper destructors on all the objects, so a sketch that listens for real-time updates from the GUI could be made someday.

Realistically, this stuff is probably quite some time away, unless of course anyone steps up and implements it and send a good pull request. I'm just one guy, and over the last couple weeks of having too much fun building audio objects, a ton of other non-audio stuff has piled up.

If you will have some time, you might want to look into http://nmedit.sourceforge.net/ :)
 
I've been testing the new filter with real signals and it's looking pretty good.

Now I'm working on the "voltage control" part. There's 2 sticky issues....

#1: How should the input signal change the frequency? The actual data is 16 bit signed integers, which we're representing at -1.0 to +1.0 in the API. So how should -1.0, 0, +1.0 and all the values in-between modulate the filter's corner frequency?

My current thought is to try implementing Fc = Fo * 2^(signal * N), where "Fo" is the value set by the frequency() function, and "N" is a number-of-octaves parameter, to be set by another function. So if N defaults to 1 and you set the filter's nominal frequency to 1000 Hz, with full scale signal of +1.0 the filter becomes 2000 Hz, and with a -1.0 signal it becomes 500 Hz. If you set to 3, then the signal shifts the filter corner frequency up 3 octaves (to 8kHz) and down 3 octaves (to 125 Hz).

Another alternative would be simply using the signal, either linear or log scaling, without influence from any settings.

My gut feeling is log scaling will "feel" much more natural, since human hearing is logarithmic in octaves. But really, I could use some input and guidance from you guys who are into sound synthesis.


#2: The equation to convert from frequency to filter coefficient involves computing sine. The input range is only 0 to pi/4 (0.7854). I've seen several sites recommend just using a linear fit, or just assume sin(n) = n over this range. That results in a 6% error, which doesn't seem great, but then not terrible either.

A quick google search turned up this paper.

http://krisgarrett.net/papers/l2approx.pdf

The 2nd equation in section A.5 gets the frequency error well 0.1%. I'm fiddling with ways to adapt it to the Cortex-M4 DSP instructions....
 
My current thought is to try implementing Fc = Fo * 2^(signal * N), where "Fo" is the value set by the frequency() function, and "N" is a number-of-octaves parameter, to be set by another function.

That sounds good; N becomes like 1 volt/octave ie linear with respect to octaves. And Fo is the exponentiated form (analog implementations have a lot of trouble makin a good, wide range expo converter; in the digital domain its much easier and ARM4 can do it in real time).

So if N defaults to 1 and you set the filter's nominal frequency to 1000 Hz, with full scale signal of +1.0 the filter becomes 2000 Hz, and with a -1.0 signal it becomes 500 Hz. If you set to 3, then the signal shifts the filter corner frequency up 3 octaves (to 8kHz) and down 3 octaves (to 125 Hz).
That sounds good to me. Its similar to how an analog synth would be patched.


That results in a 6% error, which doesn't seem great, but then not terrible either.

The 2nd equation in section A.5 gets the frequency error well 0.1%. I'm fiddling with ways to adapt it to the Cortex-M4 DSP instructions....
6% error sounds like a big problem. 0.1% sounds fine.
 
Nantonos, any idea of the maximum speed the "control voltage" should support?

It's looking like both sin() and exp2() can be approximated, but still it's quite a good number of instructions. If I average together every 2 or 4 samples of the control signal, it could add up to be a pretty substantial savings in CPU time for complex synthesis projects using several VCFs. Maybe an extra several percent CPU time would be better if available for more mixers, oscillators, or other objects, rather than updating the filter coefficients much faster than any control signal could ever usefully modulate them?
 
Hi,

Just subscribed to the forum, first of all, to thank you guys for all the work you put in the development of this hardware and audio library. I'm very excited that there's something like this to experiment with audio processing without breaking the bank.

I'm also a software developer and developed some audio unit plugins (filters, delays, loop plugins and the like) for the mac. But mainly I wanted to give some feedback in the form of feature requests and comments from the point of view of a user (me) that could be described as an audiophile. I was looking for some DSP board to build a frequency divider and equalizer for a set of active bi-amped open baffle speakers when I discovered the teensy3.1+audio adapter. I just bought couple to start experimenting with them. I'm also interested in using them for guitar effects, but that's for later.

As far as I know, I understand there's only support for 16 bits because the effective SNR/THD of the current audio adapter is under 90dB. This makes sense for the "driver" of the audio adapter, but I think that making the sample size of the whole library just 16 bits for this reason is somewhat questionable. I also saw in this thread that some guys are trying to use higher quality ADC/DAC chips which may eventually be available to us as an alternative. It would be nice to have the capability to use more bits in this case.

But the main issue I see with 16 bits samples is more serious than having support for higher quality boards. Looking at the library code, most effects are using 32 bit arithmetic to minimize quantization/rounding errors. Also with a 32 bit processor is in many cases more efficient to do 32 bits arithmetic than using 16 bits arithmetic. Now, with a sample size of 16 bits, these effects need to convert samples from 16 to 32 bits, compute the new sample value, and then convert back from 32 to 16 bits. This is obviously less efficient than directly computing a 32 bits output from a 32 bits input.

The problem I think is that, in the process of 32 to 16 bits conversion, simple truncation (shift) produces quantization errors. To avoid these quantization errors, each effect that uses 32 bits arithmetic should perform dithering when going back to 16 bits. Processing dithering for each effect would be quite inefficient, but if no dithering is done, the quantization errors of each effect would at least add up. In many cases, these quantization errors could be multiplied by downstream effects.

Using 32 bits samples would fix this situation. 32 bit samples provide a more than enough dynamic range and precision to deal with most audio processing. Dithering would be needed only before the processed blocks are sent to the DAC and could be done in the audio board "driver", in the correct amount needed by the supported bit depth.

For example, a user could decide to attenuate the 16/24 bits input signal first in order to provide some headroom for the downstream effects chain while still having enough precision to avoid significant DSP quantization/rounding errors. A gain and soft limiter effect at the end of the chain would restore the level of the signal before the DAC.

As an obvious side effect of using 32 bit samples, the memory blocks that contain the samples would be double the size than they are now, but the number of blocks used in a chain of effects is usually small. Effects that need to save a large amount of samples (e.g. long delays) may convert samples to a lower bit count to increase delay times, but this is a design decision of the developer of a particular effect and not enforced by the library.

I think that using 32 bit samples would be a big improvement in audio quality for the library, with some potential performance gains, more clear (i.e. easy to maintain) code, and only a limited hit in memory usage.

I would also love to have support for other sample rates (just 88.2 would be great).

I think that the current ADC/DAC already supports more than 44.1KHz. I also understand that at 88.2KHz, signal processing would be perhaps limited with a 70MHz processor, but it is already possible to process simple equalization and frequency dividers without much trouble at under 100KHz sample rates. In the particular case I'm thinking of, I need to process only one channel to generate output to 2 channels, so the processing of a single channel at 88KHz sample rate is more or less equivalent to 2 channels at 44KHz.

So, it would be great if other sample rates were already supported by the library, even with the current audio adapter. This may be attractive to people that would like to experiment with the possibilities of the platform at higher rates. In the case of audiophiles, higher sample rates would make possible the implementation of high quality filters at audio frequencies close to 20KHz. Yes, I know that upsampling can be done inside effects, but it sounds like a compromise, not very efficient (if done well), and adds unnecessary complexity.

Also, from the perspective of the evolution of the platform (ok, I'm stretching things a little bit here, but it's worth mentioning I think), it is almost sure that a next teensy version would be able to process higher sample rates. I'm not saying to implement higher sample rates right now, but at least have a design, a framework, to support other sample rates, so developers of effects, device drivers, users, are all aware that the sample rate could be different than 44KHz and write more flexible code to support more capable hardware.

I read the whole thread and saw these same topics were discussed some time ago. Just wanted to share my thoughts and give my 2 cents, which is nothing compared with what you guys already did.

Thanks again.
 
I'm sure this 24-32 bit & fast sample rate question is going to keep coming up. Maybe I'll write a specific page about it?

TL;DR = Eventually, some objects might gain 32 bit support, but the plan is to do so with pairs of 16 bit streams.

Some prior messages outlined what I'm willing to accept, in the unlikely case anyone actually wants to really work on coding this and submit pull requests. If anyone seriously intends to devote real programming effort, I could elaborate further. But otherwise, I'm reluctant to pour much more time into even talking about more than 16 bit resolution.

But briefly, the main reason for the 16 bit design is the ARM Cortex-M4 DSP instructions are dramatically optimized for 16 bit samples. The ARM registers and bus are 32 bits wide, and the DSP instructions are designed for pairs of 16 bit samples packed into 32 bit registers and bus cycles. Most of the processing objects do use 32, 48 or 64 bits internally, but for passing data between objects, going over 16 bits would make everything at least 2X slower.

Many high power DSPs and PC-class systems exist for high performance audio. At some point in the future, I'm sure ARM and companies like Freescale, ST, NXP, etc will provide single-chip, low-power microcontrollers with hardware appropriate for such audio (eg, the "Neon" instructions). But today we have Cortex-M4. This library has well over a year of very careful design to really leverage the Cortex-M4 DSP capability.
 
Yeah, I did see that many objects use dsp ops and also 32 bits internally. That's why I thought going to 32 bits would be beneficial to avoid 16-32-16 conversions and lost precision. I didn't expect that passing 32 bits buffers would take 2x the time compared to passing 16 bits buffers (assuming buffers are passed by reference).
Anyway, I'll start playing with the library as soon as the boards arrive and see what I can do to adapt it.
 
Status
Not open for further replies.
Back
Top