Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Export audio file after adding an effect

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)

like image 579
Eyal Avatar asked Aug 16 '15 14:08

Eyal


1 Answers

Since using AVAudioSession is not an explicit requirement in the title of the question, there are 2 possible answers:

Play through the node (with AVAudioSession)

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.


Use AudioUnit (without AVAudioSession)

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.

like image 133
SwiftArchitect Avatar answered Oct 17 '22 16:10

SwiftArchitect