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

时间:2018-01-19 15:33:06

标签: ios swift cocoa-touch audio audiotoolbox

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

现在似乎无缘无故地发生这种情况:用户开始录制,一切顺利,然后连续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%
  5. 任何帮助,评论或建议都会非常有用!

    这是代码(它也可以在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
    }
    

1 个答案:

答案 0 :(得分:1)

好的我希望有这个帮助,我认为问题在于记录与写入对渲染的速度,所以你可以用块写入以便你可以隔离方法,我有相同的图形问题所以我总是阻止(锁定和解锁缓冲区。)

Working with Blocks