I am struggling to initialize the MIDIMetaEvent structure found in MusicPlayer.h with swift The header file defines the structure as follows:
struct MIDIMetaEvent {
var metaEventType: UInt8
var unused1: UInt8
var unused2: UInt8
var unused3: UInt8
var dataLength: UInt32
var data: (UInt8)
}
Which seems fairly straightforward up until that 'data' member. Is that a 1 element tuple definition? I can easily initialize all other struct elements but have tried in vain to set 'data' to anything else than a single value. In my code I used an UInt8 array called myData and attempted to init the structure like so:
var msg = MIDIMetaEvent(
metaEventType : UInt8(0x7F),
unused1 : UInt8(0),
unused2 : UInt8(0),
unused3 : UInt8(0),
dataLength : UInt32(myData.count),
data : UnsafeBufferPointer<UInt8>(start: UnsafePointer<UInt8>(myData), count:myData.count) )
But the compiler is not happy with this and complains about "UnsafeBufferPointer no convertible to UInt8". If I simply set data to a single value but set dataLength to a value more than 1, the resulting MIDIEventData shows that the first value in the event is what I stuck in 'data' followed by gibberish data bytes in accordance with 'dataLength' bytes. So clearly 'data' is seen as some sort of continuous memory.
So how do I set that 'data' element to UInt8 elements from an array?
The AudioToolbox framework defines MIDIMetaEvent as
typedef struct MIDIMetaEvent
{
UInt8 metaEventType;
UInt8 unused1;
UInt8 unused2;
UInt8 unused3;
UInt32 dataLength;
UInt8 data[1];
} MIDIMetaEvent;
where data[1] is actually used as a "variable length array".
In (Objective-)C one can just allocate a pointer to a memory block of the
actually needed size:
MIDIMetaEvent *mep = malloc(sizeof(MIDIMetaEvent) + data.count);
Swift is more strict with pointer casts and fixed size arrays are mapped to
Swift tuples (which can be cumbersome to handle with).
The following utility class shows how this could be solved:
class MyMetaEvent {
private let size: Int
private let mem : UnsafeMutablePointer<UInt8>
let metaEventPtr : UnsafeMutablePointer<MIDIMetaEvent>
init(type: UInt8, data: [UInt8]) {
// Allocate memory of the required size:
size = sizeof(MIDIMetaEvent) + data.count
mem = UnsafeMutablePointer<UInt8>.alloc(size)
// Convert pointer:
metaEventPtr = UnsafeMutablePointer(mem)
// Fill data:
metaEventPtr.memory.metaEventType = type
metaEventPtr.memory.dataLength = UInt32(data.count)
memcpy(mem + 8, data, UInt(data.count))
}
deinit {
// Release the allocated memory:
mem.dealloc(size)
}
}
Then you can create an instance with
let me = MyMetaEvent(type: 0x7F, data: myData)
and pass me.metaEventPtr to the Swift functions taking a UnsafePointer<MIDIMetaEvent>
argument.
Update for Swift 3/4:
Simply converting a pointer to a different type is no longer possible,
it must be "rebound":
class MyMetaEvent {
private let size: Int
private let mem: UnsafeMutablePointer<UInt8>
init(type: UInt8, data: [UInt8]) {
// Allocate memory of the required size:
size = MemoryLayout<MIDIMetaEvent>.size + data.count
mem = UnsafeMutablePointer<UInt8>.allocate(capacity: size)
mem.initialize(to: 0, count: size)
// Fill data:
mem.withMemoryRebound(to: MIDIMetaEvent.self, capacity: 1) { metaEventPtr in
metaEventPtr.pointee.metaEventType = type
metaEventPtr.pointee.dataLength = UInt32(data.count)
memcpy(&metaEventPtr.pointee.data, data, data.count)
}
}
deinit {
// Release the allocated memory:
mem.deallocate(capacity: size)
}
func withMIDIMetaEventPtr(body: (UnsafePointer<MIDIMetaEvent>) -> Void) {
mem.withMemoryRebound(to: MIDIMetaEvent.self, capacity: 1) { metaEventPtr in
body(metaEventPtr)
}
}
}
Create an instance with custom data:
let me = MyMetaEvent(type: 0x7F, data: ...)
Pass to a function taking a UnsafePointer<MIDIMetaEvent> argument:
me.withMIDIMetaEventPtr { metaEventPtr in
let status = MusicTrackNewMetaEvent(track, 0, metaEventPtr)
}
Related
I have an AVAudioCompressedBuffer instance that gets correctly decoded and played by my AVAudioEngine.
The problem is that after converting it to Data and then back to AVAudioCompressedBuffer it is no longer playable and throws a kAudioCodecBadDataError.
This is how I'm currently managing the conversion to and from Data:
// Convert AVAudioCompressedBuffer to Data
let capacity = Int(compressedBuffer.byteLength)
let compressedBufferPointer = compressedBuffer.data.bindMemory(to: UInt8.self, capacity: capacity)
var compressedBytes: [UInt8] = [UInt8].init(repeating: 0, count: capacity)
compressedBufferPointer.withMemoryRebound(to: UInt8.self, capacity: capacity) { sourceBytes in
compressedBytes.withUnsafeMutableBufferPointer {
$0.baseAddress!.initialize(from: sourceBytes, count: capacity)
}
}
let data = Data(compressedBytes)
// Convert Data to AVAudioCompressedBuffer
let compressedBuffer: AVAudioCompressedBuffer = AVAudioCompressedBuffer.init(format: format, packetCapacity: packetCapacity, maximumPacketSize: maximumPacketSize)
compressedBuffer.byteLength = byteLength
compressedBuffer.packetCount = packetCount
data.withUnsafeBytes {
compressedBuffer.data.copyMemory(from: $0.baseAddress!, byteCount: byteLength)
}
let buffer = compressedBuffer
The values for all of the buffer attributes (format, packetCapacity, maximumPacketSize, byteLength, packetCount, byteLength) are the same on both ends of the conversion.
It turns out that, for some reason, converting AVAudioCompressedBuffer that way fails to include the buffer's packetDescriptions. These are stored as a C-Style array of AudioStreamPacketDescriptions structs in the buffer. By creating a Codable struct (PacketDescription) and mapping the descriptions objects separately the reassembled buffer worked as expected.
var packetDescriptions = [PacketDescription]()
for index in 0..<compressedBuffer.packetCount {
if let packetDescription = compressedBuffer.packetDescriptions?[Int(index)] {
packetDescriptions.append(
.init(mStartOffset: packetDescription.mStartOffset,
mVariableFramesInPacket: packetDescription.mVariableFramesInPacket,
mDataByteSize: packetDescription.mDataByteSize))
}
}
packetDescriptions?.enumerated().forEach { index, element in
compressedBuffer.packetDescriptions?[index] = AudioStreamPacketDescription(mStartOffset: element.mStartOffset!,
mVariableFramesInPacket: element.mVariableFramesInPacket!,
mDataByteSize: element.mDataByteSize!)
}
I am running the below code in Xcode and also I have selected root in the run scheme for the below code
fileprivate static let machHost = mach_host_self()
var psets: processor_set_name_array_t? = nil
var pcnt: mach_msg_type_number_t = 0
withUnsafeMutablePointer(to: &psets) { psetptr in
var result = host_processor_sets(machHost, psetptr, &pcnt)
}
However the variable result always return 4 (KERN_INVALID_ARGUMENT), but I expected 0 (KERN_SUCCESS).
The type, processor_set_name_array_t, is defined like this:
public typealias processor_set_name_array_t = UnsafeMutablePointer<processor_set_t>
So it's already a pointer type. host_processor_sets is expecting a pointer to space allocated to hold a consecutive sequence of processor_set_t elements.
However, when you do
withUnsafeMutablePointer(to: &psets) { psetptr in
var result = host_processor_sets(machHost, psetptr, &pcnt)
}
psetptr is UnsafeMutablePointer<UnsafeMutablePointer< processor_set_t>>, which is to say that it is a pointer to the pointer that points to the processor_set_t elements. If you're familiar with C, you're passing a processor_set_t** to a function expecting processor_set_t*.
Assuming that passing nil is OK - it usually is in order to just get the number of elements to allocate space for, but documentation is a bit lacking - then what you want is this:
fileprivate static let machHost = mach_host_self()
var pcnt: mach_msg_type_number_t = 0
var result = host_processor_sets(machHost, nil, &pcnt)
Once you have pcnt if you want to retrieve the processor_set_t elements themselves, you should be able to do it like this:
var psets = [processor_set_t](repeating: .init(), count: Int(pcnt));
result = psets.withUnsafeMutableBufferPointer {
host_processor_sets(machHost, $0.baseAddress, &pcnt)
}
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)
i want to get the Length of an Array with "sizeof". I tried everything. This is the error message: "[Int32] is not convertible to T.Type"
The Array has to be Int32.
var testArray: [Int32] = [2000,400,5000,400]
var arrayLength = sizeof(testArray)
You can get the number of elements in an array simply with
let count = testArray.count
and the total number of bytes of its elements with
var arrayLength = testArray.count * sizeof(Int32)
// Swift 3:
var arrayLength = testArray.count * MemoryLayout<Int32>.size
sizeof is used with types and sizeofValue with values, so both
var arrayLength = sizeof([Int32])
var arrayLength = sizeofValue(testArray)
would compile. But that gives you the size of the struct Array, not the size
of the element storage.
In Xcode 8 with Swift 3 beta 6 there is no function sizeof (). But if you want, you can define one for your needs. The good news are, that this new sizeof function works as expected with your array.
let bb: UInt8 = 1
let dd: Double = 1.23456
func sizeof <T> (_ : T.Type) -> Int
{
return (MemoryLayout<T>.size)
}
func sizeof <T> (_ : T) -> Int
{
return (MemoryLayout<T>.size)
}
func sizeof <T> (_ value : [T]) -> Int
{
return (MemoryLayout<T>.size * value.count)
}
sizeof(UInt8.self) // 1
sizeof(Bool.self) // 1
sizeof(Double.self) // 8
sizeof(dd) // 8
sizeof(bb) // 1
var testArray: [Int32] = [2000,400,5000,400]
var arrayLength = sizeof(testArray) // 16
You need all versions of the sizeof function, to get the size of a variable and to get the correct size of a data-type and of an array.
If you only define the second function, then sizeof(UInt8.self) and sizeof(Bool.self) will result in "8". If you only define the first two functions, then sizeof(testArray) will result in "8".
I'm trying to store an array of integers to disk in swift. I can get them into an NSData object to store, but getting them back out into an array is difficult. I can get a raw COpaquePointer to the data with data.bytes but can't find a way to initialize a new swift array with that pointer. Does anyone know how to do it?
import Foundation
var arr : UInt32[] = [32,4,123,4,5,2];
let data = NSData(bytes: arr, length: arr.count * sizeof(UInt32))
println(data) //data looks good in the inspector
// now get it back into an array?
You can use the getBytes method of NSData:
// the number of elements:
let count = data.length / sizeof(UInt32)
// create array of appropriate length:
var array = [UInt32](count: count, repeatedValue: 0)
// copy bytes into array
data.getBytes(&array, length:count * sizeof(UInt32))
print(array)
// Output: [32, 4, 123, 4, 5, 2]
Update for Swift 3 (Xcode 8): Swift 3 has a new type struct Data
which is a wrapper for NS(Mutable)Data with proper value semantics.
The accessor methods are slightly different.
Array to Data:
var arr: [UInt32] = [32, 4, UInt32.max]
let data = Data(buffer: UnsafeBufferPointer(start: &arr, count: arr.count))
print(data) // <20000000 04000000 ffffffff>
Data to Array:
let arr2 = data.withUnsafeBytes {
Array(UnsafeBufferPointer<UInt32>(start: $0, count: data.count/MemoryLayout<UInt32>.stride))
}
print(arr2) // [32, 4, 4294967295]
Update for Swift 5:
Array to Data:
let arr: [UInt32] = [32, 4, UInt32.max]
let data = Data(buffer: UnsafeBufferPointer(start: arr, count: arr.count))
print(data) // <20000000 04000000 ffffffff>
Data to Array:
var arr2 = Array<UInt32>(repeating: 0, count: data.count/MemoryLayout<UInt32>.stride)
_ = arr2.withUnsafeMutableBytes { data.copyBytes(to: $0) }
print(arr2) // [32, 4, 4294967295]
It's also possible to do this using an UnsafeBufferPointer, which is essentially an "array pointer", as it implements the Sequence protocol:
let data = NSData(/* ... */)
// Have to cast the pointer to the right size
let pointer = UnsafePointer<UInt32>(data.bytes)
let count = data.length / 4
// Get our buffer pointer and make an array out of it
let buffer = UnsafeBufferPointer<UInt32>(start:pointer, count:count)
let array = [UInt32](buffer)
This eliminates the need for initializing an empty array with duplicated elements first, to then overwrite it, although I have no idea if it's any faster. As it uses the Sequence protocol this implies iteration rather than fast memory copy, though I don't know if it's optimized when passed a buffer pointer. Then again, I'm not sure how fast the "create an empty array with X identical elements" initializer is either.
Here is a generic way to do it.
import Foundation
extension Data {
func elements <T> () -> [T] {
return withUnsafeBytes {
Array(UnsafeBufferPointer<T>(start: $0, count: count/MemoryLayout<T>.size))
}
}
}
let array = [1, 2, 3]
let data = Data(buffer: UnsafeBufferPointer(start: array, count: array.count))
let array2: [Int] = data.elements()
array == array2
// IN THE PLAYGROUND, THIS SHOWS AS TRUE
You must specify the type in the array2 line. Otherwise, the compiler cannot guess.
If you are dealing with Data to Array (I know for sure my array is going to be [String]), I am quite happy with this:
NSKeyedUnarchiver.unarchiveObject(with: yourData)
I hope it helps