I have an audio file that I want to process using some effects (like pitch effect) and then write the final result to a file.
Before I process the file and save it to disc I let the user play with the pitch effect and listen to the changes in real time.
this is how I do the real time stuff:
let audioSession = AVAudioSession.sharedInstance()
audioSession.setCategory(AVAudioSessionCategoryPlayback, error: nil)
audioSession.setActive(true, error: nil)
audioEngine = AVAudioEngine()
audioFile = AVAudioFile(forReading: audioUrl!, error: nil)
audioPlayerNode = AVAudioPlayerNode()
audioEngine.attachNode(audioPlayerNode)
changePitchEffect = AVAudioUnitTimePitch()
changePitchEffect.pitch = 1.0 // default
audioEngine.attachNode(changePitchEffect)
audioEngine.connect(audioPlayerNode, to: changePitchEffect, format: nil)
audioEngine.connect(changePitchEffect, to: audioEngine.outputNode, format: nil)
let frameCapacity = UInt32(audioFile.length)
let buffer = AVAudioPCMBuffer(PCMFormat: audioFile.processingFormat, frameCapacity: frameCapacity)
if audioFile.readIntoBuffer(buffer, error: nil) {
audioEngine.startAndReturnError(nil)
audioPlayerNode.scheduleBuffer(buffer, atTime: nil, options: .Loops, completionHandler: nil)
audioPlayerNode.play() // start playing in a loop
}
then using a UISlider
I let the user change the value of the pitch while listening to the audio in a loop.
So when the user finishes playing with the pitch and taps the next button, I need to save the audio file with the chosen pitch value.
My question is, how do I create a new audio file with the pitch effect?
I don't want to record while the user is listening to the audio and play with the pitch effect, I only want to export the final result (without playing the file of course)
Since using AVAudioSession
is not an explicit requirement in the title of the question, there are 2 possible answers:
Install a tap on the node. See AV Foundation Framework Reference.
audioPlayerNode.installTapOnBus(0, bufferSize:frameLength, format: audioInputNode.outputFormatForBus(0), block: {(buffer, time) in
let channels = UnsafeArray(start: buffer.floatChannelData, length: Int(buffer.format.channelCount))
let floats = UnsafeArray(start: channels[0], length: Int(buffer.frameLength))
for var i = 0; i < Int(self.audioBuffer.frameLength); i+=Int(self.audioMixerNode.outputFormatForBus(0).channelCount)
{
// process
}
})
The AVAudioSession
tap mechanism requires play though. More about recording while playing on the Stack Overflow answer by @matt.
Initialize an AudioSession
with AudioSessionInitialize
to play & record, set up a read & write stream, and pass a AURenderCallbackStruct
pointing to your data processing code. This, however, precludes you from capitalizing upon AVAudioUnitTimePitch
. You can find out more about this solution in this How to do realtime recording with effect processing on iOS article.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With