How to decode m4a to PCM using AVFoundation's AVAsetReader - swift

Trying to convert M4A to PCM goes well in the start.
i am able to convert and read the bytes.
however i am not sure if this is the correct way to do this.
as i am getting 16384 bytes when i try to get the bytes in NSData.
Here is my function
func getData(sampleRef:CMSampleBufferRef) -> NSMutableData{
let dataBuffer = CMSampleBufferGetDataBuffer(sampleRef)
let length = CMBlockBufferGetDataLength(dataBuffer!)
var data = NSMutableData(length: length)
CMBlockBufferCopyDataBytes(dataBuffer!, 0, length, data!.mutableBytes)
print(data!)// this prints 16384 bytes
return data!
}
this i try to convert this data to Int16
// 3 lines below i was just testing how it converts to Int16
let count = data!.length / sizeof(Int16)
var array = [Int16](count: count, repeatedValue: 0)
data!.getBytes(&array, length: data!.length)
these are my settings to decode the PCM from
M4A file.
let outputSettings = [
AVFormatIDKey: Int(kAudioFormatLinearPCM),
AVSampleRateKey: 44100,
AVLinearPCMBitDepthKey:16,
AVLinearPCMIsFloatKey:0,
AVNumberOfChannelsKey: 1 as NSNumber,
]
PS. i record the file with the same settings

Related

Convert PCM Buffer to AAC ELD Format and vice versa

I'm having trouble converting a linear PCM buffer to a compressed AAC ELD (Enhanced Low Delay) buffer.
I got some working code for the conversion into ilbc format from this question:
AVAudioCompressedBuffer to UInt8 array and vice versa
This approach worked fine.
I changed the input for the format to this:
let packetCapacity = 8
let maximumPacketSize = 96
lazy var capacity = packetCapacity * maximumPacketSize // 768
let convertedSampleRate: Double = 16000
lazy var aaceldFormat: AVAudioFormat = {
var descriptor = AudioStreamBasicDescription(mSampleRate: convertedSampleRate, mFormatID: kAudioFormatMPEG4AAC_ELD, mFormatFlags: 0, mBytesPerPacket: 0, mFramesPerPacket: 0, mBytesPerFrame: 0, mChannelsPerFrame: 1, mBitsPerChannel: 0, mReserved: 0)
return AVAudioFormat(streamDescription: &descriptor)!
}()
The conversion to a compressed buffer worked fine and I was able to convert the buffer to a UInt8 Array.
However, the conversion back to a PCM Buffer didn't work. The input block for the conversion back to a buffer looks like this:
func convertToBuffer(uints: [UInt8], outcomeSampleRate: Double) -> AVAudioPCMBuffer? {
// Convert to buffer
let compressedBuffer: AVAudioCompressedBuffer = AVAudioCompressedBuffer(format: aaceldFormat, packetCapacity: AVAudioPacketCount(packetCapacity), maximumPacketSize: maximumPacketSize)
compressedBuffer.byteLength = UInt32(capacity)
compressedBuffer.packetCount = AVAudioPacketCount(packetCapacity)
var compressedBytes = uints
compressedBytes.withUnsafeMutableBufferPointer {
compressedBuffer.data.copyMemory(from: $0.baseAddress!, byteCount: capacity)
}
guard let audioFormat = AVAudioFormat(
commonFormat: AVAudioCommonFormat.pcmFormatFloat32,
sampleRate: outcomeSampleRate,
channels: 1,
interleaved: false
) else { return nil }
guard let uncompressor = getUncompressingConverter(outputFormat: audioFormat) else { return nil }
var newBufferAvailable = true
let inputBlock : AVAudioConverterInputBlock = {
inNumPackets, outStatus in
if newBufferAvailable {
outStatus.pointee = .haveData
newBufferAvailable = false
return compressedBuffer
} else {
outStatus.pointee = .noDataNow
return nil
}
}
guard let uncompressedBuffer: AVAudioPCMBuffer = AVAudioPCMBuffer(pcmFormat: audioFormat, frameCapacity: AVAudioFrameCount((audioFormat.sampleRate / 10))) else { return nil }
var conversionError: NSError?
uncompressor.convert(to: uncompressedBuffer, error: &conversionError, withInputFrom: inputBlock)
if let err = conversionError {
print("couldnt decompress compressed buffer", err)
}
return uncompressedBuffer
}
The error block after the convert method triggers and prints out "too few bits left in input buffer". Also, it seems like the input block only gets called once.
I've tried different codes and this seems to be one of the most common outcomes. I'm also not sure if the problem is in the initial conversion from the pcm buffer to uint8 array although I get an UInt8 Array filled with 768 values every 0.1 seconds (Sometimes the array contains a few zeros at the end, which doesn't happen in ilbc format.
Questions:
1. Is the initial conversion from pcm buffer to uint8 array done with the right approach? Are the packetCapacity, capacity and maximumPacketSize valid? -> Again, seems to work
2. Am I missing something at the conversion back to pcm buffer? Also, am I using the variables in the right way?
3. Has anyone achieved this conversion without using C in the project?
** EDIT: ** I also worked with the approach from this post:
Decode AAC to PCM format using AVAudioConverter Swift
It works fine with AAC format, but not with AAC_LD or AAC_ELD

Read large file of binary data in chunks of 1024 bytes

I'm trying to read an MP4 file in chunks of 1024 bytes. I've made a code that - almost - works. I'm doing the following:
let audioFilePath = Bundle.main.path(forResource: "video", ofType: "mp4")!
var chunks = [[UInt8]]()
if let stream: InputStream = InputStream(fileAtPath: audioFilePath) {
var buf: [UInt8] = [UInt8](repeating: 0, count: 1024)
stream.open()
while stream.hasBytesAvailable {
stream.read(&buf, maxLength: 1024)
chunks.append(buf)
}
stream.close()
}
print(chunks.count)
The problem with the code above is that I'm reading an MP4 file of size 15.948.514 bytes. It means that it should finish in exactly 15.574 chunks (the last chunk may have less than 1024, but this is not a problem), but the code prints 15.576 chunks, and all of them of size 1024. What is wrong with the code above?
hasBytesAvailable can also return true if a read must be attempted in order to determine the availability of bytes. That is what happens in your case: The final read returns zero for “end of file.”
hasBytesAvailable can be useful with input streams like TCP sockets to avoid a blocking read(), but is not really needed for reading from files. In any case, you must check the return value of read() which can be zero (end of file) or -1 (read error) or the actual number of bytes read into the buffer (which can be less than the number of bytes requested).
Note also that you always append a chunk with 1024 bytes to the chunks array, even if the buffer is only partially filled with bytes from the input stream.
if let stream = InputStream(fileAtPath: audioFilePath) {
var buf = [UInt8](repeating: 0, count: 1024)
stream.open()
while case let amount = stream.read(&buf, maxLength: 1024), amount > 0 {
// print(amount)
chunks.append(Array(buf[..<amount]))
}
stream.close()
}

Swift NSData getBytes reversed

I made an app communicating with a device with Bluetooth Low Energy.
Basically, my app and this device have their own message syntax. They exchange data as bytes and each values in thoses data are reversed.
My problem is that after reversing back value, when I'm converting a 3 bytes value to an Int32, the NSData.getBytes function seems to reverse the value, so I have a wrong value. Example:
var value; // containing [ 0x01, 0xD3, 0x00 ]
value = value.reverse(); // Reverse back : [ 0x00, 0xD3, 0x01 ]
let numb = value.getUInt32(); // Numb will be 119552, instead of 54017...
I don't know if I'm clear enough on my problem, but here is my code. A function which reverse back data and then tries to convert data to int.
// Those functions are in an extension of NSData
func getRUInt32(range:NSRange) -> UInt32
{
var data = message.subdataWithRange(range); // Extract data from main message
data = data.reverse(); // Reverse back data
return data.getUInt32(); // Convert to UInt32
}
func getUInt32() -> UInt32
{
var value:Int32 = 0;
getBytes(&value, length: self.length);
return UInt32(value);
}
func reverse() -> NSData
{
let count:Int = length / sizeof(UInt8);
var array = [UInt8](count: count, repeatedValue: 0);
getBytes(&array, length: count * sizeof(UInt8));
var reversedArray = [UInt8](count: count, repeatedValue: 0);
for index in 0..<array.count
{
reversedArray[index] = array[array.count - index - 1];
}
return NSData(bytes: reversedArray, length: reversedArray.count);
}
You should have a look at the byte order utilities reference:
https://developer.apple.com/library/ios/documentation/CoreFoundation/Reference/CFByteOrderUtils/index.html#//apple_ref/c/func/CFConvertDoubleHostToSwapped
You identify the native format of the current platform using the
CFByteOrderGetCurrent function. Use functions such as
CFSwapInt32BigToHost and CFConvertFloat32HostToSwapped to convert
values between different byte order formats.

NSData to [Uint8] in Swift

I couldn't find a solution to this problem in Swift (all of them are Objective-C, and they deal with pointers which I don't think exist in Swift in the same form). Is there any way to convert a NSData object into an array of bytes in the form of [Uint8] in Swift?
You can avoid first initialising the array to placeholder values, if you go through pointers in a slightly convoluted manner, or via the new Array constructor introduced in Swift 3:
Swift 3
let data = "foo".data(using: .utf8)!
// new constructor:
let array = [UInt8](data)
// …or old style through pointers:
let array = data.withUnsafeBytes {
[UInt8](UnsafeBufferPointer(start: $0, count: data.count))
}
Swift 2
Array(UnsafeBufferPointer(start: UnsafePointer<UInt8>(data.bytes), count: data.length))
Swift 5 Solution
Data to [bytes]
extension Data {
var bytes: [UInt8] {
return [UInt8](self)
}
}
[bytes] to Data
extension Array where Element == UInt8 {
var data: Data {
return Data(self)
}
}
It's funny but exist more simple solution. Works in Swift 3. Surely. I've used this today.
data: Data // as function parameter
let byteArray = [UInt8](data)
That's all! :)
NSData easily bridged to Data.
UPDATE: (due to Andrew Koster comment)
Swift 4.1, Xcode 9.3.1
Just has been rechecked - all works as expected.
if let nsData = NSData(base64Encoded: "VGVzdFN0cmluZw==", options: .ignoreUnknownCharacters) {
let bytes = [UInt8](nsData as Data)
print(bytes, String(bytes: bytes, encoding: .utf8))
Output: [84, 101, 115, 116, 83, 116, 114, 105, 110, 103] Optional("TestString")
You can use the getBytes function of NSData to get the byte array equivalent.
As you did not provide any source code, I will use a Swift String contents that has been converted to NSData.
var string = "Hello World"
let data : NSData! = string.dataUsingEncoding(NSUTF8StringEncoding)
let count = data.length / sizeof(UInt8)
// create an array of Uint8
var array = [UInt8](count: count, repeatedValue: 0)
// copy bytes into array
data.getBytes(&array, length:count * sizeof(UInt8))
println(array)
Swift 3/4
let count = data.length / MemoryLayout<UInt8>.size
// create an array of Uint8
var byteArray = [UInt8](repeating: 0, count: count)
// copy bytes into array
data.getBytes(&byteArray, length:count)
Swift 3/4
let data = Data(bytes: [0x01, 0x02, 0x03])
let byteArray: [UInt8] = data.map { $0 }
You can try
extension Data {
func toByteArray() -> [UInt8]? {
var byteData = [UInt8](repeating:0, count: self.count)
self.copyBytes(to: &byteData, count: self.count)
return byteData
}
}
swift 4 and image data to a byte array.
func getArrayOfBytesFromImage(imageData:Data) ->[UInt8]{
let count = imageData.count / MemoryLayout<UInt8>.size
var byteArray = [UInt8](repeating: 0, count: count)
imageData.copyBytes(to: &byteArray, count:count)
return byteArray
}

Read a WAV file and convert it to an array of amplitudes in Swift

I have followed a very good tutorial on udacity to explore the basis of audio applications with Swift. I would like to extend its current functionalities, starting with displaying the waveform of the WAV file. For that purpose, I would need to retrieve the amplitude versus sample from the WAV file. How could I proceed in swift, given that I have a recorded file already?
Thank you!
AudioToolBox meets you need.
You can use AudioFileService to get the audio samples from the audio file, such as the WAV file,
Then you can get the amplitude from every sample.
// this is your desired amplitude data
public internal(set) var packetsX = [Data]()
public required init(src path: URL) throws {
Utility.check(error: AudioFileOpenURL(path as CFURL, .readPermission, 0, &playbackFile) , // set on output to the AudioFileID
operation: "AudioFileOpenURL failed")
guard let file = playbackFile else {
return
}
var numPacketsToRead: UInt32 = 0
GetPropertyValue(val: &numPacketsToRead, file: file, prop: kAudioFilePropertyAudioDataPacketCount)
var asbdFormat = AudioStreamBasicDescription()
GetPropertyValue(val: &asbdFormat, file: file, prop: kAudioFilePropertyDataFormat)
dataFormatD = AVAudioFormat(streamDescription: &asbdFormat)
/// At this point we should definitely have a data format
var bytesRead: UInt32 = 0
GetPropertyValue(val: &bytesRead, file: file, prop: kAudioFilePropertyAudioDataByteCount)
guard let dataFormat = dataFormatD else {
return
}
let format = dataFormat.streamDescription.pointee
let bytesPerPacket = Int(format.mBytesPerPacket)
for i in 0 ..< Int(numPacketsToRead) {
var packetSize = UInt32(bytesPerPacket)
let packetStart = Int64(i * bytesPerPacket)
let dataPt: UnsafeMutableRawPointer = malloc(MemoryLayout<UInt8>.size * bytesPerPacket)
AudioFileReadBytes(file, false, packetStart, &packetSize, dataPt)
let startPt = dataPt.bindMemory(to: UInt8.self, capacity: bytesPerPacket)
let buffer = UnsafeBufferPointer(start: startPt, count: bytesPerPacket)
let array = Array(buffer)
packetsX.append(Data(array))
}
}
For example , the WAV file has channel one 、bit depth of Int16 .
// buffer is of two Int8, to express an Int16
let buffer = UnsafeBufferPointer(start: startPt, count: bytesPerPacket)
more information , you can check my github repo