You can view this project on github here: https://github.com/Lkember/MotoIntercom/
The class that is of importance is PhoneViewController.swift
I have an AVAudioPCMBuffer. The buffer is then converted to NSData using this function:
func audioBufferToNSData(PCMBuffer: AVAudioPCMBuffer) -> NSData {
let channelCount = 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
}
This data needs to be converted to UnsafePointer< UInt8 > according to the documentation on OutputStream.write.
https://developer.apple.com/reference/foundation/outputstream/1410720-write
This is what I have so far:
let data = self.audioBufferToNSData(PCMBuffer: buffer)
let output = self.outputStream!.write(UnsafePointer<UInt8>(data.bytes.assumingMemoryBound(to: UInt8.self)), maxLength: data.length)
When this data is received, it is converted back to an AVAudioPCMBuffer using this method:
func dataToPCMBuffer(data: NSData) -> AVAudioPCMBuffer {
let audioFormat = AVAudioFormat(commonFormat: AVAudioCommonFormat.pcmFormatFloat32, sampleRate: 8000, channels: 1, interleaved: false) // given NSData audio format
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
}
Unfortunately, when I play this audioBuffer, I only hear static. I don't believe that it is an issue with my conversion from AVAudioPCMBuffer to NSData or my conversion from NSData back to AVAudioPCMBuffer. I imagine it is the way that I am writing NSData to the stream.
The reason I don't believe that it is my conversion is because I have created a sample project located here (which you can download and try) that records audio to an AVAudioPCMBuffer, converts it to NSData, converts the NSData back to AVAudioPCMBuffer and plays the audio. In this case there are no problems playing the audio.
EDIT:
I never showed how I actually get Data from the stream as well. Here is how it's done:
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
switch (eventCode) {
case Stream.Event.hasBytesAvailable:
DispatchQueue.global().async {
var buffer = [UInt8](repeating: 0, count: 8192)
let length = self.inputStream!.read(&buffer, maxLength: buffer.count)
let data = NSData.init(bytes: buffer, length: buffer.count)
print("\(#file) > \(#function) > \(length) bytes read on queue \(self.currentQueueName()!) buffer.count \(data.length)")
if (length > 0) {
let audioBuffer = self.dataToPCMBuffer(data: data)
self.audioPlayerQueue.async {
self.peerAudioPlayer.scheduleBuffer(audioBuffer)
if (!self.peerAudioPlayer.isPlaying && self.localAudioEngine.isRunning) {
self.peerAudioPlayer.play()
}
}
}
else if (length == 0) {
print("\(#file) > \(#function) > Reached end of stream")
}
}
Once I have this data, I use the dataToPCMBuffer method to convert it to an AVAudioPCMBuffer.
EDIT 1:
Here is the AVAudioFormat's that I use:
self.localInputFormat = AVAudioFormat.init(commonFormat: .pcmFormatFloat32, sampleRate: 44100, channels: 1, interleaved: false)
Originally, I was using this:
self.localInputFormat = self.localInput?.inputFormat(forBus: 0)
However, if the channel count does not equal the expected channel count, than I was getting crashes. So I switched it to the above.
The actual AVAudioPCMBuffer I'm using is in the installTap method (where localInput is an AVAudioInputNode):
localInput?.installTap(onBus: 0, bufferSize: 4096, format: localInputFormat) {
(buffer, time) -> Void in
Pretty sure you want to replace this:
With
Also, I am not 100% sure that the random blocks of data will always be ok to use to make the audio buffers. You might need to collect up the data first into a bigger block of NSData.
Right now, since you always pass in blocks of 8192 (even if you read less), the buffer creation probably always succeeds. It might not now.