2017-05-25 62 views
2

我一直在使用音頻隊列服務玩了大約一個星期,並且我已經從Apple Audio Queue Services Guide中編寫了一個快速版本。 我記錄的線性PCM和保存到磁盤,用這種方法:Swift中的音頻隊列服務播放器不調用回調

AudioFileCreateWithURL(url, kAudioFileWAVEType, &format, 
              AudioFileFlags.dontPageAlignAudioData.union(.eraseFile), &audioFileID) 

我AudioQueueOutputCallback不會被調用即使我可以確認我的緩衝區大小足夠大似乎和它獲得通過實際的數據。我沒有收到任何OSStatus錯誤,看起來好像一切都應該起作用。 Theres在Swift寫的AudioServiceQueues方面很少,我應該得到這個工作,我很樂意打開我的代碼的其餘部分。

歡迎任何和所有建議!

class SVNPlayer: SVNPlayback { 

    var state: PlayerState! 

    private let callback: AudioQueueOutputCallback = { aqData, inAQ, inBuffer in 

    guard let userData = aqData else { return } 
    let audioPlayer = Unmanaged<SVNPlayer>.fromOpaque(userData).takeUnretainedValue() 

    guard audioPlayer.state.isRunning, 
     let queue = audioPlayer.state.mQueue else { return } 

    var buffer = inBuffer.pointee // dereference pointers 

    var numBytesReadFromFile: UInt32 = 0 
    var numPackets = audioPlayer.state.mNumPacketsToRead 
    var mPacketDescIsNil = audioPlayer.state.mPacketDesc == nil // determine if the packetDesc 

    if mPacketDescIsNil { 
     audioPlayer.state.mPacketDesc = AudioStreamPacketDescription(mStartOffset: 0, mVariableFramesInPacket: 0, mDataByteSize: 0) 
    } 

    AudioFileReadPacketData(audioPlayer.state.mAudioFile, false, &numBytesReadFromFile, // read the packet at the saved file 
     &audioPlayer.state.mPacketDesc!, audioPlayer.state.mCurrentPacket, 
     &numPackets, buffer.mAudioData) 

    if numPackets > 0 { 
     buffer.mAudioDataByteSize = numBytesReadFromFile 
     AudioQueueEnqueueBuffer(queue, inBuffer, mPacketDescIsNil ? numPackets : 0, 
           &audioPlayer.state.mPacketDesc!) 
     audioPlayer.state.mCurrentPacket += Int64(numPackets) 
    } else { 
     AudioQueueStop(queue, false) 
     audioPlayer.state.isRunning = false 
    } 
    } 

    init(inputPath: String, audioFormat: AudioStreamBasicDescription, numberOfBuffers: Int) throws { 
    super.init() 
    var format = audioFormat 
    let pointer = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()) // get an unmananged reference to self 

    guard let audioFileUrl = CFURLCreateFromFileSystemRepresentation(nil, 
                    inputPath, 
                    CFIndex(strlen(inputPath)), false) else { 
                     throw MixerError.playerInputPath } 

    var audioFileID: AudioFileID? 

    try osStatus { AudioFileOpenURL(audioFileUrl, AudioFilePermissions.readPermission, 0, &audioFileID) } 

    guard audioFileID != nil else { throw MixerError.playerInputPath } 

    state = PlayerState(mDataFormat: audioFormat, // setup the player state with mostly initial values 
     mQueue: nil, 
     mAudioFile: audioFileID!, 
     bufferByteSize: 0, 
     mCurrentPacket: 0, 
     mNumPacketsToRead: 0, 
     isRunning: false, 
     mPacketDesc: nil, 
     onError: nil) 

    var dataFormatSize = UInt32(MemoryLayout<AudioStreamBasicDescription>.stride) 

    try osStatus { AudioFileGetProperty(audioFileID!, kAudioFilePropertyDataFormat, &dataFormatSize, &state.mDataFormat) } 

    var queue: AudioQueueRef? 

    try osStatus { AudioQueueNewOutput(&format, callback, pointer, CFRunLoopGetCurrent(), CFRunLoopMode.commonModes.rawValue, 0, &queue) } // setup output queue 

    guard queue != nil else { throw MixerError.playerOutputQueue } 

    state.mQueue = queue // add to playerState 

    var maxPacketSize = UInt32() 
    var propertySize = UInt32(MemoryLayout<UInt32>.stride) 

    try osStatus { AudioFileGetProperty(state.mAudioFile, kAudioFilePropertyPacketSizeUpperBound, &propertySize, &maxPacketSize) } 

    deriveBufferSize(maxPacketSize: maxPacketSize, seconds: 0.5, outBufferSize: &state.bufferByteSize, outNumPacketsToRead: &state.mNumPacketsToRead) 

    let isFormatVBR = state.mDataFormat.mBytesPerPacket == 0 || state.mDataFormat.mFramesPerPacket == 0 

    if isFormatVBR { //Allocating Memory for a Packet Descriptions Array 
     let size = UInt32(MemoryLayout<AudioStreamPacketDescription>.stride) 
     state.mPacketDesc = AudioStreamPacketDescription(mStartOffset: 0, 
                 mVariableFramesInPacket: state.mNumPacketsToRead, 
                 mDataByteSize: size) 


    } // if CBR it stays set to null 

    for _ in 0..<numberOfBuffers { // Allocate and Prime Audio Queue Buffers 
     let bufferRef = UnsafeMutablePointer<AudioQueueBufferRef?>.allocate(capacity: 1) 
     let foo = state.mDataFormat.mBytesPerPacket * 1024/UInt32(numberOfBuffers) 
     try osStatus { AudioQueueAllocateBuffer(state.mQueue!, foo, bufferRef) } // allocate the buffer 

     if let buffer = bufferRef.pointee { 
     AudioQueueEnqueueBuffer(state.mQueue!, buffer, 0, nil) 
     } 
    } 

    let gain: Float32 = 1.0 // Set an Audio Queue’s Playback Gain 
    try osStatus { AudioQueueSetParameter(state.mQueue!, kAudioQueueParam_Volume, gain) } 
    } 

    func start() throws { 
    state.isRunning = true // Start and Run an Audio Queue 
    try osStatus { AudioQueueStart(state.mQueue!, nil) } 
    while state.isRunning { 
     CFRunLoopRunInMode(CFRunLoopMode.defaultMode, 0.25, false) 
    } 
    CFRunLoopRunInMode(CFRunLoopMode.defaultMode, 1.0, false) 
    state.isRunning = false 
    } 

    func stop() throws { 
    guard state.isRunning, 
     let queue = state.mQueue else { return } 
    try osStatus { AudioQueueStop(queue, true) } 
    try osStatus { AudioQueueDispose(queue, true) } 
    try osStatus { AudioFileClose(state.mAudioFile) } 

    state.isRunning = false 
    } 


    private func deriveBufferSize(maxPacketSize: UInt32, seconds: Float64, outBufferSize: inout UInt32, outNumPacketsToRead: inout UInt32){ 
    let maxBufferSize = UInt32(0x50000) 
    let minBufferSize = UInt32(0x4000) 

    if state.mDataFormat.mFramesPerPacket != 0 { 
     let numPacketsForTime: Float64 = state.mDataFormat.mSampleRate/Float64(state.mDataFormat.mFramesPerPacket) * seconds 
     outBufferSize = UInt32(numPacketsForTime) * maxPacketSize 
    } else { 
     outBufferSize = maxBufferSize > maxPacketSize ? maxBufferSize : maxPacketSize 
    } 

    if outBufferSize > maxBufferSize && outBufferSize > maxPacketSize { 
     outBufferSize = maxBufferSize 

    } else if outBufferSize < minBufferSize { 
     outBufferSize = minBufferSize 
    } 

    outNumPacketsToRead = outBufferSize/maxPacketSize 
    } 
} 

我的球員狀態結構是:

struct PlayerState: PlaybackState { 
    var mDataFormat: AudioStreamBasicDescription 
    var mQueue: AudioQueueRef? 
    var mAudioFile: AudioFileID 
    var bufferByteSize: UInt32 
    var mCurrentPacket: Int64 
    var mNumPacketsToRead: UInt32 
    var isRunning: Bool 
    var mPacketDesc: AudioStreamPacketDescription? 
    var onError: ((Error) -> Void)? 
} 

回答

1

而是排隊空緩衝區,試着打電話給你callback所以它入列(希望)全緩衝。我不確定runloop的內容,但我相信你知道你在做什麼。

+0

嗯我不知道我明白。你的意思是在分配每個緩衝區之後,我應該調用回調函數,而不是調用'EnqueueBuffer'?這不就是說,它只會被稱爲我擁有的緩衝區數量嗎?我放棄了它,它似乎沒有在AudioQueueStart上做任何事情。還有其他建議嗎?謝謝! – aBikis

+1

沒錯,回調會調用'EnqueueBuffer',但如果這沒有幫助,你可以發佈一個鏈接到一個有效的代碼示例? –

+0

哇真的嗎?那將是不可思議的。我實際上已經更新了一些源代碼。我已經能夠通過C代碼播放音頻了我一直在嘲笑這個快速文件,我認爲我已經正確排入隊列,但是從分組數據讀取似乎存在問題。我已經包含了所有相關文件,問題行在'SVNPlayer'第30行。非常感謝! https://gist.github.com/bikisDesign/57c58355c2cb4498595dab52f0ff0be8 – aBikis

相關問題