How to record and play audio simultaneously in iOS using Swift?

醉酒当歌 提交于 2019-12-21 04:00:44

问题


In Objective-C, recording and playing audio simultaneously is fairly simple. And there are tonnes of sample code on the internet. But I want to record and play audio simultaneously using Audio Unit/Core Audio in Swift. There are vary small amount of help and sample code on this using Swift. And i couldn't find any help which could show how to achieve this.

I am struggling with the bellow code.

let preferredIOBufferDuration = 0.005 
let kInputBus  = AudioUnitElement(1)
let kOutputBus = AudioUnitElement(0)

init() {
    // This is my Audio Unit settings code.
    var status: OSStatus

    do {
        try AVAudioSession.sharedInstance().setPreferredIOBufferDuration(preferredIOBufferDuration)
    } catch let error as NSError {
        print(error)
    }


    var desc: AudioComponentDescription = AudioComponentDescription()
    desc.componentType = kAudioUnitType_Output
    desc.componentSubType = kAudioUnitSubType_VoiceProcessingIO
    desc.componentFlags = 0
    desc.componentFlagsMask = 0
    desc.componentManufacturer = kAudioUnitManufacturer_Apple

    let inputComponent: AudioComponent = AudioComponentFindNext(nil, &desc)

    status = AudioComponentInstanceNew(inputComponent, &audioUnit)
    checkStatus(status)

    var flag = UInt32(1)
    status = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, UInt32(sizeof(UInt32)))
    checkStatus(status)

    status = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, UInt32(sizeof(UInt32)))
    checkStatus(status)

    var audioFormat: AudioStreamBasicDescription! = AudioStreamBasicDescription()
    audioFormat.mSampleRate = 8000
    audioFormat.mFormatID = kAudioFormatLinearPCM
    audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked
    audioFormat.mFramesPerPacket = 1
    audioFormat.mChannelsPerFrame = 1
    audioFormat.mBitsPerChannel = 16
    audioFormat.mBytesPerPacket = 2
    audioFormat.mBytesPerFrame = 2

    status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &audioFormat, UInt32(sizeof(UInt32)))
    checkStatus(status)


    try! AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord)
    status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &audioFormat, UInt32(sizeof(UInt32)))
    checkStatus(status)


    // Set input/recording callback
    var inputCallbackStruct: AURenderCallbackStruct! = AURenderCallbackStruct(inputProc: recordingCallback, inputProcRefCon: UnsafeMutablePointer(unsafeAddressOf(self)))
    inputCallbackStruct.inputProc = recordingCallback
    inputCallbackStruct.inputProcRefCon = UnsafeMutablePointer(unsafeAddressOf(self))
    status = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, kInputBus, &inputCallbackStruct, UInt32(sizeof(UInt32)))
    checkStatus(status)


    // Set output/renderar/playback callback
    var renderCallbackStruct: AURenderCallbackStruct! = AURenderCallbackStruct(inputProc: playbackCallback, inputProcRefCon: UnsafeMutablePointer(unsafeAddressOf(self)))
    renderCallbackStruct.inputProc = playbackCallback
    renderCallbackStruct.inputProcRefCon = UnsafeMutablePointer(unsafeAddressOf(self))
    status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, kOutputBus, &renderCallbackStruct, UInt32(sizeof(UInt32)))
    checkStatus(status)


    flag = 0
    status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_ShouldAllocateBuffer, kAudioUnitScope_Output, kInputBus, &flag, UInt32(sizeof(UInt32)))
}


func recordingCallback(inRefCon: UnsafeMutablePointer<Void>,
                      ioActionFlags: UnsafeMutablePointer<AudioUnitRenderActionFlags>,
                      inTimeStamp: UnsafePointer<AudioTimeStamp>,
                      inBufNumber: UInt32,
                      inNumberFrames: UInt32,
                      ioData: UnsafeMutablePointer<AudioBufferList>) -> OSStatus {

    print("recordingCallback got fired  >>>")


    return noErr
}



func playbackCallback(inRefCon: UnsafeMutablePointer<Void>,
                      ioActionFlags: UnsafeMutablePointer<AudioUnitRenderActionFlags>,
                      inTimeStamp: UnsafePointer<AudioTimeStamp>,
                      inBufNumber: UInt32,
                      inNumberFrames: UInt32,
                      ioData: UnsafeMutablePointer<AudioBufferList>) -> OSStatus {

    print("playbackCallback got fired  <<<")


    return noErr
}

With that code, only recordingCallback method is getting called. And playbackCallback method is not being fired at all. I sure that i am going something wrong here. Can someone please help me with this. I am banging my head over this problem.


回答1:


You are setting the InputCallback and RenderCallback method incorrectly. Other settings seems OK. So your init method should be like this.

init() {

    var status: OSStatus

    do {
        try AVAudioSession.sharedInstance().setPreferredIOBufferDuration(preferredIOBufferDuration)
    } catch let error as NSError {
        print(error)
    }


    var desc: AudioComponentDescription = AudioComponentDescription()
    desc.componentType = kAudioUnitType_Output
    desc.componentSubType = kAudioUnitSubType_VoiceProcessingIO
    desc.componentFlags = 0
    desc.componentFlagsMask = 0
    desc.componentManufacturer = kAudioUnitManufacturer_Apple

    let inputComponent: AudioComponent = AudioComponentFindNext(nil, &desc)

    status = AudioComponentInstanceNew(inputComponent, &audioUnit)
    checkStatus(status)

    var flag = UInt32(1)
    status = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, UInt32(sizeof(UInt32)))
    checkStatus(status)

    status = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, UInt32(sizeof(UInt32)))
    checkStatus(status)

    var audioFormat: AudioStreamBasicDescription! = AudioStreamBasicDescription()
    audioFormat.mSampleRate = 8000
    audioFormat.mFormatID = kAudioFormatLinearPCM
    audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked
    audioFormat.mFramesPerPacket = 1
    audioFormat.mChannelsPerFrame = 1
    audioFormat.mBitsPerChannel = 16
    audioFormat.mBytesPerPacket = 2
    audioFormat.mBytesPerFrame = 2

    status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &audioFormat, UInt32(sizeof(UInt32)))
    checkStatus(status)


    try! AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord)
    status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &audioFormat, UInt32(sizeof(UInt32)))
    checkStatus(status)


    // Set input/recording callback
    var inputCallbackStruct = AURenderCallbackStruct(inputProc: recordingCallback, inputProcRefCon: UnsafeMutablePointer(unsafeAddressOf(self)))
    AudioUnitSetProperty(audioUnit, AudioUnitPropertyID(kAudioOutputUnitProperty_SetInputCallback), AudioUnitScope(kAudioUnitScope_Global), 1, &inputCallbackStruct, UInt32(sizeof(AURenderCallbackStruct)))


    // Set output/renderar/playback callback
    var renderCallbackStruct = AURenderCallbackStruct(inputProc: playbackCallback, inputProcRefCon: UnsafeMutablePointer(unsafeAddressOf(self)))
    AudioUnitSetProperty(audioUnit, AudioUnitPropertyID(kAudioUnitProperty_SetRenderCallback), AudioUnitScope(kAudioUnitScope_Global), 0, &renderCallbackStruct, UInt32(sizeof(AURenderCallbackStruct)))


    flag = 0
    status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_ShouldAllocateBuffer, kAudioUnitScope_Output, kInputBus, &flag, UInt32(sizeof(UInt32)))
}

Try with this code and let us know if that helps.



来源:https://stackoverflow.com/questions/36968813/how-to-record-and-play-audio-simultaneously-in-ios-using-swift

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!