如何转换音频以便可以跨设备进行流式传输

时间:2017-03-15 17:58:38

标签: ios xcode audio stream type-conversion

我一直在寻找这个问题的答案大约一个月了,所以对任何帮助表示赞赏!

我正在使用AVAudioEngine来录制音频。使用水龙头录制此音频:

localInput?.installTap(onBus: 0, bufferSize: 4096, format: localInputFormat) {

记录为AVAudioPCMBuffer类型。它需要转换为[UInt8]类型

我使用这种方法:

func audioBufferToBytes(audioBuffer: AVAudioPCMBuffer) -> [UInt8] {
    let srcLeft = audioBuffer.floatChannelData![0]
    let bytesPerFrame = audioBuffer.format.streamDescription.pointee.mBytesPerFrame
    let numBytes = Int(bytesPerFrame * audioBuffer.frameLength)

    // initialize bytes by 0 
    var audioByteArray = [UInt8](repeating: 0, count: numBytes)

    srcLeft.withMemoryRebound(to: UInt8.self, capacity: numBytes) { srcByteData in
        audioByteArray.withUnsafeMutableBufferPointer {
            $0.baseAddress!.initialize(from: srcByteData, count: numBytes)
        }
    }

    return audioByteArray
}

然后将音频写入输出流。在另一台设备上,数据需要转换回AVAudioPCMBuffer才能播放。我用这个方法:

func bytesToAudioBuffer(_ buf: [UInt8]) -> AVAudioPCMBuffer {

    let fmt = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 44100, channels: 1, interleaved: true)
    let frameLength = UInt32(buf.count) / fmt.streamDescription.pointee.mBytesPerFrame

    let audioBuffer = AVAudioPCMBuffer(pcmFormat: fmt, frameCapacity: frameLength)
    audioBuffer.frameLength = frameLength

    let dstLeft = audioBuffer.floatChannelData![0]

    buf.withUnsafeBufferPointer {
        let src = UnsafeRawPointer($0.baseAddress!).bindMemory(to: Float.self, capacity: Int(frameLength))
        dstLeft.initialize(from: src, count: Int(frameLength))
    }

    return audioBuffer
}

然而,我的逻辑肯定有问题,因为在设备上,当我播放音频时,我确实听到了一些声音,但它听起来像是静态的。

正如我所说,任何帮助都表示赞赏,我已经暂时停留在这个问题上了一段时间。

修改

感谢您的帮助。我已切换到使用数据。所以我的转换看起来像这样(我在网上找到了这个代码):

func audioBufferToData(audioBuffer: AVAudioPCMBuffer) -> Data {
    let channelCount = 1
    let bufferLength = (audioBuffer.frameCapacity * audioBuffer.format.streamDescription.pointee.mBytesPerFrame)

    let channels = UnsafeBufferPointer(start: audioBuffer.floatChannelData, count: channelCount)
    let data = Data(bytes: channels[0], count: Int(bufferLength))

    return data
}

转换回AVAudioPCMBuffer的转换如下:

func dataToAudioBuffer(data: Data) -> AVAudioPCMBuffer {
    let audioFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 8000, channels: 1, interleaved: false)
    let audioBuffer = AVAudioPCMBuffer(pcmFormat: audioFormat, frameCapacity: UInt32(data.count)/2)
    audioBuffer.frameLength = audioBuffer.frameCapacity
    for i in 0..<data.count/2 {
        audioBuffer.floatChannelData?.pointee[i] = Float(Int16(data[i*2+1]) << 8 | Int16(data[i*2]))/Float(INT16_MAX)
    }

    return audioBuffer
}

不幸的是,同样的问题仍然存在......

编辑2

我已经创建了一个模拟此问题的项目。它所做的就是录制音频,将其转换为数据,将其转换回AVAudioPCMBuffer,然后播放音频。

这是链接: https://github.com/Lkember/IntercomTest

编辑3

使用带有2个频道的设备时发生了崩溃,但我已修复它。

编辑4

提交的答案修复了我的示例项目中的问题,但它没有解决我的主项目中的问题。我在这里添加了一个新问题:

How to send NSData over an OutputStream

3 个答案:

答案 0 :(得分:1)

免责声明:好的,这完全基于苹果文档中的理论 - 我以前没有这样做,你的代码也没有足够的信息,以便理解你想要完成的整个事情。

首先,您要尝试将.floatChannelData转换为Uint8,根据文档集

  

通过将给定的浮点值舍入为零来创建新实例。

这会导致数组填充可能错误或更糟,空值(空,如零)。

据我了解,.withMemoryRebound NOT 允许您将浮点数作为整数访问。隐式转换削减数字,因此应该扭曲您的结果。这不是你想要的。

相反,您应该使用Audio Converter Services (documentation)

将您的浮点audioBuffer 安全和无损转换为整数audioBuffer。

我认为这应该指向正确的方向。 在开始转换之前,您还应该检查AVAudioPCMBuffer的格式。处理可能取决于案例。

我希望我能提供帮助。

答案 1 :(得分:0)

结帐https://www.iis.fraunhofer.de/en/ff/amm/dl/whitepapers.html 在这里使用信息我做了非常相似的事情。有一个详细的PDF和一些示例代码可以帮助您入门。

答案 2 :(得分:0)

你走了:

func audioBufferToNSData(PCMBuffer: AVAudioPCMBuffer) -> NSData {
    let channelCount = 1  // given PCMBuffer channel count is 1
    let channels = UnsafeBufferPointer(start: PCMBuffer.floatChannelData, count: channelCount)
    let data = NSData(bytes: channels[0], length:Int(PCMBuffer.frameCapacity * PCMBuffer.format.streamDescription.pointee.mBytesPerFrame))
    return data
}

func dataToAudioBuffer(data: NSData) -> AVAudioPCMBuffer {
    let audioFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 44100, channels: 1, interleaved: false)
    let audioBuffer = AVAudioPCMBuffer(pcmFormat: audioFormat, frameCapacity: UInt32(data.length) / audioFormat.streamDescription.pointee.mBytesPerFrame)
    audioBuffer.frameLength = audioBuffer.frameCapacity
    let channels = UnsafeBufferPointer(start: audioBuffer.floatChannelData, count: Int(audioBuffer.format.channelCount))
    data.getBytes(UnsafeMutableRawPointer(channels[0]) , length: data.length)
    return audioBuffer
}