2017-05-25 2 views
2

J'ai joué avec les services de file d'attente audio pendant environ une semaine et j'ai rédigé une version rapide du Guide des services de file d'attente audio Apple. Je suis en enregistrement PCM linéaire et l'enregistrement sur le disque avec cette méthode:Le lecteur de services de file d'attente audio de Swift n'appelle pas de rappel

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

Mon AudioQueueOutputCallback n'est pas appelé, même si je peux vérifier que mon bufferSize est apparemment assez grand et qu'il est passé obtenir des données réelles. Je ne reçois aucune erreur OSStatus et il semble que tout devrait fonctionner. Il y a très peu de choses dans la manière de Swift AudioServiceQueues écrite et si je devais faire fonctionner cela, je serais heureux d'ouvrir le reste de mon code.

Toutes les suggestions sont les bienvenues!

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 
    } 
} 

Mon joueur struct Etat est:

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)? 
} 

Répondre

1

lieu de mise en attente d'un tampon vide, essayez d'appeler votre callback il enqueues plein (espérons-le) tampon. Je ne suis pas sûr de ce qui se passe au niveau du runloop, mais je suis sûr que vous savez ce que vous faites.

+0

Hmm Je ne suis pas sûr de comprendre. Voulez-vous dire après avoir alloué chaque tampon que je devrais appeler le callback au lieu d'appeler 'EnqueueBuffer'? Cela ne veut-il pas dire que cela ne serait appelé que pour le nombre de tampons que j'ai? Je l'ai essayé et ça ne semble rien faire sur AudioQueueStart. D'autres suggestions? Merci! – aBikis

+1

C'est vrai, le callback appelle 'EnqueueBuffer' de toute façon, mais si cela ne vous aide pas, pouvez-vous poster un lien vers un exemple de code de travail? –

+0

Wow vraiment? Ce serait incroyable. J'ai effectivement mis à jour le code source un peu. J'ai été en mesure de jouer de l'audio via le code C, je me suis moqué de ce fichier rapide et je pense que je l'ai mis en file d'attente correctement mais il semble y avoir un problème de lecture à partir des données par paquets. J'ai inclus tous les fichiers pertinents et la ligne problématique est dans la ligne SVNPlayer 30. Merci beaucoup! https://gist.github.com/bikisDesign/57c58355c2cb4498595dab52f0ff0be8 – aBikis