Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Build a simple Equalizer

I would like to make a 5-band audio equalizer (60Hz, 230Hz, 910Hz, 4kHz, 14kHz) using AVAudioEngine. I would like to have the user input gain per band through a vertical slider and accordingly adjust the audio that is playing. I tried using AVAudioUnitEQ to do this, but I hear no difference when playing the audio. I tried to hardcode in values to specify a gain at each frequency, but it still does not work. Here is the code I have:

var audioEngine: AVAudioEngine = AVAudioEngine()
var equalizer: AVAudioUnitEQ!
var audioPlayerNode: AVAudioPlayerNode = AVAudioPlayerNode()
var audioFile: AVAudioFile!

// in viewDidLoad():
equalizer = AVAudioUnitEQ(numberOfBands: 5)
audioEngine.attach(audioPlayerNode)
audioEngine.attach(equalizer)
let bands = equalizer.bands
let freqs = [60, 230, 910, 4000, 14000]
audioEngine.connect(audioPlayerNode, to: equalizer, format: nil)
audioEngine.connect(equalizer, to: audioEngine.outputNode, format: nil)
for i in 0...(bands.count - 1) {
    bands[i].frequency = Float(freqs[i])
}

bands[0].gain = -10.0
bands[0].filterType = .lowShelf
bands[1].gain = -10.0
bands[1].filterType = .lowShelf
bands[2].gain = -10.0
bands[2].filterType = .lowShelf
bands[3].gain = 10.0
bands[3].filterType = .highShelf
bands[4].gain = 10.0
bands[4].filterType = .highShelf

do {
    if let filepath = Bundle.main.path(forResource: "song", ofType: "mp3") {
        let filepathURL = NSURL.fileURL(withPath: filepath)
        audioFile = try AVAudioFile(forReading: filepathURL)
        audioEngine.prepare()
        try audioEngine.start()
        audioPlayerNode.scheduleFile(audioFile, at: nil, completionHandler: nil)
        audioPlayerNode.play()
    }
} catch _ {}

Since the low frequencies have a gain of -10 and the high frequencies have a gain of 10, there should be a very noticeable difference when playing any media. However, when the media starts playing, it sounds the same as if played without any equalizer attached.

I'm not sure why this is happening, but I tried several different things to debug. I thought that it might be the order of the functions so I tried switching it so that audioEngine.connect is called after adjusting all of the bands, but that did not make a difference either.

I tried this same code with using an AVAudioUnitTimePitch, and it worked perfectly, so I am dumbfounded as to why it does not work with AVAudioUnitEQ.

I do not want to use any third-party libraries or cocoa pods for this project, I would like to do it using AVFoundation alone.

Any help would be greatly appreciated!

Thanks in advance.

like image 513
Kevin Rajan Avatar asked May 29 '17 08:05

Kevin Rajan


People also ask

What is a simple EQ?

An equalizer, or EQ, is a filter that allows you to adjust the volume level of a frequency, or range of frequencies, within an audio signal. In its simplest form, an EQ will let you turn the treble and bass up or down, allowing you to adjust the coloration of, let's say, your car stereo or your television.

How do you make a custom equalizer for bass?

Bass exists between about 20Hz and 200Hz. If you want to maximize your bass-heavy music, you need to adjust your equalizer within the 20-200Hz range by boosting the decibels (dB) in that range. Of course, that remains a rather wide range, so we have broken it down according to your musical needs...

How do you set up a manual equalizer?

Normally, you'll find whichever EQ point is closest to the frequency you want to boost or reduce, and then simply move it to the exact spot you'd like for the desired effect. Turning the boost or gain knob up or down determines how much you are boosting (or reducing) your chosen frequency in decibels.


1 Answers

AVAudioUnitEQFilterParameters AVAudioUnitEQFilterParameters Documentation Looking through the documentation, I noticed that I had messed with all of the parameters except bypass and it seems that changing this flag fixed everything!

So, I believe the main issue here is that each AVAudioUnitEQ band must not be bypassed by the provided system values rather than the values the programmer sets.

So, I changed

for i in 0...(bands.count - 1) {
    bands[i].frequency = Float(freqs[i])
}

to

for i in 0...(bands.count - 1) {
    bands[i].frequency  = Float(freqs[i])
    bands[i].bypass     = false
    bands[i].filtertype = .parametric
}

and everything started working. Furthermore, to make an effective equalizer that allows the user to modify individual frequencies the filtertype for each band should be set to .parametric.

I am still unsure on what I should set the bandwith to, but I can probably check online for that or just mess with it until the sound matches a different equalizer application.

like image 87
Kevin Rajan Avatar answered Oct 17 '22 12:10

Kevin Rajan