Как применить звуковой эффект к файлу и записать в файловую систему - iOS

Я создаю приложение, которое должно позволить пользователю применять звуковые фильтры к записанному аудио, например Reverb, Boost.

Мне не удалось найти какой-либо реальный источник информации о том, как применять фильтры к самому файлу, потому что позже нужно было загрузить обработанный файл на сервер.

В настоящее время я использую AudioKit для визуализации и знаю, что он может обрабатывать аудио, но только для воспроизведения. Пожалуйста, дайте любые предложения для дальнейшего исследования.


person Alexey Savchenko    schedule 21.08.2017    source источник


Ответы (2)


AudioKit имеет автономный узел рендеринга, который не требует iOS 11. Вот пример, требуются биты player.schedule (...) и player.start (at.), Поскольку базовый AVAudioPlayerNode AKAudioPlayer будет блокироваться в вызывающем потоке, ожидая следующий рендер, если вы начнете с 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)
    }
}
person dave234    schedule 22.08.2017
comment
Привет, Дэйв, я пробую AudioKit 4.0, у которого, кажется, есть автономный рендеринг (audiokitpro.com/audiokit-4 -выпущен), но кажется, что AudioKit.mainMixer недоступен, поэтому я попробовал player >>> reverb >>> boost >>> offlineRender >>> **AudioKit.engine.mainMixerNode**, и он не работает. Он генерирует звук такой же длины, но только без звука. Спасибо! - person Juan Giorello; 22.09.2017
comment
К сожалению, AudioKit.mainMixer никогда не объединялся. Отредактирую ответ. - person dave234; 22.09.2017
comment
Я просто попробовал еще раз, и он работает, но что мне делать, чтобы не играть в него? Если я удалю player.play (), файл снова станет пустым. Кроме того, я попробовал player.pause () сразу после play (), и он вылетел. Заранее спасибо! - person Juan Giorello; 22.09.2017
comment
После выполнения рендеринга по URL-адресу и до того, как вы установите для offlineRender.internalRenderEnabled значение true, остановите проигрыватель. Когда вы устанавливаете offlineRender.internalRenderEnabled = true, вы в основном возобновляете нормальный рендеринг. - person dave234; 22.09.2017
comment
Сработало отлично! Спасибо, Дэйв! - person Juan Giorello; 22.09.2017
comment
Этот ответ сейчас неверен с версией AudioKit 4.0.4 - теперь он использует AudioKit.renderToFile и работает только на iOS 11+. - person sacred; 18.12.2017
comment
Вы частично правы. Этот метод по-прежнему необходим для ‹iOS 11. Вы должны предоставить ответ с помощью метода iOS 11+ AudioKit.renderToFile :) - person dave234; 18.12.2017

Вы можете использовать недавно представленные функции «ручного рендеринга» из плагинов Audio Unit (см. Пример ниже).

Если вам нужно поддерживать старую версию macOS / iOS, я буду удивлен, если вы не сможете добиться того же с помощью AudioKit (хотя я сам не пробовал). Например, при использовании AKSamplePlayer в качестве первого узла (который будет читать ваш аудиофайл), затем создание и подключение ваших эффектов и использование AKNodeRecorder в качестве последнего узла.

Пример ручного рендеринга с использованием новых функций аудиоблока

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")

Дополнительные документы и примеры обновлений формата AudioUnit можно найти здесь.

person filaton    schedule 21.08.2017
comment
Однако проблема в том, что вы не можете обработать файл с помощью AudioEngine без воспроизведения его. - person matt; 21.08.2017
comment
Значит, это должно быть новым в iOS 11. И о времени. Спасибо! - person matt; 21.08.2017
comment
@matt Ага, это новое в iOS 11 / macOS High Sierra. Вы можете посмотреть соответствующий WWDC по адресу developer.apple.com/videos/play/wwdc2017/ 501 (часть об автономном ручном рендеринге начинается около 8 минут и заканчивается около 15) - person filaton; 21.08.2017