在iOS 10中实现异步调用有效同步的最佳方法

在iOS 10中实现异步调用有效同步的最佳方法,ios,asynchronous,audio,swift3,ios10,Ios,Asynchronous,Audio,Swift3,Ios10,为了更好地解释我的情况,我正在尝试制作一个应用程序,当按下一个按钮时,它会发出ping的声音,然后在按下按钮后立即录制和转录用户的声音 对于ping声音,我使用系统声音服务,录制音频,我使用AudioToolbox,转录它,我使用语音工具包 我认为问题的关键在于异步系统声音服务发挥功能的时间安排: //Button pressed function let audiosession = AVAudioSession.sharedInstance() let filen

为了更好地解释我的情况,我正在尝试制作一个应用程序,当按下一个按钮时,它会发出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声音没有被播放的原因是,当它实际完成时,我已经将音频会话类别从播放改为录制。为了验证这是真的,我试着删除了所有的东西,但声音服务ping和它播放的声音正如预期的那样

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

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

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

    更新:

    根据Gruntcake的请求,下面是我在完成块中访问self的尝试

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

        var me = self
        let userData = withUnsafePointer(to: &me) { ptr in
            return unsafeBitCast(ptr, to: UnsafeMutableRawPointer.self)
    
    接下来,我在回调块中使用该常量,并尝试从中访问self:

        AudioServicesAddSystemSoundCompletion(soundId, nil, nil, {(sounded,me) -> Void in 
            AudioServicesDisposeSystemSoundID(sounded)
            let myself = Unmanaged<myclassname>.fromOpaque(me!).takeRetainedValue()
            myself.doOtherStuff()
            print("Sound played!")}, userData)
    
    AudioServicesAddSystemSoundCompletion(soundId,nil,nil,{(sounded,me)->中的Void
    AudioServicesDisposeSystemSoundID(已发声)
    让我自己=Unmanaged.from不透明(我!).takeRetainedValue()
    我自己
    打印(“播放声音!”)},用户数据)
    
    尝试在完成块中调用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)
    
    let myData=unsafeBitCast(self,UnsafeMutablePointer.self)
    AudioServicesAddSystemSoundCompletion(您的声音ID,CFRunLoopGetMain(),kCFRunLoopDefaultMode,{(mSound,mVoid)在
    let me=unsafeBitCast(mVoid,YOURCURRENTCLASS.self)
    //如果你有一个变量,比如
    //你能做什么
    打印(me.someVar)
    },myData)
    
    信用:该代码取自对该问题的回答,尽管它不是公认的答案:


    我希望它能这么简单。。。然而,我遇到的问题是,我不能简单地在闭包中引用self。具体地说,如果我把self.doOtherStuff()放在那里,我会得到以下编译器错误:“不能从捕获上下文的闭包中形成C函数指针”。AudioServicesAddSystemSoundCompletion方法的最终参数称为“inClientData”,它在文档中有用地表示“调用回调函数时要传递给回调函数的应用程序数据”。这种类型是UnsafemeutableRawPointer,这就是为什么我最初会做整个userData的事情您尝试在完成块中调用doOtherStuff()是一种正确的方法(唯一的另一种是通知,这是唯一的两个选项)。在这种情况下,使其复杂化的是从Obj-C到Swift的桥接,这是必要的。最后一个(不被接受的)答案对你有用吗@绿豆,谢谢!这就成功了。事实上,我在早些时候试图弄明白这一点的时候,偶然发现了同样的问题。但我甚至没有看那个答案,因为低评级和不可接受的状态。。。如果你想发表你的评论作为对我问题的回答,我会接受的。