Comment appliquer un effet audio à un file et écrire dans un système de files – iOS

Je construis une application qui devrait permettre à l'user d'appliquer des filters audio à un son enregistré, comme Reverb, Boost.

J'ai été incapable de find une source d'information viable sur la façon d'appliquer des filters à un file lui-même, car il est nécessaire de download le file traité sur le server plus tard.

J'utilise actuellement AudioKit pour la visualisation, et je suis conscient qu'il est capable de faire du traitement audio, mais seulement pour la lecture. S'il vous plaît donner des suggestions pour d'autres searchs.

AudioKit possède un noeud de rendu hors ligne qui ne nécessite pas iOS 11. Voici un exemple, les bits player.schedule (…) et player.start (at.) Sont requirejs car AVAudioPlayerNode sous-jacent à AKAudioPlayer bloquera sur le thread appelant en attente le rendu suivant si vous le lancez avec player.play() .

 import UIKit import AudioKit class ViewController: UIViewController { var player: AKAudioPlayer? var reverb = AKReverb() var boost = AKBooster() var offlineRender = AKOfflineRenderNode() override func viewDidLoad() { super.viewDidLoad() guard let url = Bundle.main.url(forResource: "theFunkiestFunkingFunk", withExtension: "mp3") else { return } var audioFile: AKAudioFile? do { audioFile = try AKAudioFile.init(forReading: url) player = try AKAudioPlayer.init(file: audioFile!) } catch { print(error) return } guard let player = player else { return } player >>> reverb >>> boost >>> offlineRender AudioKit.output = offlineRender AudioKit.start() let docs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let dstURL = docs.appendingPathComponent("rendered.caf") offlineRender.internalRenderEnabled = false player.schedule(from: 0, to: player.duration, avTime: nil) let sampleTimeZero = AVAudioTime(sampleTime: 0, atRate: AudioKit.format.sampleRate) player.play(at: sampleTimeZero) do { try offlineRender.renderToURL(dstURL, seconds: player.duration) } catch { print(error) return } offlineRender.internalRenderEnabled = true print("Done! Rendered to " + dstURL.path) } } 

Vous pouvez utiliser les nouvelles fonctionnalités de "rendu manuel" à partir des plugins Audio Unit (voir l'exemple ci-dessous).

Si vous avez besoin de supporter une version plus ancienne de macOS / iOS, je serais surpris si vous ne pouvez pas get la même chose avec AudioKit (même si je ne l'ai pas essayé moi-même). Par exemple, utiliser un AKSamplePlayer comme premier noeud (qui lira votre file audio), puis build et connecter vos effets et utiliser un AKNodeRecorder comme dernier noeud.

Exemple de rendu manuel utilisant les nouvelles fonctionnalités de l'unité audio

 import AVFoundation //: ## Source File //: Open the audio file to process let sourceFile: AVAudioFile let format: AVAudioFormat do { let sourceFileURL = Bundle.main.url(forResource: "mixLoop", withExtension: "caf")! sourceFile = try AVAudioFile(forReading: sourceFileURL) format = sourceFile.processingFormat } catch { fatalError("could not open source audio file, \(error)") } //: ## Engine Setup //: player -> reverb -> mainMixer -> output //: ### Create and configure the engine and its nodes let engine = AVAudioEngine() let player = AVAudioPlayerNode() let reverb = AVAudioUnitReverb() engine.attach(player) engine.attach(reverb) // set desired reverb parameters reverb.loadFactoryPreset(.mediumHall) reverb.wetDryMix = 50 // make connections engine.connect(player, to: reverb, format: format) engine.connect(reverb, to: engine.mainMixerNode, format: format) // schedule source file player.scheduleFile(sourceFile, at: nil) //: ### Enable offline manual rendering mode do { let maxNumberOfFrames: AVAudioFrameCount = 4096 // maximum number of frames the engine will be asked to render in any single render call try engine.enableManualRenderingMode(.offline, format: format, maximumFrameCount: maxNumberOfFrames) } catch { fatalError("could not enable manual rendering mode, \(error)") } //: ### Start the engine and player do { try engine.start() player.play() } catch { fatalError("could not start engine, \(error)") } //: ## Offline Render //: ### Create an output buffer and an output file //: Output buffer format must be same as engine's manual rendering output format let outputFile: AVAudioFile do { let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] let outputURL = URL(fileURLWithPath: documentsPath + "/mixLoopProcessed.caf") outputFile = try AVAudioFile(forWriting: outputURL, settings: sourceFile.fileFormat.settings) } catch { fatalError("could not open output audio file, \(error)") } // buffer to which the engine will render the processed data let buffer: AVAudioPCMBuffer = AVAudioPCMBuffer(pcmFormat: engine.manualRenderingFormat, frameCapacity: engine.manualRenderingMaximumFrameCount)! //: ### Render loop //: Pull the engine for desired number of frames, write the output to the destination file while engine.manualRenderingSampleTime < sourceFile.length { do { let framesToRender = min(buffer.frameCapacity, AVAudioFrameCount(sourceFile.length - engine.manualRenderingSampleTime)) let status = try engine.renderOffline(framesToRender, to: buffer) switch status { case .success: // data rendered successfully try outputFile.write(from: buffer) case .insufficientDataFromInputNode: // applicable only if using the input node as one of the sources break case .cannotDoInCurrentContext: // engine could not render in the current render call, retry in next iteration break case .error: // error occurred while rendering fatalError("render failed") } } catch { fatalError("render failed, \(error)") } } player.stop() engine.stop() print("Output \(outputFile.url)") print("AVAudioEngine offline rendering completed") 

Vous pouvez find plus de documents et d'exemples sur les mises à jour du format AudioUnit.