Saving Audio After Effect in iOS
Here it is my solution to question :
func playAndRecord(pitch : Float, rate: Float, reverb: Float, echo: Float) { // Initialize variables// These are global variables . if you want you can just (let audioEngine = etc ..) init here these variables audioEngine = AVAudioEngine() audioPlayerNode = AVAudioPlayerNode() audioEngine.attachNode(audioPlayerNode) playerB = AVAudioPlayerNode() audioEngine.attachNode(playerB) // Setting the pitch let pitchEffect = AVAudioUnitTimePitch() pitchEffect.pitch = pitch audioEngine.attachNode(pitchEffect) // Setting the platback-rate let playbackRateEffect = AVAudioUnitVarispeed() playbackRateEffect.rate = rate audioEngine.attachNode(playbackRateEffect) // Setting the reverb effect let reverbEffect = AVAudioUnitReverb() reverbEffect.loadFactoryPreset(AVAudioUnitReverbPreset.Cathedral) reverbEffect.wetDryMix = reverb audioEngine.attachNode(reverbEffect) // Setting the echo effect on a specific interval let echoEffect = AVAudioUnitDelay() echoEffect.delayTime = NSTimeInterval(echo) audioEngine.attachNode(echoEffect) // Chain all these up, ending with the output audioEngine.connect(audioPlayerNode, to: playbackRateEffect, format: nil) audioEngine.connect(playbackRateEffect, to: pitchEffect, format: nil) audioEngine.connect(pitchEffect, to: reverbEffect, format: nil) audioEngine.connect(reverbEffect, to: echoEffect, format: nil) audioEngine.connect(echoEffect, to: audioEngine.mainMixerNode, format: nil) // Good practice to stop before starting audioPlayerNode.stop() // Play the audio file // this player is also a global variable AvAudioPlayer if(player != nil){ player?.stop() } // audioFile here is our original audio audioPlayerNode.scheduleFile(audioFile, atTime: nil, completionHandler: { print("Complete") }) try! audioEngine.start() let dirPaths: AnyObject = NSSearchPathForDirectoriesInDomains( NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0] let tmpFileUrl: NSURL = NSURL.fileURLWithPath(dirPaths.stringByAppendingPathComponent("effectedSound2.m4a"))//Save the tmpFileUrl into global varibale to not lose it (not important if you want to do something else)filteredOutputURL = tmpFileUrl do{ print(dirPaths) self.newAudio = try! AVAudioFile(forWriting: tmpFileUrl, settings: [ AVFormatIDKey: NSNumber(unsignedInt:kAudioFormatAppleLossless), AVEncoderAudioQualityKey : AVAudioQuality.Low.rawValue, AVEncoderBitRateKey : 320000, AVNumberOfChannelsKey: 2, AVSampleRateKey : 44100.0 ]) let length = self.audioFile.length audioEngine.mainMixerNode.installTapOnBus(0, bufferSize: 1024, format: self.audioEngine.mainMixerNode.inputFormatForBus(0)) { (buffer: AVAudioPCMBuffer!, time: AVAudioTime!) -> Void in print(self.newAudio.length) print("=====================") print(length) print("**************************") if (self.newAudio.length) < length {//Let us know when to stop saving the file, otherwise saving infinitely do{ //print(buffer) try self.newAudio.writeFromBuffer(buffer) }catch _{ print("Problem Writing Buffer") } }else{ self.audioEngine.mainMixerNode.removeTapOnBus(0)//if we dont remove it, will keep on tapping infinitely //DO WHAT YOU WANT TO DO HERE WITH EFFECTED AUDIO } } }catch _{ print("Problem") } audioPlayerNode.play()}
This doesn't seem to be hooked up correctly. I'm just learning all this myself, but I found that the effects are correctly added when you connect them to a mixer node. Also, you'll want to tap the mixer, not the engine output node. I've just copied your code and made a few modifications to take this into account.
private func playAudio(pitch : Float, rate: Float, reverb: Float, echo: Float) { // Initialize variables audioEngine = AVAudioEngine() audioPlayerNode = AVAudioPlayerNode() audioEngine.attachNode(audioPlayerNode) // Setting the pitch let pitchEffect = AVAudioUnitTimePitch() pitchEffect.pitch = pitch audioEngine.attachNode(pitchEffect) // Setting the playback-rate let playbackRateEffect = AVAudioUnitVarispeed() playbackRateEffect.rate = rate audioEngine.attachNode(playbackRateEffect) // Setting the reverb effect let reverbEffect = AVAudioUnitReverb() reverbEffect.loadFactoryPreset(AVAudioUnitReverbPreset.Cathedral) reverbEffect.wetDryMix = reverb audioEngine.attachNode(reverbEffect) // Setting the echo effect on a specific interval let echoEffect = AVAudioUnitDelay() echoEffect.delayTime = NSTimeInterval(echo) audioEngine.attachNode(echoEffect) // Set up a mixer node let audioMixer = AVAudioMixerNode() audioEngine.attachNode(audioMixer) // Chain all these up, ending with the output audioEngine.connect(audioPlayerNode, to: playbackRateEffect, format: nil) audioEngine.connect(playbackRateEffect, to: pitchEffect, format: nil) audioEngine.connect(pitchEffect, to: reverbEffect, format: nil) audioEngine.connect(reverbEffect, to: echoEffect, format: nil) audioEngine.connect(echoEffect, to: audioMixer, format: nil) audioEngine.connect(audioMixer, to: audioEngine.outputNode, format: nil) audioPlayerNode.stop() let length = 4000 let buffer = AVAudioPCMBuffer(PCMFormat: audioPlayerNode.outputFormatForBus(0),frameCapacity:AVAudioFrameCount(length)) buffer.frameLength = AVAudioFrameCount(length) try! audioEngine.start() let dirPaths: AnyObject = NSSearchPathForDirectoriesInDomains( NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0] let tmpFileUrl: NSURL = NSURL.fileURLWithPath(dirPaths.stringByAppendingPathComponent("effectedSound.m4a")) do{ print(dirPaths) let settings = [AVFormatIDKey: NSNumber(unsignedInt: kAudioFormatMPEG4AAC), AVSampleRateKey: NSNumber(integer: 44100), AVNumberOfChannelsKey: NSNumber(integer: 2)] self.newAudio = try AVAudioFile(forWriting: tmpFileUrl, settings: settings) audioMixer.installTapOnBus(0, bufferSize: (AVAudioFrameCount(self.player!.duration)), format: self.audioMixer.outputFormatForBus(0)){ (buffer: AVAudioPCMBuffer!, time: AVAudioTime!) in print(self.newAudio.length) print("=====================") print(self.audioFile.length) print("**************************") if (self.newAudio.length) < (self.audioFile.length){ do{ //print(buffer) try self.newAudio.writeFromBuffer(buffer) }catch _{ print("Problem Writing Buffer") } }else{ self.audioMixer.removeTapOnBus(0) } } }catch _{ print("Problem") } audioPlayerNode.play()}
I also had trouble getting the file formatted properly. I finally got it working when I changed my path of the output file from m4a
to caf
. One other suggestion is to not have nil
for the format
parameter. I used the audioFile.processingFormat
. I hope this helps. My audio effects/mixing is functional, although I did not chain my effects. So feel free to ask questions.
just change the parameter unsigned int from kAudioFormatMPEG4AAC to kAudioFormatLinearPCM and also change file type to .caf it will sure helpfull my friend