Understanding AudioStreamBasicDescription - swift

I'm trying to understand the AudioStreamBasicDescription results. Practically non of what I can get makes sense for me. For example:
AudioStreamBasicDescription(mSampleRate: 44100.0, mFormatID: 1819304813, mFormatFlags: 41, mBytesPerPacket: 4, mFramesPerPacket: 1, mBytesPerFrame: 4, mChannelsPerFrame: 2, mBitsPerChannel: 32, mReserved: 0)
What I would expect:
"Bytes per packet" and "bytes per frame" should be 8 not 4:
4 (size of 32 bit Float) x 2 (two channels per frame) x 1 (1 frame per packet) = 8 bytes
Why is it 4?
import CoreAudio
import AudioUnit
var inputUnitDescription = AudioComponentDescription(componentType: kAudioUnitType_Output,
componentSubType: kAudioUnitSubType_HALOutput,
componentManufacturer: kAudioUnitManufacturer_Apple,
componentFlags: 0,
componentFlagsMask: 0)
let defaultInput = AudioComponentFindNext(nil, &inputUnitDescription)
var inputUnit: AudioUnit?
AudioComponentInstanceNew(defaultInput!, &inputUnit)
var asbd = AudioStreamBasicDescription()
var propertySize = UInt32(MemoryLayout<AudioStreamBasicDescription>.size)
AudioUnitGetProperty(inputUnit!,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
1,
&asbd,
&propertySize)
dump(asbd)

Your ABSD has mFormatFlags == 41 .
if (mFormatFlags & 32) != 0 , that means the format includes the kAudioFormatFlagIsNonInterleaved bit.
A non-interleaved format only returns one channel of data per frame, not 2.
Instead you get multiple buffers, each buffer with only one channel per frame, or 4 bytes (for Float32 format), not 8.

Related

Decode Ble data raw flutter

I'm developing a flutter app using the flutter_blue library to interface a BlueNRG-tile from STMicroelectronics. I'm receiving the the raw data from the desired caracteristics then i'm note able ble to convert them to string using the utf8.decode() function.
This is the received data as a list and the issue.
I/flutter (32277): Teste conversion : [121, 85, 0, 0, 209, 133, 1, 0, 5, 10, 237, 0, 0, 0]
E/flutter (32277): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: FormatException: Missing extension byte (at offset 11).
the code from the in the st board:
tBleStatus Environmental_Update(int32_t Press,int32_t Press2,uint16_t Hum, int16_t Temp,int16_t Temp2) {
uint8_t BuffPos = 0;
STORE_LE_16(buff, (getTimestamp()));
BuffPos = 2;
STORE_LE_32(buff + BuffPos, Press);
BuffPos += 4;
STORE_LE_16(buff + BuffPos, Hum);
BuffPos += 2;
STORE_LE_16(buff + BuffPos, Temp);
BuffPos += 2;
STORE_LE_16(buff + BuffPos, Temp2);
return aci_gatt_update_char_value(HWServW2STHandle, EnvironmentalCharHandle, 0, EnvironmentalCharSize, buff);
}
Environmental_Update(PressToSend,PressToSend2, HumToSend, TempToSend,TempToSend2);
Thank You.
You are not able to convert your RAW data to string because you are not sending it as string but in form of bytes.
Take your temperature for example:
You receive the temperature as int16_t, a 16-bit number storing values from –32768 to 32767. This number needs two bytes to be stored, that's why you used BuffPos += 2; and increased the position by 2 bytes.
You need to extract the values from your received array the same way, bytewise. Have a look at this example:
import 'dart:typed_data';
int fromBytesToInt16(int b1, int b0) {
final int8List = new Int8List(2)
..[1] = b1
..[0] = b0;
return ByteData.sublistView(int8List).getInt16(0);
}
void main() {
var received = [121, 85, 0, 0, 209, 133, 1, 0, 5, 10, 237, 0, 0, 0];
var temp = fromBytesToInt16(received[8], received[9]) / 100;
print('temperature: $temp');
}
The temperature was stored as a int16 at index 8 and 9 so I converted it the same way. This results in a temp value of 2565, which divided by 100 would give a pretty nice temperature of 25.65 degree

Convert UInt32 to 4 bytes Swift

How do I convert a UInt32 value to 4 bytes in swift?
I have a value of (3) when I get;
IPP_ORIENTATION.PORTRAIT.rawValue
Now, I need to convert that value into 4 bytes.
Thanks.
let value: UInt32 = 1
var u32LE = value.littleEndian // or simply value
let dataLE = Data(bytes: &u32LE, count: 4)
let bytesLE = Array(dataLE) // [1, 0, 0, 0]
var u32BE = value.bigEndian
let dataBE = Data(bytes: &u32BE, count: 4)
let bytesBE = Array(dataBE) // [0, 0, 0, 1]

Without debug mode UnsafePointer withMemoryRebound will gives wrong value

Here I am trying to concatenate 5 bytes into the single Integer value, I am getting an issue with UnsafePointer withMemoryRebound method.
when I am debugging and checking logs it will gives the correct value. But when I try without debug, it will give the wrong value.(4 out of 5 times wrong value). I got confuses on this API. Is it correct way I am using?
case 1:
let data = [UInt8](rowData) // rowData is type of Data class
let totalKM_BitsArray = [data[8],data[7],data[6],data[5],data[4]]
self.totalKm = UnsafePointer(totalKM_BitsArray).withMemoryRebound(to:UInt64.self, capacity: 1) {$0.pointee}
case 2:
Below code will work for both Enable or Disable debug mode And gives the correct value.
let byte0 : UInt64 = UInt64(data[4])<<64
let byte1 : UInt64 = UInt64(data[5])<<32
let byte2 : UInt64 = UInt64(data[6])<<16
let byte3 : UInt64 = UInt64(data[7])<<8
let byte4 : UInt64 = UInt64(data[8])
self.totalKm = byte0 | byte1 | byte2 | byte3 | byte4
Please suggest me UnsafePointer way of using? Why will this issue come?
Addtional infomation :
let totalKm : UInt64
let data = [UInt8](rowData) // data contain [100, 200, 28, 155, 0, 0, 0, 26, 244, 0, 0, 0, 45, 69, 0, 0, 0, 4, 246]
let totalKM_BitsArray = [data[8],data[7],data[6],data[5],data[4]] // contain [ 244,26,0,0,0]
self.totalKm = UnsafePointer(totalKM_BitsArray).withMemoryRebound(to:UInt64.self, capacity: 1) {$0.pointee}
// when print log gives correct value, when run on device give wrong 3544649566089386 like this.
self.totalKm = byte0 | byte1 | byte2 | byte3 | byte4
// output is 6900 This is correct as expected
There are a few problems with this approach:
let data = [UInt8](rowData) // rowData is type of Data class
let totalKM_BitsArray = [data[8], data[7], data[6], data[5], data[4]]
self.totalKm = UnsafePointer(totalKM_BitsArray)
.withMemoryRebound(to:UInt64.self, capacity: 1) { $0.pointee }
Dereferencing UnsafePointer(totalKM_BitsArray) is undefined behaviour, as the pointer to totalKM_BitsArray's buffer is only temporarily valid for the duration of the initialiser call (hopefully at some point in the future Swift will warn on such constructs).
You're trying to bind only 5 instances of UInt8 to UInt64, so the remaining 3 instances will be garbage.
You can only withMemoryRebound(_:) between types of the same size and stride; which is not the case for UInt8 and UInt64.
It's dependant on the endianness of your platform; data[8] will be the least significant byte on a little endian platform, but the most significant byte on a big endian platform.
Your implementation with bit shifting avoids all of these problems (and is generally the safer way to go as you don't have to consider things like layout compatibility, alignment, and pointer aliasing).
However, assuming that you just wanted to pad out your data with zeroes for the most significant bytes, with rowData[4] to rowData[8] making up the rest of the less significant bytes, then you'll want your bit-shifting implementation to look like this:
let rowData = Data([
100, 200, 28, 155, 0, 0, 0, 26, 244, 0, 0, 0, 45, 69, 0, 0, 0, 4, 246
])
let byte0 = UInt64(rowData[4]) << 32
let byte1 = UInt64(rowData[5]) << 24
let byte2 = UInt64(rowData[6]) << 16
let byte3 = UInt64(rowData[7]) << 8
let byte4 = UInt64(rowData[8])
let totalKm = byte0 | byte1 | byte2 | byte3 | byte4
print(totalKm) // 6900
or, iteratively:
var totalKm: UInt64 = 0
for byte in rowData[4 ... 8] {
totalKm = (totalKm << 8) | UInt64(byte)
}
print(totalKm) // 6900
or, using reduce(_:_:):
let totalKm = rowData[4 ... 8].reduce(0 as UInt64) { accum, byte in
(accum << 8) | UInt64(byte)
}
print(totalKm) // 6900
We can even abstract this into an extension on Data in order to make it easier to load such fixed width integers:
enum Endianness {
case big, little
}
extension Data {
/// Loads the type `I` from the buffer. If there aren't enough bytes to
/// represent `I`, the most significant bits are padded with zeros.
func load<I : FixedWidthInteger>(
fromByteOffset offset: Int = 0, as type: I.Type, endianness: Endianness = .big
) -> I {
let (wholeBytes, spareBits) = I.bitWidth.quotientAndRemainder(dividingBy: 8)
let bytesToRead = Swift.min(count, spareBits == 0 ? wholeBytes : wholeBytes + 1)
let range = startIndex + offset ..< startIndex + offset + bytesToRead
let bytes: Data
switch endianness {
case .big:
bytes = self[range]
case .little:
bytes = Data(self[range].reversed())
}
return bytes.reduce(0) { accum, byte in
(accum << 8) | I(byte)
}
}
}
We're doing a bit of extra work here in order to we read the right number of bytes, as well as being able to handle both big and little endian. But now that we've written it, we can simply write:
let totalKm = rowData[4 ... 8].load(as: UInt64.self)
print(totalKm) // 6900
Note that so far I've assumed that the Data you're getting is zero-indexed. This is safe for the above examples, but isn't necessarily safe depending on where the data is coming from (as it could be a slice). You should be able to do Data(someUnknownDataValue) in order to get a zero-indexed data value that you can work with, although unfortunately I don't believe there's any documentation that guarantees this.
In order to ensure you're correctly indexing an arbitrary Data value, you can define the following extension in order to perform the correct offsetting in the case where you're dealing with a slice:
extension Data {
subscript(offset offset: Int) -> Element {
get { return self[startIndex + offset] }
set { self[startIndex + offset] = newValue }
}
subscript<R : RangeExpression>(
offset range: R
) -> SubSequence where R.Bound == Index {
get {
let concreteRange = range.relative(to: self)
return self[startIndex + concreteRange.lowerBound ..<
startIndex + concreteRange.upperBound]
}
set {
let concreteRange = range.relative(to: self)
self[startIndex + concreteRange.lowerBound ..<
startIndex + concreteRange.upperBound] = newValue
}
}
}
Which you can use then call as e.g data[offset: 4] or data[offset: 4 ... 8].load(as: UInt64.self).
Finally it's worth noting that while you could probably implement this as a re-interpretation of bits by using Data's withUnsafeBytes(_:) method:
let rowData = Data([
100, 200, 28, 155, 0, 0, 0, 26, 244, 0, 0, 0, 45, 69, 0, 0, 0, 4, 246
])
let kmData = Data([0, 0, 0] + rowData[4 ... 8])
let totalKm = kmData.withUnsafeBytes { buffer in
UInt64(bigEndian: buffer.load(as: UInt64.self))
}
print(totalKm) // 6900
This is relying on Data's buffer being 64-bit aligned, which isn't guaranteed. You'll get a runtime error for attempting to load a misaligned value, for example:
let data = Data([0x01, 0x02, 0x03])
let i = data[1...].withUnsafeBytes { buffer in
buffer.load(as: UInt16.self) // Fatal error: load from misaligned raw pointer
}
By loading individual UInt8 values instead and performing bit shifting, we can avoid such alignment issues (however if/when UnsafeMutableRawPointer supports unaligned loads, this will no longer be an issue).

Allocate an AudioBufferList with two buffers for stereo audio stream

I have the following code in C that allocate an AudioBufferList with the appropriate length.
UInt32 bufferSizeBytes = bufferSizeFrames * sizeof(Float32);
propertySize = offsetof(AudioBufferList, mBuffers[0]) + (sizeof(AudioBuffer) * mRecordSBD.mChannelsPerFrame);
mBufferList = (AudioBufferList *) malloc(propertySize);
mBufferList->mNumberBuffers = mRecordSBD.mChannelsPerFrame;
for(UInt32 i = 0; i < mBufferList->mNumberBuffers; ++i)
{
mBufferList->mBuffers[i].mNumberChannels = 1;
mBufferList->mBuffers[i].mDataByteSize = bufferSizeBytes;
mBufferList->mBuffers[i].mData = malloc(bufferSizeBytes);
}
Most of the time, mChannelsPerFrame is 2, so the above code creates two buffers, one for each channel. Each buffer has a reserved memory worths bufferSizeBytes.
How can I replicate the same behaviour in Swift?
Unfortunately in Swift, a C array is treated as a tuple, so AudioBufferList.mBuffers is imported a tuple with a single AudioBuffer. C lets you just access the neighboring memory by using pointer math (or array subscript in this case) in order to create a variable length struct, Swift does not.
AudioBufferList just plain doesn't translate to Swift very well. Apple have mitigated this issue with a few helper functions and types. They created a static allocate function that returns an UnsafeMutableAudioBufferListPointer which is a special type where subscript returns the audioBuffers.
let bufferSizeBytes = MemoryLayout<Float>.size * 1234
var bufferlist = AudioBufferList.allocate(maximumBuffers: 2)
bufferlist[0] = AudioBuffer(mNumberChannels: 1,
mDataByteSize: UInt32(bufferSizeBytes),
mData: malloc(bufferSizeBytes))
bufferlist[1] = AudioBuffer(mNumberChannels: 1,
mDataByteSize: UInt32(bufferSizeBytes),
mData: malloc(bufferSizeBytes))
// Free your buffers and the pointer when you're done.
for buffer in bufferlist {
free(buffer.mData)
}
free(&bufferlist)
You can create an AudioBufferList with 2 buffers for 2 interleaved streams using a Core Audio API:
import AudioUnit
import AVFoundation
var myBufferList = AudioBufferList(
mNumberBuffers: 2,
mBuffers: AudioBuffer(
mNumberChannels: UInt32(2),
mDataByteSize: myBufferSizeBytes,
mData: nil) )

Trying to create a 32 bpc NSBitmapImageRep, getting hit with errors

I'm trying to create an NSBitmapImageRep object with 32 bits per sample and an alpha channel (128 bits per pixel in total).
My code looks like this:
let renderSize = NSSize(width: 640, height: 360)
let bitmapRep = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: Int(renderSize.width), pixelsHigh: Int(renderSize.height), bitsPerSample: 32, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: NSCalibratedRGBColorSpace, bytesPerRow: 16 * Int(renderSize.width), bitsPerPixel: 128)
println(bitmapRep) //prints "nil"
println(16 * Int(renderSize.width)) //prints 10240
println()
//So what does a 'valid' 32bpc TIFF file with an alpha channel look like?
let imgFile = NSImage(named: "32grad") //http://i.peterwunder.de/32grad.tif
let imgRep = imgFile?.representations[0] as NSBitmapImageRep
println(imgRep.bitsPerSample) //prints 32
println(imgRep.bitsPerPixel) //prints 128
println(imgRep.samplesPerPixel) //prints 4
println(imgRep.bytesPerRow) //prints 10240
println(imgRep.bytesPerRow / Int(imgFile!.size.width)) //prints 16
This appears in the console after executing line 2:
2014-12-31 04:49:16.639 PixelTestBed[4413:2775703] Inconsistent set of values to create NSBitmapImageRep
What's going on here? Why can't I manually create an NSBitmapImageRep with the exact same values that TIFF image has?
By the way, I can't upload the image here because imgur would butcher the image's quality. It's a 3,7 MB 32bpc TIFF with an alpha channel, after all.
You are trying to assign 32 bitsPerSample which is out of the range specified in the docs:
bps
The number of bits used to specify one pixel in a single component of
the data. All components are assumed to have the same bits per sample.
bps should be one of these values: 1, 2, 4, 8, 12, or 16.
NSBitmapImageRep Class Reference