Sanjug Sonowal
Sanjug Sonowal

Reputation: 1

how to save and share the proccessed audio in swift?

I'm trying to modify an iOS app that takes audio via text to speech and applies pitch and rate changes to it, and then allows the user to save and share the processed audio. The recording and processing parts work fine, but I'm having trouble with the saving and sharing part.

Here's the code I'm using to save and share the audio:


    func saveOutputAudio(rate: Float = 1.0, pitch: Float = 0.0, echo: Bool = false, reverb: Bool = false, completionHandler: (() -> Void)? = nil) {
        let audioFileURL = getDocumentsDirector().appendingPathComponent(fileName) as URL
        let audioFile = try! AVAudioFile(forReading: audioFileURL)
        let audioFormat = audioFile.processingFormat
        let audioFrameCount = UInt32(audioFile.length)
        let audioFileBuffer = AVAudioPCMBuffer(pcmFormat: audioFormat, frameCapacity: audioFrameCount)!
        try! audioFile.read(into: audioFileBuffer)

        let audioEngine = AVAudioEngine()
        let audioPlayerNode = AVAudioPlayerNode()
        audioEngine.attach(audioPlayerNode)

        let changeRateEffect = AVAudioUnitTimePitch()
        changeRateEffect.rate = rate
        audioEngine.attach(changeRateEffect)

        let changePitchEffect = AVAudioUnitTimePitch()
        changePitchEffect.pitch = pitch
        audioEngine.attach(changePitchEffect)

        let echoEffect = AVAudioUnitDelay()
        echoEffect.wetDryMix = echo ? 50 : 0
        audioEngine.attach(echoEffect)

        let reverbEffect = AVAudioUnitReverb()
        reverbEffect.wetDryMix = reverb ? 50 : 0
        audioEngine.attach(reverbEffect)

        audioEngine.connect(audioPlayerNode, to: changeRateEffect, format: audioFormat)
        audioEngine.connect(changeRateEffect, to: changePitchEffect, format: audioFormat)
        audioEngine.connect(changePitchEffect, to: echoEffect, format: audioFormat)
        audioEngine.connect(echoEffect, to: reverbEffect, format: audioFormat)
        audioEngine.connect(reverbEffect, to: audioEngine.mainMixerNode, format: audioFormat)

        audioPlayerNode.scheduleBuffer(audioFileBuffer, at: nil, options: .loops, completionHandler: nil)
        try! audioEngine.start()
        audioPlayerNode.play()

        let outputFileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("outputAudio.m4a")
        let outputFile = try! AVAudioFile(forWriting: outputFileURL, settings: audioFormat.settings)
        let outputFileBuffer = AVAudioPCMBuffer(pcmFormat: audioFormat, frameCapacity: audioFrameCount)!

        var counter = 0
        while audioPlayerNode.isPlaying && counter < 2 {
            if let nextRenderTime = audioPlayerNode.lastRenderTime {
                let outputBuffer = AVAudioPCMBuffer(pcmFormat: audioFormat, frameCapacity: audioFrameCount)!
                try! outputFile.write(from: outputBuffer)
                counter += 1
            } else {
                usleep(10_000)
            }
        }

        try! audioEngine.stop()

        // Get the audio data from the output file
        let audioData = try! Data(contentsOf: outputFileURL)

        // Convert audio data to M4A format
        let convertedData = convertToM4A(audioData: audioData)

        // Share the processed audio data
        let activityViewController = UIActivityViewController(activityItems: [convertedData], applicationActivities: nil)
        activityViewController.popoverPresentationController?.sourceView = self.view
        self.present(activityViewController, animated: true) {
            completionHandler?()
        }
    }

Upvotes: 0

Views: 78

Answers (1)

Gene De Lisa
Gene De Lisa

Reputation: 3838

Use the manual rendering mode of the engine to save the processed audio.

try engine.enableManualRenderingMode(.offline,
                                     format: audioFormat,
                                     maximumFrameCount: maxFrames)

The engine has a field for the rendering sample time. Use that to loop up to the length of your buffer, and in that loop, write to your audio file.

engine.manualRenderingSampleTime

Upvotes: 0

Related Questions