Audio being played over a stream is jittering/stuttering - ios

I will briefly go over all the elements of my app:
I have an application that records audio to an AVAudioPCMBuffer. This buffer is then converted to NSData and then to [UInt8]. It is then streamed over an OutputStream. On another device, this data is received using an InputStream. Then it is converted to NSData, and back to an AVAudioPCMBuffer. This buffer is then played.
The issue is that the audio is very jittery and you can't make out voices, only that the audio gets louder or quieter depending on if the other person is talking.
When scheduling the buffer:
self.peerAudioPlayer.scheduleBuffer(audioBuffer, completionHandler: nil)
I have delayed playing this audio for a few seconds and then played it, hoping that this would make the audio clearer, however it did not help. My best guess is that the buffer I'm creating is somehow cutting off some of the audio. So I will show you my relevant code:
Here is how I record audio:
localInput?.installTap(onBus: 1, bufferSize: 4096, format: localInputFormat) {
(buffer, when) -> Void in
let data = self.audioBufferToNSData(PCMBuffer: buffer)
let output = self.outputStream!.write(data.bytes.assumingMemoryBound(to: UInt8.self), maxLength: data.length)
}
audioBufferToNSData is just a method which converts AVAudioPCMBuffer to NSData and here it is:
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
}
I'm wondering if the issue could be at the method above. Possibly when I calculate the length of the NSData object, maybe I am cutting off part of the audio.
On the receiving end I have this:
case Stream.Event.hasBytesAvailable:
DispatchQueue.global().async {
var tempBuffer: [UInt8] = .init(repeating: 0, count: 17640)
let length = self.inputStream!.read(&tempBuffer, maxLength: tempBuffer.count)
self.testBufferCount += length
self.testBuffer.append(contentsOf: tempBuffer)
if (self.testBufferCount >= 17640) {
let data = NSData.init(bytes: &self.testBuffer, length: self.testBufferCount)
let audioBuffer = self.dataToPCMBuffer(data: data)
self.peerAudioPlayer.scheduleBuffer(audioBuffer, completionHandler: nil)
self.testBuffer.removeAll()
self.testBufferCount = 0
}
}
The reason I check for 17640 is because the data being sent is exactly 17640 bytes, so I need to get all of this data before I play it.
Furthermore, the dataToPCMBuffer method just converts NSData to an AVAudioPCMBuffer so that it can be played. Here is that method:
func dataToPCMBuffer(data: NSData) -> AVAudioPCMBuffer {
let audioFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 44100, 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
}
Thank you in advance!

I think that in audioBufferToNSData you should use frame​Length instead of frameCapacity.
let data = NSData(bytes: channels[0], length:Int(PCMBuffer.frame​Length * PCMBuffer.format.streamDescription.pointee.mBytesPerFrame))
PCMBuffer.frameCapacity -> how much can be stored
PCMBuffer.frame​Length -> how much of PCMBuffer.frameCapacity is actual valid data

Related

I want to record from iphone microphone and convert to ulaw format streaming

I want to record from iphone microphone and convert to ulaw format streaming data,I guess that is pcm data but I got noise.
What audio format is installTap buff? How can I do to got ulaw data format?
I can got it from AVAudioRecorder but I'm not to got a file.
Do change format settings 'AVFormatIDKey=kAudioFormatULaw' will got crash.
func testMicrophoneRecording1 () throws {
let tapNode: AVAudioNode = mixerNode
let format = tapNode.outputFormat(forBus: 0)
tapNode.installTap(onBus: 0, bufferSize: 1024, format: format, block: {
(buffer, time) in
let d = buffer.toNSData() as Data
let ulaw_data = convert_pcm_(to_ulaw: d)
sendUlawDataToDevice(data: ulaw_data)
})
try engine.start()
}
and connections is:
func makeConnections() {
let inputNode = engine.inputNode
let inputFormat = inputNode.outputFormat(forBus: 0)
engine.connect(inputNode, to: mixerNode, format: inputFormat)
let mainMixerNode = engine.mainMixerNode
let mixerFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 44100, channels: 1, interleaved: true)
engine.connect(mixerNode, to: mainMixerNode, format: mixerFormat)
}
I have got pcm from microphone and convert to ulaw,form this example:
https://github.com/Epskampie/ios-coreaudio-example

Converting Audio Samples to .pcmFormatInt16 showing 0s ios swift AVAudioEngine

I am a beginner in working with sounds and AVAudioEngine in IOS, and I'm developing an application that captures the audio samples as buffers and analyzes it. Furthermore, the sample rate must be 8 kHz with an integer16 PCM data, but when I try to record from the inputNode and convert the data to 8 kHz, it shows 0s in the buffer. However, when I set the commonFormat to .pcmFormatFloat32 it works fine.
My Code:
let inputNode = audioEngine.inputNode
let downMixer = AVAudioMixerNode()
let main = audioEngine.mainMixerNode
let format = inputNode.inputFormat(forBus: 0)
let format16KHzMono = AVAudioFormat(commonFormat: AVAudioCommonFormat.pcmFormatInt16, sampleRate: 8000, channels: 1, interleaved: true)
audioEngine.attach(downMixer)
downMixer.installTap(onBus: 0, bufferSize: 640, format: format16KHzMono) { (buffer, time) -> Void in
do{
print(buffer.description)
if let channel1Buffer = buffer.int16ChannelData?[0] {
// print(channel1Buffer[0])
for i in 0 ... Int(buffer.frameLength-1) {
print((channel1Buffer[i])) //prints 0s :(
}
}
}
}
audioEngine.connect(inputNode, to: downMixer, format: format)
audioEngine.connect(downMixer, to: main, format: format16KHzMono)
audioEngine.prepare()
try! audioEngine.start()
Thanks

Swift/iOS - How to compress audio so that it can be streamed to another device, then decompress and play the same audio

I've been using the following methods to convert an AVAudioPCMBuffer to [UInt8] and then [UInt8] back to an AVAudioPCMBuffer. The problem is that every single conversion is a total of 17640 bytes, which to stream over MultipeerConnectivity is a lot. In fact, I think my stream ends up reading data slower than data is coming in, since if I end the stream on one device I can see that the other continues to read data until it realizes that the stream has ended.
Here is my conversion of AVAudioPCMBuffer to [UInt8]. Credit for this answer goes to Rhythmic Fistman from this answer.
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
}
And here is [UInt8] to AVAudioPCMBuffer
func bytesToAudioBuffer(_ buf: [UInt8]) -> AVAudioPCMBuffer {
let fmt = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 8000, channels: 1, interleaved: false)
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
}
Can anyone help me compress this data so that it is easier to send over a stream, and then decompress it so it can be played?

AVAudioEngine to NSData - wav file doesn't play on server

I am trying to record audio using AVAudioEngine. The file gets recorded and plays correctly. However, I also need to send AVAudioPCMBuffer that I receive in the tap handler to my server via socket. I am converting AVAudioPCMBuffer to NSData and sending it. The server is receiving it - however the file doesn't play correctly on the server. Am I missing something while converting AVAudioPCMBuffer to NSData or is my recording missing some configuration.
Any help would be appreciated guys. Thanks!
let audioEngine = AVAudioEngine()
let inputNode = audioEngine.inputNode
let bus = 0
try file = AVAudioFile(forWriting: URLFor("recording.wav")!, settings: audioEngine.inputNode!.inputFormatForBus(0).settings)
inputNode!.installTapOnBus(bus, bufferSize: 4096, format: inputNode!.inputFormatForBus(bus)) {
(buffer: AVAudioPCMBuffer!, time: AVAudioTime!) -> Void in
self.file?.writeFromBuffer(buffer)
self.socketio.send(self.toNSData(buffer))
}
do{
audioEngine.prepare()
try audioEngine.start()
}
catch{
print("catch")
}
func toNSData(PCMBuffer: AVAudioPCMBuffer) -> NSData {
let channelCount = 1 // given PCMBuffer channel count is 1
let channels = UnsafeBufferPointer(start: PCMBuffer.floatChannelData, count: channelCount)
let ch0Data = NSData(bytes: channels[0], length:Int(PCMBuffer.frameCapacity * PCMBuffer.format.streamDescription.memory.mBytesPerFrame))
return ch0Data
}

How to play audio from AVAudioPCMBuffer converted from NSData

I am getting audio PCM 16bit Mono data from udp packets like this:
(void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data
fromAddress:(NSData *)address
withFilterContext:(id)filterContext
{
...
}
I am converting this data into PCM buffer by calling a swift function as below:
func toPCMBuffer(data: NSData) -> AVAudioPCMBuffer {
let audioFormat = AVAudioFormat(commonFormat: AVAudioCommonFormat.PCMFormatFloat32, sampleRate: 8000, channels: 1, interleaved: false) // given NSData audio format
var PCMBuffer = AVAudioPCMBuffer(PCMFormat: audioFormat, frameCapacity:1024*10)
PCMBuffer.frameLength = PCMBuffer.frameCapacity
let channels = UnsafeBufferPointer(start: PCMBuffer.floatChannelData, count: Int(PCMBuffer.format.channelCount))
data.getBytes(UnsafeMutablePointer<Void>(channels[0]) , length: data.length)
return PCMBuffer
}
Data is converted to PCM buffer and i can see its length in logs.
But when i try to play the buffer i hear no voice.
Here is the code for receiving:
func toPCMBuffer(data: NSData) -> AVAudioPCMBuffer {
let audioFormat = AVAudioFormat(commonFormat: AVAudioCommonFormat.PCMFormatFloat32, sampleRate: 8000, channels: 1, interleaved: false) // given NSData audio format
var PCMBuffer = AVAudioPCMBuffer(PCMFormat: audioFormat, frameCapacity:1024*10)
PCMBuffer.frameLength = PCMBuffer.frameCapacity
let channels = UnsafeBufferPointer(start: PCMBuffer.floatChannelData, count: Int(PCMBuffer.format.channelCount))
data.getBytes(UnsafeMutablePointer<Void>(channels[0]) , length: data.length)
var mainMixer = audioEngine.mainMixerNode
audioEngine.attachNode(audioFilePlayer)
audioEngine.connect(audioFilePlayer, to:mainMixer, format: PCMBuffer.format)
audioEngine.startAndReturnError(nil)
audioFilePlayer.play()
audioFilePlayer.scheduleBuffer(PCMBuffer, atTime: nil, options: nil, completionHandler: nil)
return PCMBuffer
}
ended up using an objective-c function:data is getting converted fine
-(AudioBufferList *) getBufferListFromData: (NSData *) data
{
if (data.length > 0)
{
NSUInteger len = [data length];
//NSData *d2 = [data subdataWithRange:NSMakeRange(4, 1028)];
//I guess you can use Byte*, void* or Float32*. I am not sure if that makes any difference.
Byte* byteData = (Byte*) malloc (len);
memcpy (byteData, [data bytes], len);
if (byteData)
{
AudioBufferList * theDataBuffer =(AudioBufferList*)malloc(sizeof(AudioBufferList) * 1);
theDataBuffer->mNumberBuffers = 1;
theDataBuffer->mBuffers[0].mDataByteSize =(UInt32) len;
theDataBuffer->mBuffers[0].mNumberChannels = 1;
theDataBuffer->mBuffers[0].mData = byteData;
// Read the data into an AudioBufferList
return theDataBuffer;
}
}
return nil;
}

Resources