AudioFileWriteBytes(AudioToolbox)失败,错误代码为-38 kAudioFileNotOpenError

der*_*iuk 10 audio cocoa-touch audiotoolbox ios swift

我正在研究记录用户音频的应用程序,使用数据写入文件AudioToolbox并处理这些数据.在分析中,我看到AudioToolbox录制中发生了很多错误.其中几乎一半是-38 kAudioFileNotOpenErrorAudioFileWriteBytes()通话中返回的错误.

现在似乎没有任何理由发生这种情况:用户开始录制,一切顺利,然后连续10-80次记录错误.在分析中,没有用户操作,例如转到后台或暂停录制(我们有此功能).

我在线研究了信息,但没有找到有关错误的更多信息.

我发布了所有录制代码(带有剥离的未使用或分析代码),因为除了-38 kAudioFileNotOpenError我有一堆其他错误,这可能意味着我可能以AudioToolbox错误的方式使用.以下是最重要的错误:

  1. AudioFileWriteBytes()返回-38 kAudioFileNotOpenError错误:( [1]代码中) - 约50%的错误
  2. AudioUnitRender()返回-10863 kAudioUnitErr_CannotDoInCurrentContext错误([2]代码中) - ~5%
  3. AudioFileWriteBytes()返回1868981823 kAudioFileDoesNotAllow64BitDataSizeError错误:( [1]代码中) - ~4%
  4. AudioUnitRender()返回-1错误([2]代码中) - ~3%

任何帮助,评论或建议将非常有帮助!

这是代码(它也可以在GitHub上找到:https://github.com/derpoliuk/SO-AudioToolbox-error-quesion):

class Recorder {

    static let shared = Recorder()
    private static let sampleRate: Float64 = 16000

    var processAudioData: ((Data) -> ())?

    fileprivate var remoteIOUnit: AudioComponentInstance?
    private var audioFile: AudioFileID?
    private var startingByte: Int64 = 0
    // Audio recording settings
    private let formatId: AudioFormatID = kAudioFormatLinearPCM
    private let bitsPerChannel: UInt32 = 16
    private let channelsPerFrame: UInt32 = 1
    private let bytesPerFrame: UInt32 = 2 // channelsPerFrame * 2
    private let framesPerPacket: UInt32 = 1
    private let encoderBitRate = 12800
    private let formatFlags: AudioFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked

    func record(atURL url: URL) {
        var status = openFileForWriting(fileURL: url)
        startingByte = 0
        status = prepareAudioToolbox()
        status = startAudioToolboxRecording()
    }

    func openFileForWriting(fileURL: URL) -> OSStatus {
        var asbd = AudioStreamBasicDescription()
        memset(&asbd, 0, MemoryLayout<AudioStreamBasicDescription>.size)
        asbd.mSampleRate = Recorder.sampleRate
        asbd.mFormatID = formatId
        asbd.mFormatFlags = formatFlags
        asbd.mBitsPerChannel = bitsPerChannel
        asbd.mChannelsPerFrame = channelsPerFrame
        asbd.mFramesPerPacket = framesPerPacket
        asbd.mBytesPerFrame = bytesPerFrame
        asbd.mBytesPerPacket = framesPerPacket * bytesPerFrame
        // Set up the file
        var audioFile: AudioFileID?
        var audioErr: OSStatus = noErr
        audioErr = AudioFileCreateWithURL(fileURL as CFURL, AudioFileTypeID(kAudioFileWAVEType), &asbd, .eraseFile, &audioFile)
        if audioErr == noErr {
            self.audioFile = audioFile
        }
        return audioErr
    }

    func prepareAudioToolbox() -> OSStatus {
        var status = noErr
        // Describe the RemoteIO unit
        var audioComponentDescription = AudioComponentDescription()
        audioComponentDescription.componentType = kAudioUnitType_Output
        audioComponentDescription.componentSubType = kAudioUnitSubType_RemoteIO
        audioComponentDescription.componentManufacturer = kAudioUnitManufacturer_Apple
        audioComponentDescription.componentFlags = 0
        audioComponentDescription.componentFlagsMask = 0
        // Get the RemoteIO unit
        var ioUnit: AudioComponentInstance?
        let remoteIOComponent = AudioComponentFindNext(nil, &audioComponentDescription)
        status = AudioComponentInstanceNew(remoteIOComponent!, &ioUnit)
        guard status == noErr else {
            return status
        }
        guard let remoteIOUnit = ioUnit else {
            return 656783
        }
        self.remoteIOUnit = remoteIOUnit
        // Configure the RemoteIO unit for input
        let bus1: AudioUnitElement = 1
        var oneFlag: UInt32 = 1
        status = AudioUnitSetProperty(remoteIOUnit,
                                      kAudioOutputUnitProperty_EnableIO,
                                      kAudioUnitScope_Input,
                                      bus1,
                                      &oneFlag,
                                      UInt32(MemoryLayout<UInt32>.size));
        guard status == noErr else {
            return status
        }
        var asbd = AudioStreamBasicDescription()
        asbd.mSampleRate = Recorder.sampleRate
        asbd.mFormatID = formatId
        asbd.mFormatFlags = formatFlags
        asbd.mBitsPerChannel = bitsPerChannel
        asbd.mChannelsPerFrame = channelsPerFrame
        asbd.mFramesPerPacket = framesPerPacket
        asbd.mBytesPerFrame = bytesPerFrame
        asbd.mBytesPerPacket = framesPerPacket * bytesPerFrame
        // Set format for mic input (bus 1) on RemoteIO's output scope
        status = AudioUnitSetProperty(remoteIOUnit,
                                      kAudioUnitProperty_StreamFormat,
                                      kAudioUnitScope_Output,
                                      bus1,
                                      &asbd,
                                      UInt32(MemoryLayout<AudioStreamBasicDescription>.size))
        guard status == noErr else {
            return status
        }
        // Set the recording callback
        var callbackStruct = AURenderCallbackStruct()
        callbackStruct.inputProc = recordingCallback
        callbackStruct.inputProcRefCon = nil
        status = AudioUnitSetProperty(remoteIOUnit,
                                      kAudioOutputUnitProperty_SetInputCallback,
                                      kAudioUnitScope_Global,
                                      bus1,
                                      &callbackStruct,
                                      UInt32(MemoryLayout<AURenderCallbackStruct>.size));
        guard status == noErr else {
            return status
        }
        // Initialize the RemoteIO unit
        return AudioUnitInitialize(remoteIOUnit)
    }

    func startAudioToolboxRecording() -> OSStatus {
        guard let remoteIOUnit = remoteIOUnit else {
            return 656783
        }
        return AudioOutputUnitStart(remoteIOUnit)
    }

    func writeDataToFile(audioBuffers: UnsafeMutableBufferPointer<AudioBuffer>) -> OSStatus {
        guard let audioFile = audioFile else {
            return 176136
        }
        var startingByte = self.startingByte
        for audioBuffer in audioBuffers {
            var numBytes = audioBuffer.mDataByteSize
            guard let mData = audioBuffer.mData else {
                continue
            }
            // [1] following call fails with `-38` error (`kAudioFileNotOpenError`). Less often it fails with `1868981823` error (`kAudioFileDoesNotAllow64BitDataSizeError`)
            let audioErr = AudioFileWriteBytes(audioFile,
                                               false,
                                               startingByte,
                                               &numBytes,
                                               mData)
            guard audioErr == noErr else {
                return audioErr
            }
            let data = Data(bytes:  mData, count: Int(numBytes))
            processAudioData?(data)
            startingByte += Int64(numBytes)
        }
        self.startingByte = startingByte
        return noErr
    }

}

private func recordingCallback(
    inRefCon: UnsafeMutableRawPointer,
    ioActionFlags: UnsafeMutablePointer<AudioUnitRenderActionFlags>,
    inTimeStamp: UnsafePointer<AudioTimeStamp>,
    inBusNumber: UInt32,
    inNumberFrames: UInt32,
    ioData: UnsafeMutablePointer<AudioBufferList>?) -> OSStatus {

    guard let remoteIOUnit = Recorder.shared.remoteIOUnit else {
        return 656783
    }
    var status = noErr
    let channelCount: UInt32 = 1
    var bufferList = AudioBufferList()
    bufferList.mNumberBuffers = channelCount
    let buffers = UnsafeMutableBufferPointer<AudioBuffer>(start: &bufferList.mBuffers,
                                                          count: Int(bufferList.mNumberBuffers))
    buffers[0].mNumberChannels = 1
    buffers[0].mDataByteSize = inNumberFrames * 2
    buffers[0].mData = nil
    // get the recorded samples
    // [2] following call fails with `-10863` error (`kAudioUnitErr_CannotDoInCurrentContext`) and less often with `-1` error
    status = AudioUnitRender(remoteIOUnit,
                             ioActionFlags,
                             inTimeStamp,
                             inBusNumber,
                             inNumberFrames,
                             UnsafeMutablePointer<AudioBufferList>(&bufferList))
    guard status == noErr else {
        return status
    }
    status = Recorder.shared.writeDataToFile(audioBuffers: buffers)
    return status
}
Run Code Online (Sandbox Code Playgroud)

小智 1

好的,我希望这有所帮助,我认为问题在于记录速度、写入速度和渲染速度,因此您可以使用块进行写入,以便可以隔离方法,我在图形方面也有同样的问题,所以我总是块(锁定和解锁)缓冲区)。

使用块