【问题标题】:AudioKit 4.3: record audio, render it offline, then play itAudioKit 4.3:录制音频,离线渲染,然后播放
【发布时间】:2018-11-20 04:15:03
【问题描述】:

我正在尝试录制音频,然后使用AudioKit.renderToFile 离线保存,然后使用AKPlayer 播放原始录制的音频文件。

import UIKit
import AudioKit


class ViewController: UIViewController {

private var recordUrl:URL!
private var isRecording:Bool = false

public var player:AKPlayer!
private let format = AVAudioFormat(commonFormat: .pcmFormatFloat64, sampleRate: 44100, channels: 2, interleaved: true)!

private var amplitudeTracker:AKAmplitudeTracker!
private var boostedMic:AKBooster!
private var mic:AKMicrophone!
private var micMixer:AKMixer!
private var silence:AKBooster!
public var recorder: AKNodeRecorder!

@IBOutlet weak var recordButton: UIButton!

override func viewDidLoad() {
    super.viewDidLoad()
    //self.recordUrl = Bundle.main.url(forResource: "sound", withExtension: "caf")
    //self.startAudioPlayback(url: self.recordUrl!)
    self.recordUrl = self.urlForDocument("record.caf")
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

func requestMic(completion: @escaping () -> Void) {
    AVAudioSession.sharedInstance().requestRecordPermission({ (granted: Bool) in
        
        if granted { completion()}
    })
}
public func switchToMicrophone() {
    stopEngine()
    do {
        try AKSettings.setSession(category: .playAndRecord, with: .allowBluetoothA2DP)
    } catch {
        AKLog("Could not set session category.")
    }
    mic = AKMicrophone()
    micMixer = AKMixer(mic)
    boostedMic = AKBooster(micMixer, gain: 5)
    amplitudeTracker = AKAmplitudeTracker(boostedMic)
    silence = AKBooster(amplitudeTracker, gain: 0)
    AudioKit.output = silence
    startEngine()
}

@IBAction func startStopRecording(_ sender: Any) {
    self.isRecording = !self.isRecording
    
    if self.isRecording {
        self.startRecording()
        self.recordButton.setTitle("Stop Recording", for: .normal)
    } else {
        self.stopRecording()
        self.recordButton.setTitle("Start Recording", for: .normal)
    }
}

func startRecording() {
    self.requestMic() {
        self.switchToMicrophone()
        if let url = self.recordUrl {
            do {
            let audioFile = try AKAudioFile(forWriting: url, settings: self.format.settings, commonFormat: .pcmFormatFloat64, interleaved: true)

            self.recorder = try AKNodeRecorder(node: self.micMixer, file: audioFile)

            try self.recorder.reset()
            try self.recorder.record()
            } catch {
                print("error setting up recording", error)
            }
        }
    }
}

func stopRecording() {
    recorder.stop()
    startAudioPlayback(url: self.recordUrl)
}

@IBAction func saveToDisk(_ sender: Any) {
    if let source = self.player, let saveUrl = self.urlForDocument("pitchAudio.caf") {
        do {
            source.stop()
            
            let audioFile = try AKAudioFile(forWriting: saveUrl, settings: self.format.settings, commonFormat: .pcmFormatFloat64, interleaved: true)
            try AudioKit.renderToFile(audioFile, duration: source.duration, prerender: {
                source.play()
            })
            print("audio file rendered")
            
        } catch {
            print("error rendering", error)
        }
        
        // PROBLEM STARTS HERE //
        
        self.startAudioPlayback(url: self.recordUrl)
        
    }
}

public func startAudioPlayback(url:URL) {
    print("loading playback audio", url)
    self.stopEngine()
    
    do {
        try AKSettings.setSession(category: .playback)
        player = AKPlayer.init()
        try player.load(url: url)
    }
    catch {
        print("error setting up audio playback", error)
        return
    }
    
    player.prepare()
    player.isLooping = true
    self.setPitch(pitch: self.getPitch(), saveValue: false)
    AudioKit.output = player
    
    startEngine()
    startPlayer()
}


public func startPlayer() {
    if AudioKit.engine.isRunning { self.player.play() }
    else { print("audio engine not running, can't play") }
}

public func startEngine() {
    if !AudioKit.engine.isRunning {
        print("starting engine")
        do { try AudioKit.start() }
        catch {
            print("error starting audio", error)
        }
    }
}

public func stopEngine(){
    
    if AudioKit.engine.isRunning {
        print("stopping engine")
        do {
            try AudioKit.stop()
        }
        catch {
            print("error stopping audio", error)
        }
    }
    
    //playback doesn't work without this?
    mic = nil
}

@IBAction func changePitch(_ sender: UISlider) {
    self.setPitch(pitch:Double(sender.value))
}

public func getPitch() -> Double {
    return UserDefaults.standard.double(forKey: "pitchFactor")
}

public func setPitch(pitch:Double, saveValue:Bool = true) {
    player.pitch = pitch * 1000.0
    if saveValue {
        UserDefaults.standard.set(pitch, forKey: "pitchFactor")
        UserDefaults.standard.synchronize()
    }
}

func urlForDocument(_ named:String) -> URL? {
    let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
    let url = NSURL(fileURLWithPath: path)
    if let pathComponent = url.appendingPathComponent(named) {
        return pathComponent
    }
    return nil
}

}

调用顺序为switchToMicrophonestartRecordingstopRecordingstartAudioPlaybacksaveToDisk,再次为startAudioPlayback

请查看github repo以获取ViewController.swift中的完整代码

renderToFile函数后,为播放器重启AudioKit时,出现如下错误:


[mcmx] 338: 输入总线 0 的采样率为 0

[avae] AVAEInternal.h:103:_AVAE_CheckNoErr: [AVAudioEngineGraph.mm:1265:Initialize: (err = AUGraphParser::InitializeActiveNodesInOutputChain(ThisGraph, kOutputChainOptimizedTraversal, *GetOutputNode(), isOutputChainActive)): 错误 -10875

[avae] AVAudioEngine.mm:149:-[AVAudioEngine prepare]: Engine@0x1c4008ae0: 无法初始化,错误 = -10875

[mcmx] 338: 输入总线 0 的采样率为 0

[avae] AVAEInternal.h:103:_AVAE_CheckNoErr: [AVAudioEngineGraph.mm:1265:Initialize: (err = AUGraphParser::InitializeActiveNodesInOutputChain(ThisGraph, kOutputChainOptimizedTraversal, *GetOutputNode(), isOutputChainActive)): 错误 -10875

错误启动音频 Error Domain=com.apple.coreaudio.avfaudio Code=-10875 "(null)" UserInfo={failed call=err = AUGraphParser::InitializeActiveNodesInOutputChain(ThisGraph, kOutputChainOptimizedTraversal, *GetOutputNode(), isOutputChainActive)} ***

如果我取出录音片段或离线渲染,这一切都完美无缺,但不包括两者。

【问题讨论】:

  • "请查看 github repo" 如果您在问题中包含相关代码,我怀疑您会得到更好的帮助。
  • 感谢 matt -- 不确定一堵代码墙是否会分散注意力,但我现在将其添加

标签: ios swift audiokit


【解决方案1】:

我能够通过将录制和回放合并到一个管道中来使其工作:

mixer = AKMixer(mic)
boostedMic = AKBooster(mixer, gain: 5)
amplitudeTracker = AKAmplitudeTracker(boostedMic)
micBooster = AKBooster(amplitudeTracker, gain: 0)

player = AKPlayer()
try? player.load(url: self.recordUrl)
player.prepare()
player.gain = 2.0

outputMixer = AKMixer(micBooster, player)
AudioKit.output = outputMixer

【讨论】:

  • 太棒了,你已经弄明白了。
【解决方案2】:

可能是您的执行顺序问题尝试交换 startAudioPlayback、saveToDisk,以便它首先执行 saveToDisk,然后读回文件并播放它,startAudioPlayback。

编辑:到目前为止,我相信我已经确定了这个问题。保存文件后,另一个记录的临时文件由于某种原因消失了。我认为这需要缩小为什么会这样。

或者也许是在不中断当前播放文件的情况下播放并将整个 saveToDisk 方法发送到后台线程。

在我的空闲时间我会尝试更多地调整它并让你知道。

编辑 2: 检查这个https://stackoverflow.com/a/48133092/9497657 如果您无处可去,请尝试在此处发布您的问题: https://github.com/audiokit/AudioKit/issues/

还可以查看本教程: https://www.raywenderlich.com/145770/audiokit-tutorial-getting-started

向 Aurelius Prochazka 发送消息可能会很有用,因为他是 AudioKit 的开发者,可以提供帮助。

【讨论】:

  • 问题是我需要在保存到磁盘之前和之后播放音频。 renderToFile function 停止音频引擎,因此必须重新启动它。我怀疑问题出在某个地方。
  • 如何解决这个问题。首先保存文件然后播放它,如果由于某种原因它会被拒绝,那么只需执行删除功能。根据我的经验,如果你保存一些东西比临时保存在内存中更容易操作。
  • 我只是在我的手机上运行你的应用程序,我会尝试定位问题,但在不需要的地方有双重呼叫
  • 谢谢...我可以使用任何其他解决方案来保存文件,但请注意在保存之前可以更改声音的音高,从而让用户听到它的内容听起来像在他们保存之前
  • 我注意到该文件在不使用 AudioKit.renderToFile 方法的情况下无论如何都会被保存。现在我的问题是 saveToFile 方法应该保存新投的文件?然后再回到之前播放的文件?问题似乎与音频引擎的取消初始化有关
猜你喜欢
  • 2018-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-21
  • 1970-01-01
  • 2023-04-03
  • 1970-01-01
  • 2023-04-01
相关资源
最近更新 更多