ios - 在 iOS 10 中使异步调用有效同步的最佳方法

标签 ios asynchronous audio swift3 ios10

为了更好地解释我的情况,我正在尝试制作一个应用程序,该应用程序会在按下按钮时播放 ping 噪音,然后立即继续录制和转录用户的声音。

对于 ping 声音,我使用系统声音服务,为了录制音频,我使用 AudioToolbox,为了转录它,我使用语音套件。

我认为我的问题的症结在于异步系统声音服务播放功能的时间安排:

    //Button pressed function

    let audiosession = AVAudioSession.sharedInstance()

    let filename = "Ping"
    let ext = "wav"

    if let soundUrl = Bundle.main.url(forResource: filename, withExtension: ext){

        var soundId: SystemSoundID = 0
        AudioServicesCreateSystemSoundID(soundUrl as CFURL, &soundId)

        AudioServicesAddSystemSoundCompletion(soundId, nil, nil, {(soundid,_) -> Void in 
        AudioServicesDisposeSystemSoundID(soundid)
        print("Sound played!")}, nil)

        AudioServicesPlaySystemSound(soundId)
    }

    do{
        try audiosession.setCategory(AVAudioSessionCategoryRecord)
        try audiosession.setMode(AVAudioSessionModeMeasurement)
        try audiosession.setActive(true, with: .notifyOthersOnDeactivation)
        print("Changing modes!")

    }catch{
        print("error with audio session")
    }

    recognitionRequest = SFSpeechAudioBufferRecognitionRequest()

    guard let inputNode = audioEngine.inputNode else{
        fatalError("Audio engine has no input node!")
    }

    guard let recognitionRequest = recognitionRequest else{
        fatalError("Unable to create a speech audio buffer recognition request object")
    }

    recognitionRequest.shouldReportPartialResults = true

    recognitionTask = speechRecognizer?.recognitionTask(with: recognitionRequest, delegate: self)

    let recordingFormat = inputNode.outputFormat(forBus: 0)
    inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer, when) in
        self.recognitionRequest?.append(buffer)
    }

    audioEngine.prepare()

    do{
        try audioEngine.start()
        delegate?.didStartRecording()

    }catch{
        print("audioEngine couldn't start because of an error")
    }

当我运行这段代码时会发生什么,它记录了语音并成功转录了它。然而,ping 从未被播放过。我在那里的两个(非错误)打印语句按顺序触发:

  1. 更改模式!
  2. 声音响起!

据我了解,未播放 ping 声音的原因是,当它实际完成时,我已经将 Audio Session 类别从播放更改为录制。为了验证这是真的,我尝试删除除声音服务 ping 之外的所有内容,它按预期播放声音。

所以我的问题是绕过 AudioServicesPlaySystemSound 调用的异步特性的最佳方法是什么?我尝试过尝试将 self 传递到完成函数中,这样我就可以让它触发我的类中的一个函数,然后运行记录 block 。然而,我一直无法弄清楚如何将 self 转换为 UnsafeMutableRawPointer,以便它可以作为 clientData 传递。此外,即使我确实知道该怎么做,我也不确定这是否是一个好主意或该参数的预期用途。

或者,我或许可以依靠通知中心之类的东西来解决这个问题。但再一次,这似乎是一种非常笨拙的解决问题的方法,我以后会后悔的。

有谁知道处理这种情况的正确方法是什么?

更新:

根据 Gruntcake 的要求,这是我在完成 block 中访问 self 的尝试。

首先我创建了一个 userData 常量,它是一个指向 self 的 UnsafeMutableRawPointer:

    var me = self
    let userData = withUnsafePointer(to: &me) { ptr in
        return unsafeBitCast(ptr, to: UnsafeMutableRawPointer.self)

接下来,我在回调 block 中使用该常量,并尝试从中访问 self:

    AudioServicesAddSystemSoundCompletion(soundId, nil, nil, {(sounded,me) -> Void in 
        AudioServicesDisposeSystemSoundID(sounded)
        let myself = Unmanaged<myclassname>.fromOpaque(me!).takeRetainedValue()
        myself.doOtherStuff()
        print("Sound played!")}, userData)

最佳答案

您尝试在完成 block 中调用 doOtherStuff() 是一种正确的方法(唯一的另一种方法是通知,这是仅有的两个选项)

在这种情况下,让事情变得复杂的是从 Obj-C 到 Swift 的桥接是必要的。执行此操作的代码是:

let myData = unsafeBitCast(self, UnsafeMutablePointer<Void>.self)
AudioServicesAddSystemSoundCompletion(YOUR_SOUND_ID, CFRunLoopGetMain(), kCFRunLoopDefaultMode,{ (mSound, mVoid) in
        let me = unsafeBitCast(mVoid, YOURCURRENTCLASS.self)
        //me it is your current object so if yo have a variable like
        // var someVar you can do
        print(me.someVar)
    }, myData)

信用:此代码取自此问题的答案,尽管它不是公认的答案:

How do I implement AudioServicesSystemSoundCompletionProc in Swift?

关于ios - 在 iOS 10 中使异步调用有效同步的最佳方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42120516/

相关文章:

audio - 在特定高度范围内表示波形的好方法是什么

java - 音频播放速度太快

ios - 如何为多点触控实现n个对象;游戏的速度、移动和交互性

ios - Interface Builder 中具有空 UIStackView 的 UIScrollView 的约束错误

c# - 等待多个异步调用完成?

javascript - 以小并发批处理运行 Promise(一次不超过 X)

ios - tableView reloadData cellForRowAtIndexPath 未调用

ios - 从用户名和密码中去除空格

javascript - Nodejs 异步循环正在阻止 React UI 渲染

java - 字节流如何工作?