Envelope detector and VCA with Teensy's Audio library

RTPNaskers

Active member
Hi there!

I've been trying to build this thing for a while: an envelope detector from the signal of a bass guitar (or whatever instrument for that matter) to drive the envelope of a synth.
My first attempt, which actually work, used an analog envelope detector circuit so what I was inputting to the MCU was the envelope, which at that stage could be seen as a control signal more than an audio one.
For the sake of simplicity and also flexibility at some point, I'd like to input the pre-amplified pickup signal and then digitally get its envelope. Once I have it, it will be used as the envelope for a synth, firstly to drive the amplitude in some sort of (digital) VCA and also for the filter cutoff frequency. Pretty commom as you can see.
So, any suggestions with that? I'm using Teensy's library, cause I think it's the best and also cause the instrument I'm building it's already using it. Which diagram you'll suggest?

Thanx a lot!
 
Well, the library already has a VCA object and a "voltage controlled" filter, so the only part missing here is the envelope follower to get the "voltage" control signal.

Extracting the envelope should be pretty simple, as creating audio lib objects goes. Do you suppose this feature would be useful to more people? I might take a look... but to even begin working on this, I'd need a few pretty good audio clips for testing it.
 
Hi Paul, thanx for your quick reply!

I'll take your advice and I will use "multiply" to act as VCA, until now I was doing that by driving the amplitude variable of the waveform with the analog reading I was inputting from the envelope. Your suggestion makes more sense.

At least it will be useful for me, and I think that having an object to get envelopes can be used in a lot of situations that don't only imply audio synthesis.
I really like trying to help here: I can record some audio samples and/or help with the audio lib object development if I can get some advice on how to set and IDE to do so!

Thanx again, you rock!
 
Hmmm, I got the audio shield from last year's black friday, so I can record those. I'll give it a go trying to input the bass signal after some pre-amp.
Cheers!
 
Hey!
Been a while since I last posted here.
In this time, I've been messing around the audio library and the audio shield just to corroborate how great they are.
I've tried a couple things with the codec and the effects and the sd card playback too.
Also managed to wire the thing up so I can record samples onto the sd card and yesterday, finally recorded the bass thing.
I modified the recorder sketch for it to work with just one push button (I don't have may of those around lately) and actually recorded and played back a few seconds of the bass playing after an audio preamp.
I've been trying, not hardly, to read the .raw data in Matlab without luck so I can't tell if everything went fine.
I'm attaching it (via Drive) https://drive.google.com/file/d/0BwuJ8dDZ7C_uYlh4WFVMbGhFdU0/view?usp=sharing
Cheers!
 
to get the envelope, isn't it basically a low pass filter, or in other words, reading one (amplitude) sample at an interval of (x) and averaging it with an arbitrary interval of samples (then convert to absolute value) and sending that to the 'envelope' variable that you require? Is it more complicated than that?
 
Easiest way is to take the absolute value of each sample and the doing a moving average with a more or less sophisticated LPF
 
yeap, I supose so, actually the analog circuitry that I was using was doing that; two diodes were rectifying the wave so every value was "absolute" and a single RC net was acting as an LPF with low frequency cutoff frequency. Guess that the operation should be kinda similar in the digital domain!
 
Hi!
I need to get this topic back to life cause the project where i will be using the audio object is near of a deadline!

During this time I've been experimenting with the audio library and even trying to understand how to write new audio objects for it, anyways still don't get the whole thing so i need some advice/help.

What I've been able to do is computing a similar functionality of the one I need but in Matlab: a simple envelope follower that does what some of you folks suggested; getting the absolute value of the signal and then low pass filtering it. The function works well enough and at least I think it will help me to design the object aslong as i've learnt which cutoff frequency on the lowpass filtering gives a more edgy envelope and also the coeficients for a biquad filter.

Here's a link to the matlab function and the input and output files.
https://drive.google.com/folderview?id=0B9UQzZr4Pin_Q2F1cERaLWVCVHc&usp=sharing

I've used an slice of the same audio file I uploaded which was recorded with teensy's audio shield (there's some noises, that I guess that have something to do with the SD writing process cause I don't hear them when I just bypass the audio through the shield...)

Also some plots of the signal process:

Original signal:

bassStroke_Original.jpg

Extracted envelope:

bassStroke_Envelope.jpg

Processed signal, a 110Hz sine wave envelope by the original signal:

bassStroke_Output.jpg


So, if anyone can give some advice it'll be really nice, I'm thinking that the most straightforward thing to do will be modyfing the biquad filter object and make it filter with the coeficients I got and just before that process getting the absolute value of the signal, is that right? Will it work to just pick up the input block samples and abs(them) in the same loop they're beeing filtered or should I abs the whole block and then filter it?

Thanks a lot!
 
Ouch, nobody?
I've been trying couple things:

- Using analyzePeak/RMS to feed it's reading to the gain of the oscillator: it controls its amplitued but he variations introduce a lot of harshness, not good.

- Using the output of a LowPass (both biquad and variable) with a cutoff at 15-30Hz to control the VCA of the oscilloscope, a ring modulator effect occured, interesting but not what I was looking for.

- The same above but trying to abs(values) inside the filtering objects, actually I'm not sure that I modified the correct line (NEED HELP HERE), thought it compiled if there was a difference I coulnd't appreciate it. Here's what I modified inside biquad.cpp (for instance)

do {
in2 = abs(*data); // Does this make sense at all?
sum = signed_multiply_accumulate_32x16b(sum, b0, in2);
sum = signed_multiply_accumulate_32x16t(sum, b1, bprev);
sum = signed_multiply_accumulate_32x16b(sum, b2, bprev);
sum = signed_multiply_accumulate_32x16t(sum, a1, aprev);
sum = signed_multiply_accumulate_32x16b(sum, a2, aprev);
out2 = signed_saturate_rshift(sum, 16, 14);
sum &= 0x3FFF;
sum = signed_multiply_accumulate_32x16t(sum, b0, in2);
sum = signed_multiply_accumulate_32x16b(sum, b1, in2);
sum = signed_multiply_accumulate_32x16t(sum, b2, bprev);
sum = signed_multiply_accumulate_32x16b(sum, a1, out2);
sum = signed_multiply_accumulate_32x16t(sum, a2, aprev);
bprev = in2;
aprev = pack_16b_16b(
signed_saturate_rshift(sum, 16, 14), out2);
sum &= 0x3FFF;
bprev = in2;
*data++ = aprev;
} while (data < end);

Maybe that change in the code doesn't make anything at all... this code feels kinda low-level-ish to me and I'm pretty lost when I read it, let alone hack it propperly!
Once again HELP!!!

Salut!
 
Try making an object which only does the absolute value. That should be really simple code. You can just feed its output to an already-working filter. That'll be a *lot* easier than messing with this complicated biquad filter code!

One really important detail is the pointer and your variables need to be int16_t type. Many of the objects in the library use uint32_t, because they process 2 samples at the same time. Doing 2 at once can speed things up by at most 2X, but it makes the code 10X harder to understand and edit.

Keep it simple. The code should probably look something like this:

Code:
        int16_t *p, *end;
        audio_block_t *block;

        block = receiveReadOnly(0);
        if (block == NULL) return;

        p = block->data;
        end = p + AUDIO_BLOCK_SAMPLES;
        while (p < end) {
                int16_t s = *p;
                if (s < 0) {
                        if (s == -32768) {
                                s = 32767;
                        } else {
                                s *= -1;
                        }
                        *p = s;
                }
                p++;
        }
        transmit(block);
        release(block);
 
Hi Paul,

I'm taking you're advice and will create the abs object and see (if I manage to get it working) if it does the trick!

Thanks a lot!
 
YO!

The abs object works! I copy/paste Paul's code in the update function of a new object (and added it into the Audio.h also), insert it in a simple patch and voilà! it does the effect of a rectifier, doubling pitch into waveforms and a harshy uppitching on the mic.
Nice!
Tomorrow I'll plug it into a LPF and will see.
By the way, which filter object do you think will do better the job of a lowpass filtering around 15-30 Hz?

Thanks again!
 
Yay!
It works pretty well!
No noise artifacts caused by the envelope, responds as expected and it can be fed working also good to the filter control input of a second filter to perform an envelope controlled filter!
Paul, you are the man!
Thanks a lot!
 
Hi there!

As a conclusion to this thread I wanted post a little video playing the instrument I build thanks to Paul's Advice: the unoString. Some nice guy shot this video at this past weekend Maker's Faire in Barcelona (yeah, that was my deadline). Also in the video in second term appear two of my other instruments, both Teensy powered, of course!

https://www.youtube.com/watch?v=nwuWPWI6WbQ

Thanks again for your help Paul, now the unoString, although still needs a lot of enhancements, works way better and has a nice wide dynamic range thanks to the envelope follower connected to the VCA and the VCF!

Long life to Teensy!

Salut!
 
Hello

Hello I am new to Teensy so please bare with me. I am working on a school project where we are taking a very low frequency amplified signal(about 50 Hz) that we are wanting to store onto an SD card. Before passing our signal into an ADC, we want to capture the envelope of the signal. Is it possible to use this same method with the absolute value code to do this?

Thanks,
Samuel W.
 
Hi Samuel,

Sure you can do that, with the abs object and a low pass filter it should output the envelope as you need, I can send you the .h and .cpp of the abs object if you need even giving you any further help with the program also.

Salut!
 
I am very new to teensy so I have a couple of questions. Can you please send me the full code that you used to implement the abs object and the LPF for the envelope detector? I downloaded the Teensyduino software and I don't know if I need to use the .h or .cpp file to run it but whichever file format would work better for Teensyduino I would prefer. Additionally, do you happen to know the detectable input frequency range of the Teensy off the top of your head?

Thanks,
Samuel Welker
 
Hi Sam,

First of all, for being able to use teensy's audiolibrary you need to use a Teensy 3.x board (from 3.1 to 3.6), which are you using?.
Then, for inputting audio you can use different sources, ranging from the analog converters on the teensy using "AudioInputAnalog adc1(analogInputPinNumber);" to the USB sending audio data from the computer where you have the teensy attached amongst others (more info at: https://www.pjrc.com/teensy/gui/?info=AudioInputAnalog)
Which are you planning to use? I need to know this to help you writing the code of the sketch. The frequency range of the input in fact will depend on which input you use, I think that if your signal is a 50Hz sine, any of them will work with no problem though.
I'm attaching the code of the abs object, both .h and .cpp (you need both to declare and implement a class in C++ following teensy's audiolibrary framework). Also you'll need to add a line to the Audio.h file that you already shoud have in the Audio library folder for it to call the .h file of the new class. Anyways don't bother about that cause I'm attaching the modified Audio.h file, so you'll only need to put these three files (Audio.h, effect_abs.h and effect_abs.cpp) in the Audio library folder (located at ..Arduino/libraries/Audio/) replacing the Audio.h that you already have. For the low pass filter I simply used AudioFilterStateVariable that's already on the teensy library and Paul recommended me to use in this same thread.
One question that comes to my mind is, what do you want to do with the envelope you detect? Write it onto a file? The signal that you want to detect has a certain envelope that you want to measure and log? tell me more too! :)
So, hope this helps and whenever you explain which audio input do you want to use I can help you with the code in the sketch.
Salut!
 

Attachments

  • effec_abs&audio_h.zip
    5.3 KB · Views: 166
I am currently using the Teensy 3.6 board. I am planning on using the analog inputs on the teensy for the input signal. Do you think that the teensy will be able to handle something as high as 1MHz freq? I wasn't able to find anything in the data sheet that talked about detectable freq range. We are attempting to use the Teensy to detect human breathing patterns, so we are planning on feeding the input signals envelope into the Teensy's built in ADCs and then storing that data onto an SD card using the Teensy 3.6 micro SD dock. Thank you so much for all of your help, let me know if you have any other questions that I could answer for you to help me better.

Thanks,
Samuel W.
 
1MHz is much (much) higher than Teensy's (or most of microcontrollers) onboard ADCs can handle. Actually the audio library (being an audio library) is meant to work with audio signals which can go as high as 20kHz so the sampling frequency suffices to be as the typical 44100Hz or as much as 192kHz for higher resolution codecs. Anyways, I don't think that this can be any problem cause I don't really think that you need to sample signals near those 1MHz rates when you're trying to sample the outcome signal of a breathe detector. I'm pretty sure that anything in the audio range will be more than enough. Which sensor/transducer are you using to capture the breathe? The datasheet should say which is the bandwidth of the signal they give and I suspect that is nothing close to 1MHz...
In the other hand, something that is a little confusing to me is that you say you want feed the signal's envelope into the Teensy's ADC, then... do you mean you plan to, somehow (analog circuitry I guess), get the envelope of the signal in the analog domain and then convert it to digital? If so, you will get the envelope of the signal right into the ADC so there will be no need to detect the envelope once more digitally... OR, do you mean that you want to feed the signal into the ADC and then detect its envelope in the digital domain (using Teensy's libraries) and then storing it into the SD?
 
so you'll only need to put these three files (Audio.h, effect_abs.h and effect_abs.cpp) in the Audio library folder (located at ..Arduino/libraries/Audio/)

I am eager to try out your envelope follower. I downloaded the zip however the files included are not effect_abs.h and effect_abs.cpp, but rather effect_envelope.cpp and effect_envelope.h. Are these the right files? Also, could you post a simple example sketch on how to use the envelope follower? thanks!!
 
Ooops, my bad! You're right those are not the files, sorry! I'm attaching the right ones this time!
Concerning the example, I'll send tomorrow allright?

Salut!
 

Attachments

  • effec_abs&audio_h.zip
    5.4 KB · Views: 205
Back
Top