ios - AudioToolBox Recorder 受到 AVFoundation AudioPlayer 的影响

标签 ios avfoundation core-audio audiotoolbox

所以,我有以下类(class):

  • 录音机(使用 AudioToolboxCoreAudio )记录音频。
  • 音频播放器(使用 AVFoundation )

  • 录音机捕获音频,将其发送到服务器,然后服务器回复另一个音频,然后播放器播放接收到的音频。

    当我再次尝试调用录音机录制音频时,它没有正确录制音频。

    关于如何重置录音机以便在音频播放器完成播放后它可以正确录音的任何想法?

    我尝试再次初始化录音机(就在录音之前),但这不起作用。

    影响录音机的行是下面的 2 行,不幸的是,需要使用 AVFoundation 播放音频.
    let sharedSession = AVAudioSession.sharedInstance()
    try sharedSession.setCategory(AVAudioSessionCategoryPlayback)
    

    执行流程
  • 录音机音频
  • 向服务器发送和接收音频
  • 从服务器录音机播放音频此时录音机无法正确录音
  • 向服务器发送和接收音频 失败
  • 播放音频失败

  • 先感谢您!


    友情链接 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 做了很多更新。

    对您的播放器的快速修复是删除任何 Audio Session 调用,如下所示:

    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 本书

    关于ios - AudioToolBox Recorder 受到 AVFoundation AudioPlayer 的影响,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41230997/

    相关文章:

    ios - 如何使用 mpmovieplayercontroller 打开 dailymotion 中的视频

    objective-c - Cocoa:将音频附加到现有文件

    swift - AVPlayer 的 addPeriodicTimeObserver 函数未正确执行

    ios - 降低ExtAudioFileRead的输出幅度

    ios - 是否可以通过 Objective c 中的 USB 摄像头套件录制声音?

    iphone - iOS 5 NSNumber 错误

    IOS:如何填充 Nib 内的 UITableView?

    ios - UNCalendarNotificationTrigger 每小时触发器返回错误的触发日期

    ios - 如何从 iOS 中的自定义视频播放器屏幕捕获静止图像?

    audio - 连接节点上的 AVAudioEngine 崩溃