bartolsthoorn / NVDSP

iOS/OSX DSP for audio (with Novocaine)
MIT License
413 stars 80 forks source link

BandRejectFilter, how to apply two filters at once and how to filter vocals? #11

Closed TanejaArun closed 10 years ago

TanejaArun commented 10 years ago

Is there any privilege to apply band reject filter? or Is it possible to apply two filters at once? I tried it but with no success. I think it should have band reject filter too.

bartolsthoorn commented 10 years ago

There is a band reject filter in NVDSP, the notch filter. You can set the Q to vary the width of the filter.

Also, it is definitely possible to apply two filters at once. You first create/allocate them, and then use them. For example:

// setup filters
NVHighpassFilter *HPF;
HPF = [[NVHighpassFilter alloc] initWithSamplingRate:samplingRate];

NVLowpassFilter *LPF;
LPF = [[NVLighpassFilter alloc] initWithSamplingRate:samplingRate];

LPF.cornerFrequency = 2000.0f;
LPF.Q = 0.5f;

HPF.cornerFrequency = 400.0f;
HPF.Q = 0.5f;

// setup audio output block
[fileReader play];
[audioManager setOutputBlock:^(float *outData, UInt32 numFrames, UInt32 numChannels) {
    [fileReader retrieveFreshAudio:outData numFrames:numFrames numChannels:numChannels];

    [HPF filterData:outData numFrames:numFrames numChannels:numChannels];
    [LPF filterData:outData numFrames:numFrames numChannels:numChannels];
}];

This code creates a sort of band filter (using a HPF and LPF) and passes everything from 400 Hz to 2000 Hz.

TanejaArun commented 10 years ago

Thank you for reply, I have already implemented this to apply two filters at once but it gave me the same output as the output from a single filter. What i was trying to do is to reduce vocals from a song and i applied low pass filter with corner frequency 60.0f and high pass filter with corner frequency 4000.0f , It gave me the output of high pass filter only.

And also Is it possible with NVDSP to remove vocals? By splitting the stereo track and then invert one of the channel's sample and then changing both the stereo tracks to mono? like we do in audacity. I badly need it. Please let me know if this is possible with NVDSP. Thank you in advance.

bartolsthoorn commented 10 years ago

The corner frequencies you mentioned don't make sense.

So you should be left with no audio because the LPF produces very low signal (< 60 Hz) and the HPF doesn't allow low signals to pass.

About the vocal removal, I think that's possible. To split the stereo track into two separate buffers you can use NVDSP deinterleave. Then you apply a gain of -1.0f to one channel (left or right) to shift the polarity, for example:

NVDSP *generalDSP = [[NVDSP alloc] init];
// ...
[generalDSP applyGain:left length:numFrames gain:-1.0f];

This loops through all the sample data in the left buffer and multiplies it with -1.0f. This inverts the polarity of this channel which will later cause all the audio in the middle of the mix, like usually the vocals, to be muted.

Then you can use NVDSP interleave to bring left and right buffer back into one stereo buffer. There is unfortunately no method in NVDSP right now for turning a stereo buffer into mono.

The idea behind turning stereo into mono simple though, lets say your buffers are something like this: left: -0.2 0.3 0.4 0.5 0.4 0.3 -0.2 right: -0.4 -0.3 0.1 0.4 0.2 0.1 -0.2 To turn it into mono both the left and right channel need to be the same. So you add each sample of left and right and divide it by two. This becomes the new left and right channel (they are the same). So for the first two samples: left: ((-0.2 + -0.4) / 2) ((0.3 + -0.3) /2) right: ((-0.2 + -0.4) / 2) ((0.3 + -0.3) /2) You have to divide by two because audio samples are floating numbers from -1.0f to 1.0f, above or below this range will cause clipping.

You can also see how muting appears. If the left channel and right channel had at one point a 1.0f sample, and one of them gets inverted, the sample turns into -1.0f. When you then combine both channels to mono: (1.0f + -1.0f)/2 = 0/2 = 0, nothing remains!

To do these calculations very fast you can look at the vDSP framework of iOS, or dive into NVDSP and maybe look for examples. (see how the applyGain works, it's a good example) You can also just loop through the buffers with for loops yourself, if you have some skill with C/C++. Audio buffers are just arrays of floats, so you can just handle it yourself.

Hope this helps!

TanejaArun commented 10 years ago

Thank you very much for guiding me to the right track. I'll try this and will let you know if i get success. Thanks for your kind concern.

TanejaArun commented 10 years ago

Thank you, bartolsthoorn. With your help i am able to remove vocals from a centre-panned song by applying gain of -1 to one of the channel but i just got success when i listen to the output from mac mini speaker. Might be mono speakers. But i am unable to convert stereo to mono as you said in earlier post. I have used vDSP framework for the same but didn't get success with it.

"There is unfortunately no method in NVDSP right now for turning a stereo buffer into mono." My question - Is there any in-built method now available with NVDSP? If No, then please help me with some solution.

Thanks in advance.

bartolsthoorn commented 10 years ago

I just added a function to convert stereo to mono, you were not the first to ask for it!

First of all, the code below is how you would do it without the new mono method, it's fairly simple.

NVDSP *generalDSP = [[NVDSP alloc] init];

[self.audioManager setOutputBlock:^(float *data, UInt32 numFrames, UInt32 numChannels)
 {
     [wself.fileReader retrieveFreshAudio:data numFrames:numFrames numChannels:numChannels];

     // Deinterleave stereo buffer into seperate left and right
     float *left = (float *)malloc((numFrames + 2) * sizeof(float));
     float *right = (float *)malloc((numFrames + 2) * sizeof(float));
     [generalDSP deinterleave:data left:left right:right length:numFrames];

     // 50% left, 50% right
     [generalDSP applyGain:left length:numFrames gain:0.5];
     [generalDSP applyGain:right length:numFrames gain:0.5];

     // Add (+) right channel to left
     vDSP_vadd(left, 1, right, 1, left, 1, numFrames);

     // Turn left and right channel into a 2 channel mono ones (so fake stereo)
     [generalDSP interleave:data left:left right:left length:numFrames];

     // Free buffers
     free(left);
     free(right);
 }];

So basically it's about having left and right in separate buffers and using the vDSP_vadd function.

With the latests NVDSP code you can simply do:

// Deinterleave stereo buffer into seperate left and right
float *left = (float *)malloc((numFrames + 2) * sizeof(float));
float *right = (float *)malloc((numFrames + 2) * sizeof(float));
[generalDSP deinterleave:data left:left right:right length:numFrames];

// Convert left and right to a mono 2 channel buffer
[generalDSP mono:data left:left right:right length:numFrames];

// Free buffers
free(left);
free(right);

Please let me know if this works for you!