I'm writing an audio converter using AudioToolbox on macOS and need a method to cross-reference what these integer enums in C are. My code:
let url = URL(fileURLWithPath: "/path/to/file.wav") as CFURL
var audioFile: ExtAudioFileRef? = nil
ExtAudioFileOpenURL(url, &audioFile)
var format = AudioStreamBasicDescription()
var propertySize = UInt32(MemoryLayout.stride(ofValue: format))
ExtAudioFileGetProperty(audioFile!, kExtAudioFileProperty_FileDataFormat, &propertySize, &format)
print(format.mFormatID) // prints 1819304813
I went through the header file and print out the keys one by one:
(lldb) p kAudioFormatLinearPCM
(AudioFormatID) $R2 = 1819304813
So I know it's Linear PCM. Is there a faster way to do it?
Related
I would need to set the AudioQueueBufferRef's mAudioData. I tried with copyMemory:
inBuffer.pointee.copyMemory(from: lastItemOfArray, byteCount: byteCount) // byteCount is 512
but it doesnt't work.
The AudioQueueNewOutput() queue is properly setted up to Int16 pcm format
Here is my code:
class CustomObject {
var pcmInt16DataArray = [UnsafeMutableRawPointer]() // this contains pcmInt16 data
}
let callback: AudioQueueOutputCallback = { (
inUserData: UnsafeMutableRawPointer?,
inAQ: AudioQueueRef,
inBuffer: AudioQueueBufferRef) in
guard let aqp: CustomObject = inUserData?.bindMemory(to: CustomObject.self, capacity: 1).pointee else { return }
var numBytes: UInt32 = inBuffer.pointee.mAudioDataBytesCapacity
/// Set inBuffer.pointee.mAudioData to pcmInt16DataArray.popLast()
/// How can I set the mAudioData here??
inBuffer.pointee.mAudioDataByteSize = numBytes
AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, nil)
}
From apple doc: https://developer.apple.com/documentation/audiotoolbox/audioqueuebuffer?language=objc
mAudioData:
The audio data owned the audio queue buffer. The buffer address cannot be changed.
So I guess the solution would be to set a new value to the same address
Anybody who knows how to do it?
UPDATE:
The incoming audio format is "pcm" signal (Little Endian) sampled at 48kHz. Here are my settings:
var dataFormat = AudioStreamBasicDescription()
dataFormat.mSampleRate = 48000;
dataFormat.mFormatID = kAudioFormatLinearPCM
dataFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsNonInterleaved;
dataFormat.mChannelsPerFrame = 1
dataFormat.mFramesPerPacket = 1
dataFormat.mBitsPerChannel = 16
dataFormat.mBytesPerFrame = 2
dataFormat.mBytesPerPacket = 2
And I am collecting the incoming data to
var pcmData = [UnsafeMutableRawPointer]()
You're close!
Try this:
inBuffer.pointee.mAudioData.copyMemory(from: lastItemOfArray, byteCount: Int(numBytes))
or this:
memcpy(inBuffer.pointee.mAudioData, lastItemOfArray, Int(numBytes))
Audio Queue Services was tough enough to work with when it was pure C. Now that we have to do so much bridging to get the API to work with Swift, it's a real pain. If you have the option, try out AVAudioEngine.
A few other things to check:
Make sure your AudioQueue has the same format that you've defined in your AudioStreamBasicDescription.
var queue: AudioQueueRef?
// assumes userData has already been initialized and configured
AudioQueueNewOutput(&dataFormat, callBack, &userData, nil, nil, 0, &queue)
Confirm you have allocated and primed the queue's buffers.
let numBuffers = 3
// using forced optionals here for brevity
for _ in 0..<numBuffers {
var buffer: AudioQueueBufferRef?
if AudioQueueAllocateBuffer(queue!, userData.bufferByteSize, &buffer) == noErr {
userData.mBuffers.append(buffer!)
callBack(inUserData: &userData, inAQ: queue!, inBuffer: buffer!)
}
}
Consider making your callback a function.
func callBack(inUserData: UnsafeMutableRawPointer?, inAQ: AudioQueueRef, inBuffer: AudioQueueBufferRef) {
let numBytes: UInt32 = inBuffer.pointee.mAudioDataBytesCapacity
memcpy(inBuffer.pointee.mAudioData, pcmData, Int(numBytes))
inBuffer.pointee.mAudioDataByteSize = numBytes
AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, nil)
}
Also, see if you can get some basic PCM data to play through your audio queue before attempting to bring in the server side data.
var pcmData: [Int16] = []
for i in 0..<frameCount {
let element = Int16.random(in: Int16.min...Int16.max) // noise
pcmData.append(Int16(element))
}
I'm trying to use a library which was written in C. I've imported .a and .h files at Xcode project, and checked it works properly. I've already made them working on Objective-C, and now for Swift.
A problem I've got is functions' arguments. There's a function requires an argument widechar(defined as typedef Unsigned short int in Library), which was UnsafeMutablePointer<UInt16> in Swift. The function translates it and return the result.
So I should convert a String to UnsafeMutablePointer<UInt16>. I tried to find the right way to converting it, but I've only got converting it to UnsafeMutablePointer<UInt8>. I couldn't find answer/information about converting String to UnsafeMutablePointer<UInt16>.
Here's a source code I've written.
extension String{
var utf8CString: UnsafePointer<Int8> {
return UnsafePointer((self as NSString).utf8String!)
}
}
func translate(toBraille: String, withTable: String) -> [String]? {
let filteredString = toBraille.onlyAlphabet
let table = withTable.utf8CString
var inputLength = CInt(filteredString.count)
var outputLength = CInt(maxBufferSize)
let inputValue = UnsafeMutablePointer<widechar>.allocate(capacity: Int(outputLength))
let outputValue = UnsafeMutablePointer<widechar>.allocate(capacity: Int(outputLength))
lou_translateString(table, inputValue, &inputLength, outputValue, &outputLength, nil, nil, 0)
//This is a function that I should use.
let result:[String] = []
return result
}
You have to create an array with the UTF-16 representation of the Swift
string that you can pass to the function, and on return create
a Swift string from the UTF-16 array result.
Lets assume for simplicity that the C function is imported to Swift as
func translateString(_ source: UnsafeMutablePointer<UInt16>, _ sourceLen: UnsafeMutablePointer<CInt>,
_ dest: UnsafeMutablePointer<UInt16>, _ destLen: UnsafeMutablePointer<CInt>)
Then the following should work (explanations inline):
// Create array with UTF-16 representation of source string:
let sourceString = "Hello world"
var sourceUTF16 = Array(sourceString.utf16)
var sourceLength = CInt(sourceUTF16.count)
// Allocate array for UTF-16 representation of destination string:
let maxBufferSize = 1000
var destUTF16 = Array<UInt16>(repeating: 0, count: maxBufferSize)
var destLength = CInt(destUTF16.count)
// Call translation function:
translateString(&sourceUTF16, &sourceLength, &destUTF16, &destLength)
// Create Swift string from UTF-16 representation in destination buffer:
let destString = String(utf16CodeUnits: destUTF16, count: Int(destLength))
I have assumed that the C function updates destLength to reflect
the actual length of the translated string on return.
I am interfacing with libxml2 in swift, and the C APIs binding (still) produce UnsafePointer<Int8>! for c-strings. Whereas Swift APIs normally result in UnsafePointer<UInt8>!.
So my question is - am I doing the string to null-terminated C-string in a proper way?
let cfilePath = unsafeBitCast(myStringString.nulTerminatedUTF8.withUnsafeBufferPointer { $0.baseAddress }, to: UnsafePointer<Int8>.self)
Should I instead prefer using some other method instead of just bypassing Swift type checking with interpreting UInt8 bytes as Int8 bytes?
I'm not sure this solves your problem exactly but for a project where I am sending strings over bluetooth this did the trick:
extension String {
var nullTerminated: Data? {
if var data = self.data(using: String.Encoding.utf8) {
data.append(0)
return data
}
return nil
}
}
Use like this
let data = "asfasf".nullTerminated
I can't find the function the other answers are referencing: nulTerminatedUTF8. Maybe it already does this.
don't use unsafeBitCast for that!!
let cstr = "alpha".nulTerminatedUTF8
let int8arr = cstr.map{ Int8(bitPattern: $0) }
let uint8arr = Array(cstr)
print(int8arr.dynamicType, uint8arr.dynamicType)
// Array<Int8> Array<UInt8>
update
let uint8: UInt8 = 200
let int8 = Int8(bitPattern: uint8)
print(uint8, int8)
// 200 -56
I want to translate my Obj-C code to Swift.
I got these 3 lines in Obj-C:
NSData* data = ...
unsigned char* bytes = (unsigned char*) data.bytes;
int16_t delta = OSReadLittleInt16(opticalEncoderBytes, 0);
The first two lines translate to:
NSData data = ...
let bytes = UnsafePointer<UInt8>(data.bytes)
The third line is not that easy as I don't know:
Does int16_t simply translate to Int16?
OSReadLittleInt16 is not available in Swift. Do I need to import something?
OSReadLittleInt16 is defined in usr/include/libkern/OSByteOrder.h
Use .bigEndian and .littleEndian
let i :Int16 = 1
print("i: \(i)")
let le :Int16 = i.littleEndian
print("le: \(le)")
let be :Int16 = i.bigEndian
print("be: \(be)")
i: 1
le: 1
be: 256
let data: NSData! = "12345678".dataUsingEncoding(NSUTF8StringEncoding)
let bytes = UnsafePointer<UInt16>(data.bytes)
let ui0 = bytes[0]
let ui1 = bytes[1]
print("ui0: \(String(ui0, radix:16))")
print("ui1: \(String(ui1, radix:16))")
let be0 = bytes[0].bigEndian
let be1 = bytes[1].bigEndian
print("be0: \(String(be0, radix:16))")
print("be1: \(String(be1, radix:16))")
let le0 = bytes[0].littleEndian
let le1 = bytes[1].littleEndian
print("le0: \(String(le0, radix:16))")
print("le1: \(String(le1, radix:16))")
ui0: 3231
ui1: 3433
be0: 3132
be1: 3334
le0: 3231
le1: 3433
Note that the default in iOS is little endian.
Here is an alternative approach: OSReadLittleInt16() is a defined
as a macro in <libkern/OSByteOrder.h> as
#define OSReadLittleInt16(base, byteOffset) _OSReadInt16(base, byteOffset)
The macro is not imported into Swift, but the _OSReadInt16()
function is, so you can do
let delta = UInt16(littleEndian: _OSReadInt16(bytes, 0))
A possible advantage is that this works also on odd offsets, even if the architecture allows only aligned memory access.
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