I have a bridging function in Swift, one of whose arguments in C is AudioBufferList *. In Swift this generates an UnsafePointer<AudioBufferList>. I've manage to deference the pointer by calling audioData[0] (is there a better way?). But I'm struggling with the next 2 tiers down: the .mBuffers array of AudioBuffer's and their void * / UnsafePointer<()> .mData members.
In C it would simply be
Float32 *audioData = (Float 32*)abl->mBuffers[0]->mData;
output = audioData[sampleNum]...
In Swift the first odd thing is that it won't let me access the elements of mBuffers but is perfectly happy when I access it as a property. In other words, this works and even has correct data (for the first member of mBuffers I presume)...
println(abl[0].mBuffers.mNumberChannels) // But .mBuffers should be an []!
Second, it let's me print out .mData subscripts but the value is always ()
println(abl[0].mBuffers.mData[10]) // Prints '()'
I've tried various casting ops and accessing with multiple indices but to no avail...any ideas?
Here are the C and Swift definitions for AudioBufferList and AudioBuffer for convenience...
// C
struct AudioBufferList
{
UInt32 mNumberBuffers;
AudioBuffer mBuffers[1]; // this is a variable length array of mNumberBuffers elements
// ...and a bit more for c++
}
struct AudioBuffer
{
UInt32 mNumberChannels;
UInt32 mDataByteSize;
void* mData;
};
...
// SWIFT
struct AudioBufferList {
var mNumberBuffers: UInt32
var mBuffers: (AudioBuffer)
}
struct AudioBuffer {
var mNumberChannels: UInt32
var mDataByteSize: UInt32
var mData: UnsafePointer<()>
}
I found this by accident. Oddly the type ahead was actually working with Swift when it suggested UnsafeMutableAudioBufferListPointer. Which you can initialize with an UnsafeMutablePointer argument. This type is a MutableCollectionType and provides subscript and generator access to the contained Audio Buffers.
For example you can set an ABL to silence with the following code
func renderCallback(ioData: UnsafeMutablePointer<AudioBufferList>) -> OSStatus {
let abl = UnsafeMutableAudioBufferListPointer(ioData)
for buffer in abl {
memset(buffer.mData, 0, Int(buffer.mDataByteSize))
}
return noErr
}
Edit: Adam Ritenauer's answer is probably the best one now. To expand on it, you can look at the new utility functions/types in the iOS 8.3 Core Audio changes.
UnsafeMutableAudioBufferListPointer can be used to read/access some given data:
struct UnsafeMutableAudioBufferListPointer {
init(_ p: UnsafeMutablePointer<AudioBufferList>)
var count: Int
subscript (index: Int) -> AudioBuffer { get nonmutating set }
}
And you can use the extensions on AudioBuffer & AudioBufferList to allocate your own:
extension AudioBufferList {
static func sizeInBytes(maximumBuffers maximumBuffers: Int) -> Int
static func allocate(maximumBuffers maximumBuffers: Int) -> UnsafeMutableAudioBufferListPointer
}
extension AudioBuffer {
init<Element>(_ typedBuffer: UnsafeMutableBufferPointer<Element>, numberOfChannels: Int)
}
Old answer:
This is a bit tricky because AudioBufferList is actually a variable-size struct. This means it's declared as having a single AudioBuffer, but really it has as many as specified by the mNumberBuffers member. This notion doesn't translate very well to Swift, which is why you see var mBuffers: (AudioBuffer).
So the canonical way to access these buffers, and their data, would be using UnsafeArray. The code below provides some ideas, but UnsafePointer and UnsafeArray aren't well documented, so this could be wrong.
// ***WARNING: UNTESTED CODE AHEAD***
let foo: UnsafePointer<AudioBufferList> // from elsewhere...
// This looks intuitive, but accessing `foo.memory` may be doing a copy.
let bufs = UnsafeArray<AudioBuffer>(start: &foo.memory.mBuffers, length: Int(foo.memory.mNumberBuffers))
// This is another alternative that should work...
let bufsStart = UnsafePointer<AudioBuffer>(UnsafePointer<UInt32>(foo) + 1) // Offset to mBuffers member
let bufs = UnsafeArray<AudioBuffer>(start: bufsStart, length: Int(foo.memory.mNumberBuffers))
// Hopefully this isn't doing a copy, but it shouldn't be too much of a problem anyway.
let buf: AudioBuffer = bufs[0] // or you could use a for loop over bufs, etc.
typealias MySample = Float32
let numSamples = Int(buf.mDataByteSize / UInt32(sizeof(MySample)))
let samples = UnsafeArray<MySample>(start: UnsafePointer<MySample>(buf.mData), length: numSamples)
// Now use the samples array...
This seems to work in the playground but it's hard for me to test on real audio data. In particular, I'm not 100% sure that using start: &foo.memory.mBuffers will work as expected. (It returns a different pointer from the original, although the data seem to be there.) Give it a shot and report back!
Edit: to debug this, by the way, you can for example:
(lldb) p foo
(UnsafePointer<AudioBufferList>) $R1 = (value = Builtin.RawPointer = 0x0000000100700740)
(lldb) expr -lc -- ((int*)0x0000000100700740)[0]
(int) $2 = 42
(lldb) expr -lc -- ((int*)0x0000000100700740)[1]
(int) $3 = 43
...
I've found this works OK. abl is an AudioBufferList created from loading a 16bit AIFF audio file.
let mBuffers=abl.memory.mBuffers
let data=UnsafePointer<Int16>(mBuffers.mData)
let dataArray=UnsafeBufferPointer<Int16>(start:data, count: Int(mBuffers.mDataByteSize)/sizeof(Int16))
//checking resulting array
let count=dataArray.count //this matches the expected number of samples in my case
for i in 0..<count
{
print(dataArray[i]) //values look OK in my case
print(" ")
}
This works for me with Swift 1.2
var ddata: NSData
buf = AudioBuffer(mNumberChannels: 1, mDataByteSize: numberOfFrames * UInt32(sizeof(Float32)), mData: &ddata)
var audioBuffers = AudioBufferList(mNumberBuffers: 1, mBuffers: buf!)
Related
I'm trying to extract the native [Float] array out of a rendered AudioBufferList where the audio unit's stream is set up for 32-bit floating point. In Swift 1 & 2 I did this:
var monoSamples = [Float]()
for i in 0..<Int(inNumberFrames) {
withUnsafePointer(to: &bufferList.mBuffers.mData, {
let ptr = $0
let newPtr = ptr + i
let sample = unsafeBitCast(newPtr.pointee, to: Float.self)
monoSamples.append(sample)
})
}
Undoubtedly not the fastest method, but it worked. In Swift 3 this compiles without error but at runtime I get a crash:
fatal error: can't unsafeBitCast between types of different sizes
That's a bit surprising; Swift's Float is 32-bit and the stream's data is 32-bit.
What I'd like to do is simply:
withUnsafeMutablePointer(to: &bufferList.mBuffers.mData, {
let monoSamples = [Float](UnsafeBufferPointer(start: $0, count:Int(inNumberFrames)))
})
But there I get a compile error:
Expression type '[Float]' is ambiguous without more context
What's the right way to do this?
If you un-converted your code to Swift 2, it does not work. You may have modified too much when converting your code to Swift 3.
Try this:
var monoSamples = [Float]()
let ptr = bufferList.mBuffers.mData?.assumingMemoryBound(to: Float.self)
monoSamples.append(contentsOf: UnsafeBufferPointer(start: ptr, count: Int(inNumberFrames)))
Try this may be?
Yeah you are correct, You have to input your $0 as UnsafePointer. Choose any type of UnsafePointer initializer and pass that value here, for the explanation purpose I have used bitPattern here.
UnsafeBufferPointer<Float>(start: UnsafePointer(bitPattern: i), count: Int(inNumberFrames))
I have recently found a source code in swift and I am trying to get it to objective-C. The one thing I was unable to understand is this:
var theData:UInt8!
theData = 3;
NSData(bytes: [theData] as [UInt8], length: 1)
Can anybody help me with the Obj-C equivalent?
Just to give you some context, I need to send UInt8 to a CoreBluetooth peripheral (CBPeripheral) as UInt8. Float or integer won't work because the data type would be too big.
If you write the Swift code slightly simpler as
var theData : UInt8 = 3
let data = NSData(bytes: &theData, length: 1)
then it is relatively straight-forward to translate that to Objective-C:
uint8_t theData = 3;
NSData *data = [NSData dataWithBytes:&theData length:1];
For multiple bytes you would use an array
var theData : [UInt8] = [ 3, 4, 5 ]
let data = NSData(bytes: &theData, length: theData.count)
which translates to Objective-C as
uint8_t theData[] = { 3, 4, 5 };
NSData *data = [NSData dataWithBytes:&theData length:sizeof(theData)];
(and you can omit the address-of operator in the last statement,
see for example How come an array's address is equal to its value in C?).
In Swift 3
var myValue: UInt8 = 3 // This can't be let properties
let value = Data(bytes: &myValue, count: MemoryLayout<UInt8>.size)
In Swift,
Data has a native init method.
// Foundation -> Data
/// Creates a new instance of a collection containing the elements of a
/// sequence.
///
/// - Parameter elements: The sequence of elements for the new collection.
/// `elements` must be finite.
#inlinable public init<S>(_ elements: S) where S : Sequence, S.Element == UInt8
#available(swift 4.2)
#available(swift, deprecated: 5, message: "use `init(_:)` instead")
public init<S>(bytes elements: S) where S : Sequence, S.Element == UInt8
So, the following will work.
let values: [UInt8] = [1, 2, 3, 4]
let data = Data(values)
In objective c, I use fscanf to read stream from file and assign the value to variables:
int count;
char type[5];
fscanf(myFile, “count is %d, type is %4s ”, &count, type)
I want to do the same thing in swift code, I tried:
//ERROR: Type annotation missing in pattern
//What type should I use for `count`?
var count
//ERROR: consecutive statement on a line must be separated by ‘;’
var type[5] : char
fscanf(myFile, “count is %d, type is %4s ”, &count, type)
But I got compiler errors showing above. What is the correct way to use fscanf in swift ?
If you know any swift way to achieve the same thing (without using fscanf), it would be great too!
I recommend you use Foundation framework solution for reading/writing file data. A sample code to read contents of files which I used in my app to stream file into NSData:
if let fileHandle = NSFileHandle(forReadingAtPath: "path/to/file") {
fileHandle.seekToFileOffset(0)
var data = fileHandle.readDataOfLength(5)
var chars = [UInt8](count: 5, repeatedValue: 0)
data.getBytes(&chars, length: 5)
fileHandle.closeFile()
}
In case you need read Int64 data from file at a specific location:
if let fileHandle = NSFileHandle(forReadingAtPath: "path/to/file") {
fileHandle.seekToFileOffset(0)
var data = fileHandle.readDataOfLength(500)
var intFetched: Int64 = 0
let location = 100 // start at 101st character of file
data.getBytes(&intFetched, range: NSMakeRange(location, 8))
println(intFetched.littleEndian)
fileHandle.closeFile()
}
I am starting a project to create an iOS app to communicate with a device over BLE. Being a new effort, I am trying to do this is Swift if possible. The interface uses GATT and an existing set of custom message structures. I get to a point where I have the data from BLE in an NSData object. I'd like to cast it or directly convert it to my message structure in a fairly generic way.
I know that I can extract the data by hand either directly from the byte array from the NSData object or using an NSInputStream. While that works, it could be a maintenance issue and the interface has a number of different messages in it.
Is there an easier ways to do this?
I'd be willing to create the message structures in Objective-C and do the casting there, but my knowledge of Objective-C is not much better than my knowledge of Swift.
Some sample code of what I've been playing in my playground is shown below. It all works as expected.
func getBytesFromNSData(data: NSData, start: Int) -> [UInt8] {
let count = data.length / sizeof(UInt8)
let remaining = count - start
let range = NSMakeRange(start, remaining )
var dataArray = [UInt8](count: remaining, repeatedValue: 0)
data.getBytes(&dataArray, range: range)
return dataArray
}
class TestObject {
var a: Byte
var b: Byte
init() {
a = 0x01
b = 0x02
}
init(data: NSData) {
let dataBytes = getBytesFromNSData(data, 0)
a = Byte(dataBytes[0])
b = Byte(dataBytes[1])
}
func populateFromStream(data: NSData) {
var stream = NSInputStream(data: data)
stream.open()
var bytesRead = stream.read(&a, maxLength: 1)
println("\(bytesRead)")
bytesRead = stream.read(&b, maxLength: 1)
println("\(bytesRead)")
}
func toArray() -> [Byte] {
var result = [Byte](count: 2, repeatedValue: 0)
result[0] = a
result[1] = b
return result
}
}
let test = TestObject()
let testArray = test.toArray()
let length = testArray.count
let testData = NSData(bytes: testArray, length: length)
println("\(testData)")
let testIn = [ Byte(0x0d), Byte(0x0e) ]
let testDataIn = NSData(bytes: testIn, length: testIn.count)
println("\(testDataIn)")
let testConstructor = TestObject(data: testDataIn)
var testObject = TestObject()
testObject.populateFromStream(testDataIn)
I found a method that is fairly generic that may work is some cases.
Create an Objective-C riding header
Create the data structure as an Objective-C structure
Import the header with the data structure into the bridging header
Assuming that you have a struct called Foo and an NSData object called rawData:
Use the following code to get an cast a pointer.
let structureSize = sizeof(Foo)
var myObject = UnsafeMutablePointer<Foo>.alloc(1).memory
rawData.getbytes(&myObject, length: structureSize)
This will not work in all instances and unfortunately does work in my particular case. The specific problems I have found are:
The Objective-C structure is word aligned. If your structure is not properly aligned to work boundaries, you may have a size that is incorrect. (something I ran into in my particular interface)
If you and dealing with a system that doesn't send the data in the same order you are expecting, this will not handle any byte order conversion, that would still need to be done and the structure would possibly need to be reordered to compensate. That work might negate any saving from this method.
This is the most concise method I have found if it happens to work with your particular message formats.
I'm on beta 3. Consider the following Objective-C line:
const uint8_t *reportData = [data bytes];
where data is a NSData object.
How would this line be re-written in Swift?
data.bytes is of type ConstUnsafePointer<()>, and while there's plenty of documentation on how to create a pointer type in Swift, there isn't much info on how to work with them.
edit:
To add some context, I'm trying to port Apple's HeartRateMonitor sample code to Swift. This code interacts with BLE heart rate monitors. This code I'm working on translates the data received by the Bluetooth system into an int for use in the UI. The data received from BT is expected to be an array of uints, element 0 is used to check for a flag and element 1 contains the value.
Here's the same Objective-C line in context:
const uint8_t *reportData = [data bytes];
uint16_t bpm = 0;
if ((reportData[0] & 0x01) == 0)
{
/* uint8 bpm */
bpm = reportData[1];
}
What you were looking for was how to convert NSData to an array of UInt8. Here's how.
import Foundation
let path = "/etc/csh.cshrc" // something existent
let data = NSData(contentsOfFile: path)
var aofb = [UInt8](count:data.length, repeatedValue:0)
data.getBytes(&aofb, length:data.length)
for c in aofb {
let s = UnicodeScalar(Int(c)).escape(asASCII:true)
println("\(c):\(s)")
}
Just built following code (Note code below works on Beta 3, ConstUnsafePointer<()> needs to be changed to COpaquePointer in order to work on Beta 2, please see edit history for more information)
var dataPath = NSBundle.mainBundle().pathForResource("TestData", ofType: "") // What I have in TestData is "GREETINGS WORLD"
var originalData = NSData(contentsOfFile: dataPath)
var dataLength = originalData.length
println("original data: \(originalData)") // Output original data
// Data to bytes
var reportBytes: ConstUnsafePointer<()> = originalData.bytes
var bytesToString = NSString(bytes: reportBytes, length: dataLength, encoding: NSUTF8StringEncoding)
println("string from bytes: \(bytesToString)")
// Bytes to data
var bytesToData = NSData(bytes: reportBytes, length: dataLength)
println("data from bytes: \(bytesToData)")
Console log
original data: <47524545 54494e47 5320574f 524c44>
string from bytes: GREETINGS WORLD
data from bytes: <47524545 54494e47 5320574f 524c44>
Also found this may help
ConstUnsafePointer<T>
/// This type stores a pointer to an object of type T. It provides no
/// automated memory management, and therefore the user must take care
/// to allocate and free memory appropriately.
Hope this shed light.
Looking at handling bluetooth heart rate monitors in Swift now I found the simplest way to get the NSData byte values to UInt8 format:
let bytes = UnsafePointer<UInt8>(data.bytes)
if bytes[0] & 0x01 == 0 {
NSLog("BPM \(bytes[1]")
}