iphone low pass filter
Asked Answered
M

2

13

I'm trying to implement a low-pass filter for an iphone app where I record a sound and then it gets played back slightly muffled; like the voice is coming from another room.

I've looked into the different options for audio recording and manipulation and have found it a bit confusing...digital signal processing isn't a strong point at all. I've mainly looked into OpenAL and inside the EFX library there is a filter that specifically does what I need, but EFX is not included on the iPhone. Is there a way of replicating that behaviour using OpenAL for the iPhone? Is there another option such as Audio Units that could provide a solution?

Thanks for your help

EDIT:

So after Tom's answer and links, I've come up with what I think is a correct implementation. However, I'm not getting a muffling effect at all, rather just a decrease in volume. Here's the (summarised) code I have currently:

File is recorded using AVAudioRecorder and the following settings:

[recordSetting setValue :[NSNumber numberWithInt:kAudioFormatLinearPCM] forKey:AVFormatIDKey];
[recordSetting setValue:[NSNumber numberWithFloat:44100] forKey:AVSampleRateKey];
[recordSetting setValue:[NSNumber numberWithInt: 1] forKey:AVNumberOfChannelsKey];

[recordSetting setValue :[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];
[recordSetting setValue :[NSNumber numberWithBool:NO] forKey:AVLinearPCMIsBigEndianKey];
[recordSetting setValue :[NSNumber numberWithBool:NO] forKey:AVLinearPCMIsFloatKey];

I then read in the file and transform it with the code below:

// Read in the file using AudioFileOpenURL
AudioFileID fileID = [self openAudioFile:filePath];

// find out how big the actual audio data is
UInt32 fileSize = [self audioFileSize:fileID];

// allocate the memory to hold the file
SInt16 * outData = (SInt16 *)malloc(fileSize); 

// Read in the file to outData
OSStatus result = noErr;
result = AudioFileReadBytes(fileID, false, 0, &fileSize, outData);

// close off the file
AudioFileClose(fileID);

// Allocate memory to hold the transformed values
SInt16 * transformData = (SInt16 *)malloc(fileSize);

// Start the transform - Need to set alpha to 0.15 or below to have a noticeable affect
float alpha = 1;         

// Code as per Tom's example
transformData[0] = outData[0];

for(int sample = 1; sample < fileSize / sizeof(SInt16); sample ++) 
{
     transformData[sample] = transformData[sample - 1] + alpha * (outData[sample] - transformData[sample - 1]);
}

// Add the data to OpenAL buffer
NSUInteger bufferID;
// grab a buffer ID from openAL
alGenBuffers(1, &bufferID);

// Add the audio data into the new buffer
alBufferData(bufferID,AL_FORMAT_MONO16,transformData,fileSize,44100);

So after all that, I then play it through OpenAL using the standard method (I don't think it has any impact on my results so I won't include it here.)

I've traced through the results, both before and after transform, and they seem correct to me i.e. the values before vary positively and negatively as I would expect and the for loop is definitely flattening out those values. But as I mentioned before I'm only seeing (what seems to me) a reduction in volume, so I'm able to increase the gain and cancel out what I've just done.

It seems that I must be working on the wrong values. Any suggestions of what I'm doing wrong here?

Megaspore answered 22/12, 2010 at 1:11 Comment(1)
Hello.... Have you find anything on this ?Outrigger
G
8

Tom's answer is the following recursive filter:

y[n] = (1 - a)*y[n-1] + a*x[n]

H(z) = Y(z)/X(z) = a / (1 - (1 - a)*1/z)

I'll plot this in Python/pylab for a=0.25, a=0.50, and a=0.75:

from pylab import *

def H(a, z):
    return a / (1 - (1 - a) / z)

w = r_[0:1000]*pi/1000
z = exp(1j*w)
H1 = H(0.25, z)
H2 = H(0.50, z)
H3 = H(0.75, z)
plot(w, abs(H1), 'r') # red
plot(w, abs(H2), 'g') # green
plot(w, abs(H3), 'b') # blue

alt text

Pi radians/sample is the Nyquist frequency, which is half the sampling frequency.

If this simple filter is inadequate, try a 2nd order Butterworth filter:

# 2nd order filter:
# y[n] = -a[1]*y[n-1] - a[2]*y[n-2] + b[0]*x[n] + b[1]*x[n-1] + b[2]*x[n-2]

import scipy.signal as signal

# 2nd order Butterworth filter coefficients b,a
# 3dB cutoff = 2000 Hz
fc = 2000.0/44100
b, a = signal.butter(2, 2*fc)

# b = [ 0.01681915,  0.0336383 ,  0.01681915]
# a = [ 1.        , -1.60109239,  0.66836899]

# approximately:
# y[n] = 1.60109*y[n-1] - 0.66837*y[n-2] + 
#        0.01682*x[n] + 0.03364*x[n-1] + 0.01682*x[n-2]

# transfer function
def H(b,a,z):
    num = b[0] + b[1]/z + b[2]/(z**2)
    den = a[0] + a[1]/z + a[2]/(z**2)
    return num/den

H4 = H(b, a, z)
plot(w, abs(H4))
# show the corner frequency
plot(2*pi*fc, sqrt(2)/2, 'ro')  
xlabel('radians')

alt text

Evaluate a test signal at the 3dB cutoff frequency fc=2000:

fc = 2000.0/44100
b, a = signal.butter(2, 2*fc)

# test signal at corner frequency (signed 16-bit)
N = int(5/fc)  # sample for 5 cycles
x = int16(32767 * cos(2*pi*fc*r_[0:N]))

# signed 16-bit output
yout = zeros(size(x), dtype=int16)

# temp floats
y  = 0.0    
y1 = 0.0
y2 = 0.0

# filter the input
for n in r_[0:N]:
    y = (-a[1] * y1 + 
         -a[2] * y2 + 
          b[0] * x[n]   + 
          b[1] * x[n-1] + 
          b[2] * x[n-2])
    # convert to int16 and saturate
    if y > 32767.0:    yout[n] = 32767
    elif y < -32768.0: yout[n] = -32768
    else:              yout[n] = int16(y)
    # shift the variables
    y2 = y1
    y1 = y

# plots
plot(x,'r')       # input in red
plot(yout,'g')    # output in green
# show that this is the 3dB point
plot(sqrt(2)/2 * 32768 * ones(N),'b-')
xlabel('samples')

alt text

Gorlovka answered 16/1, 2011 at 1:6 Comment(2)
this mostly makes sense to me from all the recent reading i've done on dsp. I'll give it a go asap (i.e. tomorrow morning). The one point that I want to clarify though is that the data read from the recorded file comes back as an array of integers and they range from -7000 to 7000 from the simple voice tests that I've done. I'm guessing this is straight amplitude for each sample and is what is fed into my outData variable above. Is that the right data to be feeding into this algorithm?Megaspore
This is a brilliant answer -- and a good pylab tutorial to boot!Alkalinity
Z
0

I don't know much about digital signal processing, but I believe you can approximate a low pass filter using linear interpolation. See the end of this Wikipedia article for info.

Here's a code snippet that may give you an idea. alpha is the filter coefficient. Decreasing the value of alpha will increase the muffling effect, IIRC.


output_samples[0] = input_samples[0];

for(int sample = 1; sample < num_samples; sample ++) {
  output_samples[sample] = output_samples[sample - 1] + 
    alpha * (input_samples[sample] - output_samples[sample - 1]);
}

EDIT: I think alpha is generally between 0 and 1 here.

Zenaidazenana answered 29/12, 2010 at 21:34 Comment(6)
Thanks for the response. The function is great and I'll definitely use it but I was also wondering how to get to the low level data in the first place. I'm guessing I will have to use Core Audio for this? Sorry I can't investigate myself but I'm just assisting an iPhone developer with this functionality and don't have an environment to test in.Megaspore
I'm guessing you will have to use Core Audio. I just found another question related to this and apparently the OP used Core Audio for his low pass filter on iOS. Here's the link: #4163004Zenaidazenana
Finally, here is an overview on Core Audio: developer.apple.com/library/ios/#documentation/MusicAudio/…Zenaidazenana
As well as your example, I tried implementing the butterworth filter linked to in your comment but saw the same results (i.e. a reduction in volume) as with your function. Leads me to believe I'm not dealing with the sampled data correctlyMegaspore
Besides the decrease in volume, do the filters seem to work? That is to say, for the low pass filter are you hearing a decrease in higher frequencies? In general, these simple filters can definitely reduce the overall volume because they're only allowing part of the input signal through and blocking the rest. You could boost the volume after the filtering to compensate.Zenaidazenana
No they're not affecting the higher frequencies at all. I was able to increase the gain when playing back the sound through openal and I ended up with the original audioMegaspore

© 2022 - 2024 — McMap. All rights reserved.