AudioToolBox Recorder 受到 AVFoundation AudioPlayer 的影响
AudioToolBox Recorder gets affect by AVFoundation AudioPlayer
所以,我有以下 类:
- 录音机(使用
AudioToolbox
和 CoreAudio
)录制音频。
- 音频播放器(使用
AVFoundation
)
录音机捕获音频,将其发送到服务器,然后服务器回复另一个音频,然后播放器播放接收到的音频。
当我尝试再次调用录音机录音时,录音机无法正常录音。
知道如何重置录音机,以便在音频播放器播放完毕后可以正常录音吗?
我尝试再次初始化录音机(就在录音之前),但这不起作用。
影响录音机的行是下面的 2 行,不幸的是,为了用 AVFoundation
播放音频需要这些行。
let sharedSession = AVAudioSession.sharedInstance()
try sharedSession.setCategory(AVAudioSessionCategoryPlayback)
执行流程
- 录音机音频
- 向服务器发送和接收音频
- 从服务器录音机音频播放音频此时录音机没有正确录音
- 向服务器发送和接收音频失败
- 播放音频失败
提前致谢!
陆
Link 至 recorder project
录音机:
import UIKit
import CoreAudio
import AudioToolbox
class SpeechRecorder: NSObject {
static let sharedInstance = SpeechRecorder()
// MARK:- properties
@objc enum Status: Int {
case ready
case busy
case error
}
internal struct RecordState {
var format: AudioStreamBasicDescription
var queue: UnsafeMutablePointer<AudioQueueRef?>
var buffers: [AudioQueueBufferRef?]
var file: AudioFileID?
var currentPacket: Int64
var recording: Bool
};
private var _recordState: RecordState?
private var _audioURL:URL?
var format: AudioFormatID {
get { return _recordState!.format.mFormatID }
set { _recordState!.format.mFormatID = newValue }
}
var sampleRate: Float64 {
get { return _recordState!.format.mSampleRate }
set { _recordState!.format.mSampleRate = newValue }
}
var formatFlags: AudioFormatFlags {
get { return _recordState!.format.mFormatFlags }
set { _recordState!.format.mFormatFlags = newValue }
}
var channelsPerFrame: UInt32 {
get { return _recordState!.format.mChannelsPerFrame }
set { _recordState!.format.mChannelsPerFrame = newValue }
}
var bitsPerChannel: UInt32 {
get { return _recordState!.format.mBitsPerChannel }
set { _recordState!.format.mBitsPerChannel = newValue }
}
var framesPerPacket: UInt32 {
get { return _recordState!.format.mFramesPerPacket }
set { _recordState!.format.mFramesPerPacket = newValue }
}
var bytesPerFrame: UInt32 {
get { return _recordState!.format.mBytesPerFrame }
set { _recordState!.format.mBytesPerFrame = newValue }
}
var bytesPerPacket: UInt32 {
get { return _recordState!.format.mBytesPerPacket }
set { _recordState!.format.mBytesPerPacket = newValue }
}
//MARK: - Handlers
public var handler: ((_ status:Status, _ data:NSData?, _ errorDesc:String?) -> Void)?
// MARK:- Init
override init()
{
super.init()
self._recordState = RecordState(format: AudioStreamBasicDescription(),
queue: UnsafeMutablePointer<AudioQueueRef?>.allocate(capacity: 1),
buffers: [AudioQueueBufferRef?](repeating: nil, count: 1),
file: nil,
currentPacket: 0,
recording: false)
}//eom
// MARK:- OutputFile
private func getDocumentsPath()->URL
{
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
func setOutputFileNameWithDocumentsDirectory(nameDesired:String)
{
_audioURL = getDocumentsPath().appendingPathComponent(nameDesired)
setOutputFile(url: _audioURL!)
}//eom
func setOutputFileNameWithTempDirectory(nameDesired:String)
{
let tempDir = NSTemporaryDirectory()
let tempURLdir = URL(fileURLWithPath: tempDir)
_audioURL = tempURLdir.appendingPathComponent(nameDesired)
setOutputFile(url: _audioURL!)
}//eom
private func setOutputFile(path: String)
{
setOutputFile(url: URL(fileURLWithPath: path))
}//eom
private func setOutputFile(url: URL)
{
AudioFileCreateWithURL(url as CFURL,
kAudioFileWAVEType,
&_recordState!.format,
AudioFileFlags.dontPageAlignAudioData.union(.eraseFile),
&_recordState!.file)
}
// MARK:- Start / Stop Recording
func start()
{
handler?(.busy, nil, nil)
self._recordState?.currentPacket = 0
let inputAudioQueue: AudioQueueInputCallback =
{ (userData: UnsafeMutableRawPointer?,
audioQueue: AudioQueueRef,
bufferQueue: AudioQueueBufferRef,
startTime: UnsafePointer<AudioTimeStamp>,
packets: UInt32,
packetDescription: UnsafePointer<AudioStreamPacketDescription>?) in
let internalRSP = unsafeBitCast(userData, to: UnsafeMutablePointer<RecordState>.self)
if packets > 0
{
var packetsReceived = packets
let outputStream:OSStatus = AudioFileWritePackets(internalRSP.pointee.file!,
false,
bufferQueue.pointee.mAudioDataByteSize,
packetDescription,
internalRSP.pointee.currentPacket,
&packetsReceived,
bufferQueue.pointee.mAudioData)
if outputStream != 0
{
if verbose
{
print("Error with AudioFileWritePackets")
//<----DEBUG
switch outputStream
{
case kAudioFilePermissionsError:
print("kAudioFilePermissionsError")
break
case kAudioFileNotOptimizedError:
print("kAudioFileNotOptimizedError")
break
case kAudioFileInvalidChunkError:
print("kAudioFileInvalidChunkError")
break
case kAudioFileDoesNotAllow64BitDataSizeError:
print("kAudioFileDoesNotAllow64BitDataSizeError")
break
case kAudioFileInvalidPacketOffsetError:
print("kAudioFileInvalidPacketOffsetError")
break
case kAudioFileInvalidFileError:
print("kAudioFileInvalidFileError")
break
case kAudioFileOperationNotSupportedError:
print("kAudioFileOperationNotSupportedError")
break
case kAudioFileNotOpenError:
print("kAudioFileNotOpenError")
break
case kAudioFileEndOfFileError:
print("kAudioFileEndOfFileError")
break
case kAudioFilePositionError:
print("kAudioFilePositionError")
break
case kAudioFileFileNotFoundError:
print("kAudioFileFileNotFoundError")
break
case kAudioFileUnspecifiedError:
print("kAudioFileUnspecifiedError")
break
case kAudioFileUnsupportedFileTypeError:
print("kAudioFileUnsupportedFileTypeError")
break
case kAudioFileUnsupportedDataFormatError:
print("kAudioFileUnsupportedDataFormatError")
break
case kAudioFileUnsupportedPropertyError:
print("kAudioFileUnsupportedPropertyError")
break
case kAudioFileBadPropertySizeError:
print("kAudioFileBadPropertySizeError")
break
default:
print("unknown error")
break
}
//<----DEBUG
}
}
internalRSP.pointee.currentPacket += Int64(packetsReceived)
}
if internalRSP.pointee.recording
{
let outputStream:OSStatus = AudioQueueEnqueueBuffer(audioQueue, bufferQueue, 0, nil)
if outputStream != 0
{
if verbose
{
print("Error with AudioQueueEnqueueBuffer")
//<----DEBUG
switch outputStream
{
case kAudioFilePermissionsError:
print("kAudioFilePermissionsError")
break
case kAudioFileNotOptimizedError:
print("kAudioFileNotOptimizedError")
break
case kAudioFileInvalidChunkError:
print("kAudioFileInvalidChunkError")
break
case kAudioFileDoesNotAllow64BitDataSizeError:
print("kAudioFileDoesNotAllow64BitDataSizeError")
break
case kAudioFileInvalidPacketOffsetError:
print("kAudioFileInvalidPacketOffsetError")
break
case kAudioFileInvalidFileError:
print("kAudioFileInvalidFileError")
break
case kAudioFileOperationNotSupportedError:
print("kAudioFileOperationNotSupportedError")
break
case kAudioFileNotOpenError:
print("kAudioFileNotOpenError")
break
case kAudioFileEndOfFileError:
print("kAudioFileEndOfFileError")
break
case kAudioFilePositionError:
print("kAudioFilePositionError")
break
case kAudioFileFileNotFoundError:
print("kAudioFileFileNotFoundError")
break
case kAudioFileUnspecifiedError:
print("kAudioFileUnspecifiedError")
break
case kAudioFileUnsupportedFileTypeError:
print("kAudioFileUnsupportedFileTypeError")
break
case kAudioFileUnsupportedDataFormatError:
print("kAudioFileUnsupportedDataFormatError")
break
case kAudioFileUnsupportedPropertyError:
print("kAudioFileUnsupportedPropertyError")
break
case kAudioFileBadPropertySizeError:
print("kAudioFileBadPropertySizeError")
break
default:
print("unknown error")
break
//<----DEBUG
}
}
}
}
}
let queueResults = AudioQueueNewInput(&_recordState!.format, inputAudioQueue, &_recordState, nil, nil, 0, _recordState!.queue)
if queueResults == 0
{
let bufferByteSize: Int = calculate(format: _recordState!.format, seconds: 0.5)
for index in (0..<_recordState!.buffers.count)
{
AudioQueueAllocateBuffer(_recordState!.queue.pointee!, UInt32(bufferByteSize), &_recordState!.buffers[index])
AudioQueueEnqueueBuffer(_recordState!.queue.pointee!, _recordState!.buffers[index]!, 0, nil)
}
AudioQueueStart(_recordState!.queue.pointee!, nil)
_recordState?.recording = true
}
else
{
handler?(.error, nil, "Error setting audio input.")
}
}//eom
func stop()
{
_recordState?.recording = false
if let recordingState: RecordState = _recordState
{
AudioQueueStop(recordingState.queue.pointee!, true)
AudioQueueDispose(recordingState.queue.pointee!, true)
AudioFileClose(recordingState.file!)
let audioData:NSData? = NSData(contentsOf: _audioURL!)
handler?(.ready, audioData, nil)
}
}//eom
// MARK:- Helper methods
func calculate(format: AudioStreamBasicDescription, seconds: Double) -> Int
{
let framesRequiredForBufferTime = Int(ceil(seconds * format.mSampleRate))
if framesRequiredForBufferTime > 0
{
return (framesRequiredForBufferTime * Int(format.mBytesPerFrame))
}
else
{
var maximumPacketSize = UInt32(0)
if format.mBytesPerPacket > 0
{
maximumPacketSize = format.mBytesPerPacket
}
else
{
audioQueueProperty(propertyId: kAudioQueueProperty_MaximumOutputPacketSize, value: &maximumPacketSize)
}
var packets = 0
if format.mFramesPerPacket > 0
{
packets = (framesRequiredForBufferTime / Int(format.mFramesPerPacket))
} else
{
packets = framesRequiredForBufferTime
}
if packets == 0
{
packets = 1
}
return (packets * Int(maximumPacketSize))
}
}//eom
func audioQueueProperty<T>(propertyId: AudioQueuePropertyID, value: inout T)
{
let propertySize = UnsafeMutablePointer<UInt32>.allocate(capacity: 1)
propertySize.pointee = UInt32(MemoryLayout<T>.size)
let queueResults = AudioQueueGetProperty(_recordState!.queue.pointee!, propertyId, &value, propertySize)
propertySize.deallocate(capacity: 1)
if queueResults != 0 {
handler?(.error, nil, "Unable to get audio queue property.")
}
}//eom
}
玩家:
import UIKit
import AVFoundation
protocol AudioPlayerDelegate {
func audioPlayer_playbackError(playerItemID:String, error:String)
func audioPlayer_playbackSuccess(playerItemID:String)
}
class AudioPlayer: NSObject, AVAudioPlayerDelegate
{
//properties
private var _audioPlayer:AVAudioPlayer?
var delegate:AudioPlayerDelegate?
var playerItemID:String = ""
var volume:Float?
//MARK: - Play Audio
func playAudioFromData(_ playerItemID:String, dataToPlay:Data)
{
do {
let sharedSession = AVAudioSession.sharedInstance()
try sharedSession.setCategory(AVAudioSessionCategoryPlayback)
try sharedSession.setActive(true)
_audioPlayer = try AVAudioPlayer(data: dataToPlay)
_audioPlayer?.numberOfLoops = 0
_audioPlayer?.isMeteringEnabled = true
_audioPlayer?.delegate = self
//volume
if volume != nil {
_audioPlayer?.volume = volume!
}
//id
self.playerItemID = playerItemID
_audioPlayer?.play()
}
catch let error {
self.delegate?.audioPlayer_playbackError(playerItemID: self.playerItemID, error: error.localizedDescription)
}
}//eom
func playAudioFromUrl(_ url:URL)
{
do {
let sharedSession = AVAudioSession.sharedInstance()
try sharedSession.setCategory(AVAudioSessionCategoryPlayback)
try sharedSession.setActive(true)
if FileManager.default.fileExists(atPath: url.path) {
_audioPlayer = try AVAudioPlayer(contentsOf: url)
_audioPlayer?.numberOfLoops = 0
_audioPlayer?.isMeteringEnabled = true
_audioPlayer?.delegate = self
//volume
if volume != nil {
_audioPlayer?.volume = volume!
}
//id
self.playerItemID = url.absoluteString
_audioPlayer?.play()
}
else {
self.delegate?.audioPlayer_playbackError(playerItemID: self.playerItemID, error: "audio file does not exist")
}
}
catch let error {
self.delegate?.audioPlayer_playbackError(playerItemID: self.playerItemID, error: error.localizedDescription)
}
}//eom
//MARK: - Player Options
func pausePlay()
{
_audioPlayer?.pause()
}//eom
func stopPlay()
{
_audioPlayer?.stop()
do {
let sharedSession = AVAudioSession.sharedInstance()
try sharedSession.setActive(false)
}
catch let error {
if verbose { print("un-able to set session to inactive, error: \(error)") }
}
}//eom
//MARK: - Delegates
func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) {
//inactive session
do {
let sharedSession = AVAudioSession.sharedInstance()
try sharedSession.setActive(false)
}
catch let error {
if verbose { print("un-able to set session to inactive, error: \(error)") }
}
//report status
if error != nil {
self.delegate?.audioPlayer_playbackError(playerItemID: self.playerItemID, error: error!.localizedDescription)
}
else {
self.delegate?.audioPlayer_playbackError(playerItemID: self.playerItemID, error: "decode error did occurred")
}
//reset
self._audioPlayer?.delegate = nil
self._audioPlayer = nil
self.playerItemID = ""
}//eom
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
//inactive session
do {
let sharedSession = AVAudioSession.sharedInstance()
try sharedSession.setActive(false)
}
catch let error {
if verbose { print("un-able to set session to inactive, error: \(error)") }
}
//report status
if flag {
delegate?.audioPlayer_playbackSuccess(playerItemID: self.playerItemID)
}
else {
delegate?.audioPlayer_playbackError(playerItemID: self.playerItemID, error: "player finish playing with error")
}
//reset
self._audioPlayer?.delegate = nil
self._audioPlayer = nil
self.playerItemID = ""
}//eom
}//eoc
如果您要同时使用 AudioToolBox 和 AVFoundation,您可能需要小心使用 AudioSession。 AVFoundation 对后端的 AudioSession 做了很多更新。
您的播放器的快速修复方法是删除任何音频会话调用,如下所示:
let sharedSession = AVAudioSession.sharedInstance()
try sharedSession.setCategory(AVAudioSessionCategoryPlayback)
try sharedSession.setActive(true)
_audioPlayer?.numberOfLoops = 0
_audioPlayer?.isMeteringEnabled = true
如需更高级的音频处理,请查看本书Learning Core Audio by Chris Adamson, Kevin Avila
所以,我有以下 类:
- 录音机(使用
AudioToolbox
和CoreAudio
)录制音频。 - 音频播放器(使用
AVFoundation
)
录音机捕获音频,将其发送到服务器,然后服务器回复另一个音频,然后播放器播放接收到的音频。
当我尝试再次调用录音机录音时,录音机无法正常录音。
知道如何重置录音机,以便在音频播放器播放完毕后可以正常录音吗?
我尝试再次初始化录音机(就在录音之前),但这不起作用。
影响录音机的行是下面的 2 行,不幸的是,为了用 AVFoundation
播放音频需要这些行。
let sharedSession = AVAudioSession.sharedInstance()
try sharedSession.setCategory(AVAudioSessionCategoryPlayback)
执行流程
- 录音机音频
- 向服务器发送和接收音频
- 从服务器录音机音频播放音频此时录音机没有正确录音
- 向服务器发送和接收音频失败
- 播放音频失败
提前致谢! 陆
Link 至 recorder project
录音机:
import UIKit
import CoreAudio
import AudioToolbox
class SpeechRecorder: NSObject {
static let sharedInstance = SpeechRecorder()
// MARK:- properties
@objc enum Status: Int {
case ready
case busy
case error
}
internal struct RecordState {
var format: AudioStreamBasicDescription
var queue: UnsafeMutablePointer<AudioQueueRef?>
var buffers: [AudioQueueBufferRef?]
var file: AudioFileID?
var currentPacket: Int64
var recording: Bool
};
private var _recordState: RecordState?
private var _audioURL:URL?
var format: AudioFormatID {
get { return _recordState!.format.mFormatID }
set { _recordState!.format.mFormatID = newValue }
}
var sampleRate: Float64 {
get { return _recordState!.format.mSampleRate }
set { _recordState!.format.mSampleRate = newValue }
}
var formatFlags: AudioFormatFlags {
get { return _recordState!.format.mFormatFlags }
set { _recordState!.format.mFormatFlags = newValue }
}
var channelsPerFrame: UInt32 {
get { return _recordState!.format.mChannelsPerFrame }
set { _recordState!.format.mChannelsPerFrame = newValue }
}
var bitsPerChannel: UInt32 {
get { return _recordState!.format.mBitsPerChannel }
set { _recordState!.format.mBitsPerChannel = newValue }
}
var framesPerPacket: UInt32 {
get { return _recordState!.format.mFramesPerPacket }
set { _recordState!.format.mFramesPerPacket = newValue }
}
var bytesPerFrame: UInt32 {
get { return _recordState!.format.mBytesPerFrame }
set { _recordState!.format.mBytesPerFrame = newValue }
}
var bytesPerPacket: UInt32 {
get { return _recordState!.format.mBytesPerPacket }
set { _recordState!.format.mBytesPerPacket = newValue }
}
//MARK: - Handlers
public var handler: ((_ status:Status, _ data:NSData?, _ errorDesc:String?) -> Void)?
// MARK:- Init
override init()
{
super.init()
self._recordState = RecordState(format: AudioStreamBasicDescription(),
queue: UnsafeMutablePointer<AudioQueueRef?>.allocate(capacity: 1),
buffers: [AudioQueueBufferRef?](repeating: nil, count: 1),
file: nil,
currentPacket: 0,
recording: false)
}//eom
// MARK:- OutputFile
private func getDocumentsPath()->URL
{
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
func setOutputFileNameWithDocumentsDirectory(nameDesired:String)
{
_audioURL = getDocumentsPath().appendingPathComponent(nameDesired)
setOutputFile(url: _audioURL!)
}//eom
func setOutputFileNameWithTempDirectory(nameDesired:String)
{
let tempDir = NSTemporaryDirectory()
let tempURLdir = URL(fileURLWithPath: tempDir)
_audioURL = tempURLdir.appendingPathComponent(nameDesired)
setOutputFile(url: _audioURL!)
}//eom
private func setOutputFile(path: String)
{
setOutputFile(url: URL(fileURLWithPath: path))
}//eom
private func setOutputFile(url: URL)
{
AudioFileCreateWithURL(url as CFURL,
kAudioFileWAVEType,
&_recordState!.format,
AudioFileFlags.dontPageAlignAudioData.union(.eraseFile),
&_recordState!.file)
}
// MARK:- Start / Stop Recording
func start()
{
handler?(.busy, nil, nil)
self._recordState?.currentPacket = 0
let inputAudioQueue: AudioQueueInputCallback =
{ (userData: UnsafeMutableRawPointer?,
audioQueue: AudioQueueRef,
bufferQueue: AudioQueueBufferRef,
startTime: UnsafePointer<AudioTimeStamp>,
packets: UInt32,
packetDescription: UnsafePointer<AudioStreamPacketDescription>?) in
let internalRSP = unsafeBitCast(userData, to: UnsafeMutablePointer<RecordState>.self)
if packets > 0
{
var packetsReceived = packets
let outputStream:OSStatus = AudioFileWritePackets(internalRSP.pointee.file!,
false,
bufferQueue.pointee.mAudioDataByteSize,
packetDescription,
internalRSP.pointee.currentPacket,
&packetsReceived,
bufferQueue.pointee.mAudioData)
if outputStream != 0
{
if verbose
{
print("Error with AudioFileWritePackets")
//<----DEBUG
switch outputStream
{
case kAudioFilePermissionsError:
print("kAudioFilePermissionsError")
break
case kAudioFileNotOptimizedError:
print("kAudioFileNotOptimizedError")
break
case kAudioFileInvalidChunkError:
print("kAudioFileInvalidChunkError")
break
case kAudioFileDoesNotAllow64BitDataSizeError:
print("kAudioFileDoesNotAllow64BitDataSizeError")
break
case kAudioFileInvalidPacketOffsetError:
print("kAudioFileInvalidPacketOffsetError")
break
case kAudioFileInvalidFileError:
print("kAudioFileInvalidFileError")
break
case kAudioFileOperationNotSupportedError:
print("kAudioFileOperationNotSupportedError")
break
case kAudioFileNotOpenError:
print("kAudioFileNotOpenError")
break
case kAudioFileEndOfFileError:
print("kAudioFileEndOfFileError")
break
case kAudioFilePositionError:
print("kAudioFilePositionError")
break
case kAudioFileFileNotFoundError:
print("kAudioFileFileNotFoundError")
break
case kAudioFileUnspecifiedError:
print("kAudioFileUnspecifiedError")
break
case kAudioFileUnsupportedFileTypeError:
print("kAudioFileUnsupportedFileTypeError")
break
case kAudioFileUnsupportedDataFormatError:
print("kAudioFileUnsupportedDataFormatError")
break
case kAudioFileUnsupportedPropertyError:
print("kAudioFileUnsupportedPropertyError")
break
case kAudioFileBadPropertySizeError:
print("kAudioFileBadPropertySizeError")
break
default:
print("unknown error")
break
}
//<----DEBUG
}
}
internalRSP.pointee.currentPacket += Int64(packetsReceived)
}
if internalRSP.pointee.recording
{
let outputStream:OSStatus = AudioQueueEnqueueBuffer(audioQueue, bufferQueue, 0, nil)
if outputStream != 0
{
if verbose
{
print("Error with AudioQueueEnqueueBuffer")
//<----DEBUG
switch outputStream
{
case kAudioFilePermissionsError:
print("kAudioFilePermissionsError")
break
case kAudioFileNotOptimizedError:
print("kAudioFileNotOptimizedError")
break
case kAudioFileInvalidChunkError:
print("kAudioFileInvalidChunkError")
break
case kAudioFileDoesNotAllow64BitDataSizeError:
print("kAudioFileDoesNotAllow64BitDataSizeError")
break
case kAudioFileInvalidPacketOffsetError:
print("kAudioFileInvalidPacketOffsetError")
break
case kAudioFileInvalidFileError:
print("kAudioFileInvalidFileError")
break
case kAudioFileOperationNotSupportedError:
print("kAudioFileOperationNotSupportedError")
break
case kAudioFileNotOpenError:
print("kAudioFileNotOpenError")
break
case kAudioFileEndOfFileError:
print("kAudioFileEndOfFileError")
break
case kAudioFilePositionError:
print("kAudioFilePositionError")
break
case kAudioFileFileNotFoundError:
print("kAudioFileFileNotFoundError")
break
case kAudioFileUnspecifiedError:
print("kAudioFileUnspecifiedError")
break
case kAudioFileUnsupportedFileTypeError:
print("kAudioFileUnsupportedFileTypeError")
break
case kAudioFileUnsupportedDataFormatError:
print("kAudioFileUnsupportedDataFormatError")
break
case kAudioFileUnsupportedPropertyError:
print("kAudioFileUnsupportedPropertyError")
break
case kAudioFileBadPropertySizeError:
print("kAudioFileBadPropertySizeError")
break
default:
print("unknown error")
break
//<----DEBUG
}
}
}
}
}
let queueResults = AudioQueueNewInput(&_recordState!.format, inputAudioQueue, &_recordState, nil, nil, 0, _recordState!.queue)
if queueResults == 0
{
let bufferByteSize: Int = calculate(format: _recordState!.format, seconds: 0.5)
for index in (0..<_recordState!.buffers.count)
{
AudioQueueAllocateBuffer(_recordState!.queue.pointee!, UInt32(bufferByteSize), &_recordState!.buffers[index])
AudioQueueEnqueueBuffer(_recordState!.queue.pointee!, _recordState!.buffers[index]!, 0, nil)
}
AudioQueueStart(_recordState!.queue.pointee!, nil)
_recordState?.recording = true
}
else
{
handler?(.error, nil, "Error setting audio input.")
}
}//eom
func stop()
{
_recordState?.recording = false
if let recordingState: RecordState = _recordState
{
AudioQueueStop(recordingState.queue.pointee!, true)
AudioQueueDispose(recordingState.queue.pointee!, true)
AudioFileClose(recordingState.file!)
let audioData:NSData? = NSData(contentsOf: _audioURL!)
handler?(.ready, audioData, nil)
}
}//eom
// MARK:- Helper methods
func calculate(format: AudioStreamBasicDescription, seconds: Double) -> Int
{
let framesRequiredForBufferTime = Int(ceil(seconds * format.mSampleRate))
if framesRequiredForBufferTime > 0
{
return (framesRequiredForBufferTime * Int(format.mBytesPerFrame))
}
else
{
var maximumPacketSize = UInt32(0)
if format.mBytesPerPacket > 0
{
maximumPacketSize = format.mBytesPerPacket
}
else
{
audioQueueProperty(propertyId: kAudioQueueProperty_MaximumOutputPacketSize, value: &maximumPacketSize)
}
var packets = 0
if format.mFramesPerPacket > 0
{
packets = (framesRequiredForBufferTime / Int(format.mFramesPerPacket))
} else
{
packets = framesRequiredForBufferTime
}
if packets == 0
{
packets = 1
}
return (packets * Int(maximumPacketSize))
}
}//eom
func audioQueueProperty<T>(propertyId: AudioQueuePropertyID, value: inout T)
{
let propertySize = UnsafeMutablePointer<UInt32>.allocate(capacity: 1)
propertySize.pointee = UInt32(MemoryLayout<T>.size)
let queueResults = AudioQueueGetProperty(_recordState!.queue.pointee!, propertyId, &value, propertySize)
propertySize.deallocate(capacity: 1)
if queueResults != 0 {
handler?(.error, nil, "Unable to get audio queue property.")
}
}//eom
}
玩家:
import UIKit
import AVFoundation
protocol AudioPlayerDelegate {
func audioPlayer_playbackError(playerItemID:String, error:String)
func audioPlayer_playbackSuccess(playerItemID:String)
}
class AudioPlayer: NSObject, AVAudioPlayerDelegate
{
//properties
private var _audioPlayer:AVAudioPlayer?
var delegate:AudioPlayerDelegate?
var playerItemID:String = ""
var volume:Float?
//MARK: - Play Audio
func playAudioFromData(_ playerItemID:String, dataToPlay:Data)
{
do {
let sharedSession = AVAudioSession.sharedInstance()
try sharedSession.setCategory(AVAudioSessionCategoryPlayback)
try sharedSession.setActive(true)
_audioPlayer = try AVAudioPlayer(data: dataToPlay)
_audioPlayer?.numberOfLoops = 0
_audioPlayer?.isMeteringEnabled = true
_audioPlayer?.delegate = self
//volume
if volume != nil {
_audioPlayer?.volume = volume!
}
//id
self.playerItemID = playerItemID
_audioPlayer?.play()
}
catch let error {
self.delegate?.audioPlayer_playbackError(playerItemID: self.playerItemID, error: error.localizedDescription)
}
}//eom
func playAudioFromUrl(_ url:URL)
{
do {
let sharedSession = AVAudioSession.sharedInstance()
try sharedSession.setCategory(AVAudioSessionCategoryPlayback)
try sharedSession.setActive(true)
if FileManager.default.fileExists(atPath: url.path) {
_audioPlayer = try AVAudioPlayer(contentsOf: url)
_audioPlayer?.numberOfLoops = 0
_audioPlayer?.isMeteringEnabled = true
_audioPlayer?.delegate = self
//volume
if volume != nil {
_audioPlayer?.volume = volume!
}
//id
self.playerItemID = url.absoluteString
_audioPlayer?.play()
}
else {
self.delegate?.audioPlayer_playbackError(playerItemID: self.playerItemID, error: "audio file does not exist")
}
}
catch let error {
self.delegate?.audioPlayer_playbackError(playerItemID: self.playerItemID, error: error.localizedDescription)
}
}//eom
//MARK: - Player Options
func pausePlay()
{
_audioPlayer?.pause()
}//eom
func stopPlay()
{
_audioPlayer?.stop()
do {
let sharedSession = AVAudioSession.sharedInstance()
try sharedSession.setActive(false)
}
catch let error {
if verbose { print("un-able to set session to inactive, error: \(error)") }
}
}//eom
//MARK: - Delegates
func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) {
//inactive session
do {
let sharedSession = AVAudioSession.sharedInstance()
try sharedSession.setActive(false)
}
catch let error {
if verbose { print("un-able to set session to inactive, error: \(error)") }
}
//report status
if error != nil {
self.delegate?.audioPlayer_playbackError(playerItemID: self.playerItemID, error: error!.localizedDescription)
}
else {
self.delegate?.audioPlayer_playbackError(playerItemID: self.playerItemID, error: "decode error did occurred")
}
//reset
self._audioPlayer?.delegate = nil
self._audioPlayer = nil
self.playerItemID = ""
}//eom
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
//inactive session
do {
let sharedSession = AVAudioSession.sharedInstance()
try sharedSession.setActive(false)
}
catch let error {
if verbose { print("un-able to set session to inactive, error: \(error)") }
}
//report status
if flag {
delegate?.audioPlayer_playbackSuccess(playerItemID: self.playerItemID)
}
else {
delegate?.audioPlayer_playbackError(playerItemID: self.playerItemID, error: "player finish playing with error")
}
//reset
self._audioPlayer?.delegate = nil
self._audioPlayer = nil
self.playerItemID = ""
}//eom
}//eoc
如果您要同时使用 AudioToolBox 和 AVFoundation,您可能需要小心使用 AudioSession。 AVFoundation 对后端的 AudioSession 做了很多更新。
您的播放器的快速修复方法是删除任何音频会话调用,如下所示:
let sharedSession = AVAudioSession.sharedInstance()
try sharedSession.setCategory(AVAudioSessionCategoryPlayback)
try sharedSession.setActive(true)
_audioPlayer?.numberOfLoops = 0
_audioPlayer?.isMeteringEnabled = true
如需更高级的音频处理,请查看本书Learning Core Audio by Chris Adamson, Kevin Avila