How to create a pitch-shifting (whammy) effect?

edited December 2014

I'm trying to build a pitch-shifting filter, much like the whammy pedal effect in use here:
https://www.youtube.com/watch?v=yxahhLz8tNI

You can see Audioslave using this effect here:
http://youtu.be/7QU1nvuxaMA?t=2m57s

From the look of it, AVAudioEngine (iOS 8's native audio engine) doesn't support pitch-shifting live audio - so that's why I'm using AEAudioUnitFilter.

It seems like the trick is getting the pitch to happen without speeding up the rate of play - so I dug around in these forums, and found this discussion: http://forum.theamazingaudioengine.com/discussion/comment/1602/#Comment_1602

Here's my code:

let pitchFilter = AEAudioUnitFilter(componentDescription: AEAudioComponentDescriptionMake(
        OSType(kAudioUnitManufacturer_Apple),
        OSType(kAudioUnitType_FormatConverter),
        OSType(kAudioUnitSubType_NewTimePitch)),
        audioController: self.audioController, error:nil)

let pitchOffset = Float(1200) // 1200 is an octave

// An octave doubles the frequency, so we should halve the time, no?
var status = AudioUnitSetParameter(pitch.audioUnit, OSType(kNewTimePitchParam_Rate), OSType(kAudioUnitScope_Global), 0, 1/2, 0)

if status == noErr {    
    status = AudioUnitSetParameter(pitch.audioUnit, OSType(kNewTimePitchParam_Pitch), OSType(kAudioUnitScope_Global), 0, pitchOffset, 0)
     if status == noErr {
            self.audioController.addFilter(pitch)
        }   
}

Unfortunately, this results in a sound much more like a Ring Modulator:
https://www.youtube.com/watch?v=Vl_USNctWXk

What am I doing wrong here? Is there a way to do this?

Comments

  • @bryanjclark - I tested this out a bit ago in swift here: http://forum.theamazingaudioengine.com/discussion/comment/1602/#Comment_1602 -- code similar to that has worked surprisingly good for me for a set rate/pitch (not changing over time) while playing back a file using the AUAudioFilePlayer unit.

    Today I'm working on "modulating" audio unit parameters over time. So far I'm getting the best results from using an AUSampler audio unit and changing the pitch via something like:

    AudioUnitSetParameter(THIS->_audioUnit, kAUGroupParameterID_PitchBend, kAudioUnitScope_Group, 0, newValue, 0)
    

    ...where newValue is a value between 0 to 127.

    The way I initialized the AUSampler was by using a .aupreset file that I created in AU Labs with the pitch bend range set to the full +/- 2 octaves (though you can probably do this in code too). I have a file on github here: https://github.com/jcypher/TheAmazingAudioEngine/blob/VEAEforGames/TheGameSample/VEAEforGames/VEAEforGames.m -- where you can see how I loaded the AUSampler (see the audGetAUSamplerChannel(...) function).

    If you do use an AUSampler, then you can send MIDI events to do the pitch bend. The AUSampler however doesn't seem to let you change the rate, so a higher pitch speeds up the audio.

    I did try using an filter to change the pitch using the NewTimePitch - and that worked surprisingly well - but I didn't try changing it realtime using user input. I did try changing it using a modified "block timer" class based on TPPreciseTimer.h/m from atastypixel.com - that worked ok, the "bending" being slightly choppy - good enough for games, maybe, but not as a musical instrument, I think (unless you want a slightly "stepped" pitch bend).

  • I just pushed what I have (a working demo) to:

    https://github.com/jcypher/TheAmazingAudioEngine/tree/VEAEforGames

    ... if you open the TheGameSample.xcodeproj project and run it, then tap (iOS) or click (Mac) the "Play 2 stereo panning sound effects!" text, then it will play a sound sample on which I am changing volume, pitch and panning all at the same time using the AEGameSoundChannel class I created for the purpose. It uses an AUSampler initialized via a .aupreset file but assigned which audio file to use at runtime.

    This is mostly all proof-of-concept so the code is rough, but I'd love to get any feedback on it. At any length, it relates to your pitch-bend question, so I posted it here in case might work well enough for your purpose. I have yet to find out if calling AudioUnitSetParameter(...) on the renderCallback(...) or timingReceiver(...) is OK to do - I know that some people do it, but I'm not sure it's a recommended practice. I think it should be ok in the timingReceiver(...) as I currently have it.

Sign In or Register to comment.