我正在处理记录用户音频的应用程序,使用AudioToolbox
将数据写入文件并处理这些数据。在分析中,我看到AudioToolbox录制中发生了很多错误。其中几乎一半是从-38 kAudioFileNotOpenError
电话回复AudioFileWriteBytes()
错误。
现在似乎无缘无故地发生这种情况:用户开始录制,一切顺利,然后连续10-80次记录错误。在分析中,没有用户操作,例如转到后台或暂停录制(我们有此功能)。
我在线研究了信息,但没有找到有关错误的更多信息。
我发布了所有录制代码(带有已删除的未使用或分析代码),因为除-38 kAudioFileNotOpenError
之外我还有其他一些错误,这可能意味着我可能正在使用AudioToolbox
错误的方法。以下是最重要的错误:
AudioFileWriteBytes()
返回-38 kAudioFileNotOpenError
错误:(代码中[1]
) - 约占所有错误的50%AudioUnitRender()
返回-10863 kAudioUnitErr_CannotDoInCurrentContext
错误(代码中为[2]
) - ~5%AudioFileWriteBytes()
返回1868981823 kAudioFileDoesNotAllow64BitDataSizeError
错误:(代码中[1]
) - ~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
}
答案 0 :(得分:1)
好的我希望有这个帮助,我认为问题在于记录与写入对渲染的速度,所以你可以用块写入以便你可以隔离方法,我有相同的图形问题所以我总是阻止(锁定和解锁缓冲区。)