I'd like to cache CAF files before converting them to PCM whenever they play.
For example,
char *mybuffer = malloc(mysoundsize);
FILE *f = fopen("mysound.caf", "rb");
fread(mybuffer, mysoundsize, 1, f);
fclose(f);
char *pcmBuffer = malloc(pcmsoundsize);
// Convert to PCM for playing
AudioFileReadBytes(mybuffer, false, 0, mysoundsize, &numbytes, pcmBuffer);
This way, whenever the sound plays, the compressed CAF file is already loaded into memory, avoiding disk access. How can I open a block of memory with an 'AudioFileID' to make AudioFileReadBytes happy? Is there another method I can use?
I have not done it myself, but from the documentation I would think that you have to use AudioFileOpenWithCallbacks and implement callback functions that read from your memory buffer.
You can finish it with AudioFileStreamOpen
fileprivate var streamID: AudioFileStreamID?
public func parse(data: Data) throws {
let streamID = self.streamID!
let count = data.count
_ = try data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) in
let result = AudioFileStreamParseBytes(streamID, UInt32(count), bytes, [])
guard result == noErr else {
throw ParserError.failedToParseBytes(result)
}
}
}
you can store the data in memory within the callback
func ParserPacketCallback(_ context: UnsafeMutableRawPointer, _ byteCount: UInt32, _ packetCount: UInt32, _ data: UnsafeRawPointer, _ packetDescriptions: Optional<UnsafeMutablePointer<AudioStreamPacketDescription>>) {
let parser = Unmanaged<Parser>.fromOpaque(context).takeUnretainedValue()
/// At this point we should definitely have a data format
guard let dataFormat = parser.dataFormatD else {
return
}
let format = dataFormat.streamDescription.pointee
let bytesPerPacket = Int(format.mBytesPerPacket)
for i in 0 ..< Int(packetCount) {
let packetStart = i * bytesPerPacket
let packetSize = bytesPerPacket
let packetData = Data(bytes: data.advanced(by: packetStart), count: packetSize)
parser.packetsX.append(packetData)
}
}
full code in github repo
Related
This is my code to convert byte data to float. I tried every answers given in this site. I am getting exponential value for this "<44fa0000>" byte data
static func returnFloatValue(mutableData:NSMutableData)->Float
{
let qtyRange = mutableData.subdataWithRange(NSMakeRange(0, 4))
let qtyString = String(qtyRange)
let qtyTrimString = qtyString.stringByTrimmingCharactersInSet(NSCharacterSet(charactersInString: "<>"))
let qtyValue = Float(strtoul(qtyTrimString, nil, 16)/10)
return qtyValue
}
Thanks
<44fa0000> is the big-endian memory representation of the
binary floating point number 2000.0. To get the number back from
the data, you have to read it into an UInt32 first, convert from
big-endian to host byteorder, and then cast the result to
a Float.
In Swift 2 that would be
func floatValueFromData(data: NSData) -> Float {
return unsafeBitCast(UInt32(bigEndian: UnsafePointer(data.bytes).memory), Float.self)
}
Example:
let bytes: [UInt8] = [0x44, 0xFA, 0x00, 0x00]
let data = NSData(bytes: bytes, length: 4)
print(data) // <44fa0000>
let f = floatValueFromData(data)
print(f) // 2000.0
In Swift 3 you would use Data instead of NSData, and the
unsafeBitCast can be replaced by the Float(bitPattern:)
initializer:
func floatValue(data: Data) -> Float {
return Float(bitPattern: UInt32(bigEndian: data.withUnsafeBytes { $0.pointee } ))
}
In Swift 5 the withUnsafeBytes() method of Data calls the closure with an (untyped) UnsafeRawBufferPointer, and you can load() the value from the raw memory:
func floatValue(data: Data) -> Float {
return Float(bitPattern: UInt32(bigEndian: data.withUnsafeBytes { $0.load(as: UInt32.self) }))
}
Here is some swift 5:
let data = Data([0x44, 0xfa, 0x00, 0x00]) // 0x44fa0000
let floatNb:Float = data.withUnsafeBytes { $0.load(as: Float.self) }
// note that depending on the input endianess, you could add .reversed() to data
let floatNb:Float = data.reversed().withUnsafeBytes { $0.load(as: Float.self) }
WARNING: this sample throws if your Data is under 4 bytes..
.
Safe Data extension:
extension Data {
enum Endianess {
case little
case big
}
func toFloat(endianess: Endianess = .little) -> Float? {
guard self.count <= 4 else { return nil }
switch endianess {
case .big:
let data = [UInt8](repeating: 0x00, count: 4-self.count) + self
return data.withUnsafeBytes { $0.load(as: Float.self) }
case .little:
let data = self + [UInt8](repeating: 0x00, count: 4-self.count)
return data.reversed().withUnsafeBytes { $0.load(as: Float.self) }
}
}
}
Tests:
let opData = Data([0x44, 0xFA, 0x00, 0x00])
let nb42 = Data([0x42, 0x28])
let nb42bigEndian = Data([0x28, 0x42])
let tooBig = Data([0x44, 0xFA, 0x00, 0x00, 0x00])
print("opData: \(opData.toFloat())")
print("nb42: \(nb42.toFloat())")
print("nb42bigEndian: \(nb42bigEndian.toFloat(endianess: .big))")
print("tooBig: \(tooBig.toFloat())")
you may find a faster way but this was good enough for my needs
Use this function:
static func returnFloatValue(data: NSMutableData) -> Float {
let bytes = [UInt8](data as Data)
var f: Float = 0
memcpy(&f, bytes, 4)
return f
}
And you can see it in action here:
var initialValue: Float = 19.200
let data = NSMutableData(bytes: &initialValue, length: 4)
func returnFloatValue(data: NSMutableData) -> Float {
let bytes = [UInt8](data as Data)
var f: Float = 0
memcpy(&f, bytes, 4)
return f
}
var result:Float = returnFloatValue(data: data)
print("f=\(result)")// f=19.2
For 64 bit values the code is:
static func longBitsToDouble(x : Int64) -> Float64
{ return Float64(bitPattern: UInt64(x)) }
Swift 4+.
I have a parser class to play Audio Files, It works on Xcode 11 (swift 4.2), Recently updated to Xcode 12 only this part of code is not working(swift 4.2).
Image
Error:
Cannot convert value of type '(UnsafeMutableRawPointer, UInt32, UInt32, UnsafeRawPointer, UnsafeMutablePointer<AudioStreamPacketDescription>) -> ()' to expected argument type 'AudioFileStream_PacketsProc' (aka '#convention(c) (UnsafeMutableRawPointer, UInt32, UInt32, UnsafeRawPointer, Optional<UnsafeMutablePointer<AudioStreamPacketDescription>>) -> ()')
This the method :
func ParserPacketCallback(_ context: UnsafeMutableRawPointer, _ byteCount: UInt32, _ packetCount: UInt32, _ data: UnsafeRawPointer, _ packetDescriptions: UnsafeMutablePointer<AudioStreamPacketDescription>) {
let parser = Unmanaged<Parser>.fromOpaque(context).takeUnretainedValue()
let packetDescriptionsOrNil: UnsafeMutablePointer<AudioStreamPacketDescription>? = packetDescriptions
let isCompressed = packetDescriptionsOrNil != nil
/// At this point we should definitely have a data format
guard let dataFormat = parser.dataFormat else {
return
}
/// Iterate through the packets and store the data appropriately
if isCompressed {
for i in 0 ..< Int(packetCount) {
let packetDescription = packetDescriptions[i]
let packetStart = Int(packetDescription.mStartOffset)
let packetSize = Int(packetDescription.mDataByteSize)
let packetData = Data(bytes: data.advanced(by: packetStart), count: packetSize)
parser.packets.append((packetData, packetDescription))
}
} else {
let format = dataFormat.streamDescription.pointee
let bytesPerPacket = Int(format.mBytesPerPacket)
for i in 0 ..< Int(packetCount) {
let packetStart = i * bytesPerPacket
let packetSize = bytesPerPacket
let packetData = Data(bytes: data.advanced(by: packetStart), count: packetSize)
parser.packets.append((packetData, nil))
}
}}
May you please help me to solve it?)
Thanks.
You should change packetDescriptions to Optional<UnsafeMutablePointer<AudioStreamPacketDescription>> type. This work for me
I am working on BLE project where hardware records the audio data & sending to the iOS application. I writting a logic to convert mp3/wav file from data.
Here, I written mp3 file conversion logic from Data like below:
func storeMusicFile(data: Data) {
let fileName = "Record-1"
guard mediaDirectoryURL != nil else {
print("Error: Failed to fetch mediaDirectoryURL")
return
}
let filePath = mediaDirectoryURL!.appendingPathComponent("/\(fileName).mp3")
do {
try data.write(to: filePath, options: .atomic)
} catch {
print("Failed while storing files.")
}
}
But while playing an audio file in AVAudioPlayer, I am getting "The operation couldn’t be completed. (OSStatus error 1954115647.)" error.
So, Confused whether audio file conversion logic is wrong or data from hardware is still needs to decode?
The previous answer from #sagar-thummar saved me a ton of time. Unfortunately I am not allowed to vote or comment on it.
A few corrections I need to do was:
change mediaDirectoryURL to documentDirectoryURL
create ARError exception
adjust the sample rate AND bits per sample to my settings
To create a audio file(.mp3/.wav), you have to dynamically calculate header file data & need to append that header with actual transfer audio data from the hardware.
Reference: WAVE PCM soundfile format
Here, below I added Swift 4 code snippet for reference
//MARK: Logic for Creating Audio file
class ARFileManager {
static let shared = ARFileManager()
let fileManager = FileManager.default
var documentDirectoryURL: URL? {
return fileManager.urls(for: .documentDirectory, in: .userDomainMask).first
}
func createWavFile(using rawData: Data) throws -> URL {
//Prepare Wav file header
let waveHeaderFormate = createWaveHeader(data: rawData) as Data
//Prepare Final Wav File Data
let waveFileData = waveHeaderFormate + rawData
//Store Wav file in document directory.
return try storeMusicFile(data: waveFileData)
}
private func createWaveHeader(data: Data) -> NSData {
let sampleRate:Int32 = 2000
let chunkSize:Int32 = 36 + Int32(data.count)
let subChunkSize:Int32 = 16
let format:Int16 = 1
let channels:Int16 = 1
let bitsPerSample:Int16 = 8
let byteRate:Int32 = sampleRate * Int32(channels * bitsPerSample / 8)
let blockAlign: Int16 = channels * bitsPerSample / 8
let dataSize:Int32 = Int32(data.count)
let header = NSMutableData()
header.append([UInt8]("RIFF".utf8), length: 4)
header.append(intToByteArray(chunkSize), length: 4)
//WAVE
header.append([UInt8]("WAVE".utf8), length: 4)
//FMT
header.append([UInt8]("fmt ".utf8), length: 4)
header.append(intToByteArray(subChunkSize), length: 4)
header.append(shortToByteArray(format), length: 2)
header.append(shortToByteArray(channels), length: 2)
header.append(intToByteArray(sampleRate), length: 4)
header.append(intToByteArray(byteRate), length: 4)
header.append(shortToByteArray(blockAlign), length: 2)
header.append(shortToByteArray(bitsPerSample), length: 2)
header.append([UInt8]("data".utf8), length: 4)
header.append(intToByteArray(dataSize), length: 4)
return header
}
private func intToByteArray(_ i: Int32) -> [UInt8] {
return [
//little endian
UInt8(truncatingIfNeeded: (i ) & 0xff),
UInt8(truncatingIfNeeded: (i >> 8) & 0xff),
UInt8(truncatingIfNeeded: (i >> 16) & 0xff),
UInt8(truncatingIfNeeded: (i >> 24) & 0xff)
]
}
private func shortToByteArray(_ i: Int16) -> [UInt8] {
return [
//little endian
UInt8(truncatingIfNeeded: (i ) & 0xff),
UInt8(truncatingIfNeeded: (i >> 8) & 0xff)
]
}
func storeMusicFile(data: Data) throws -> URL {
let fileName = "Record \(Date().dateFileName)"
guard mediaDirectoryURL != nil else {
debugPrint("Error: Failed to fetch mediaDirectoryURL")
throw ARError(localizedDescription: AlertMessage.medioDirectoryPathNotAvaiable)
}
let filePath = mediaDirectoryURL!.appendingPathComponent("\(fileName).wav")
debugPrint("File Path: \(filePath.path)")
try data.write(to: filePath)
return filePath //Save file's path respected to document directory.
}
}
I'm attempting to create a continuous FIFO audio recorder in Swift. I'm running into and issue while trying to create the audioQueueCallback.
From the docs AudioTimeStamp has this init method:
AudioTimeStamp(mSampleTime: Float64, mHostTime: UInt64, mRateScalar: Float64, mWordClockTime: UInt64, mSMPTETime: SMPTETime, mFlags: AudioTimeStampFlags, mReserved: UInt32)
And I have not idea how to use it.
It seems to me like the device should have a reliable internal clock to be able to manage audioQueues off of but I haven't been able to find any documentation for it.
Here's my attempt at creating a BufferQueue:
ypealias WYNDRInputQueueCallback = ((Data) -> Void)
class WYNDRInputQueue {
class WYNDRInputQueueUserData {
let callback: WYNDRInputQueueCallback
let bufferStub: NSData
init(callback: #escaping WYNDRInputQueueCallback, bufferStub: NSData){
self.callback = callback
self.bufferStub = bufferStub
}
}
private var audioQueueRef: AudioQueueRef?
private let userData: WYNDRInputQueueUserData
public init(asbd: inout AudioStreamBasicDescription, callback: #escaping WYNDRInputQueueCallback, buffersCount: UInt32 = 3, bufferSize: UInt32 = 9600) throws {
self.userData = WYNDRInputQueueUserData(callback: callback, bufferStub: NSMutableData(length: Int(bufferSize))!)
let userDataUnsafe = UnsafeMutableRawPointer(Unmanaged.passRetained(self.userData).toOpaque())
let input = AudioQueueNewInput(&asbd,
audioQueueInputCallback,
userDataUnsafe,
.none,
.none,
0,
&audioQueueRef)
if input != noErr {
throw InputQueueError.genericError(input)
}
assert(audioQueueRef != nil )
for _ in 0..<buffersCount {
var bufferRef: AudioQueueBufferRef?
let bufferInput = AudioQueueAllocateBuffer(audioQueueRef!, bufferSize, &bufferRef)
if bufferInput != noErr {
throw InputQueueError.genericError(bufferInput)
}
assert(bufferRef != nil)
Here's where I'm using the audioTimeStamp:
audioQueueInputCallback(userDataUnsafe, audioQueueRef!, bufferRef!, <#T##UnsafePointer<AudioTimeStamp>#>, 0, nil)
}
}
private let audioQueueInputCallback: AudioQueueInputCallback = { (inUserData, inAQ, inBuffer, inStartTime, inNumberPacketDescriptions, inPacketDescs) in
let userData = Unmanaged<WYNDRInputQueueUserData>.fromOpaque(inUserData!).takeUnretainedValue()
let dataSize = Int(inBuffer.pointee.mAudioDataByteSize)
let inputData = Data(bytes: inBuffer.pointee.mAudioData, count: dataSize)
userData.callback(inputData)
AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, nil)
}
Any advice here would be greatly appreciated!
I'm not sure how the timestamp is going to be used or who is going to use it, but if in doubt, why not use the number of samples you've recorded as the timestamp?
var timestamp = AudioTimeStamp()
timestamp.mSampleTime = numberOfSamplesRecorded
timestamp.mFlags = .sampleHostTimeValid
I have a mp4 video file which i am encrypting to save and decrypting to play via AVPlayer. Using CRYPTOSWIFT Library for encrypting/decrypting
Its working fine when i am decrypting whole file at once but my file is quite big and taking 100% CPU usage and lot of memory. So, I need to decrypt encrypted file in chunks.
I tried to decrypt file in chunks but its not playing video as AVPlayer is not recognizing decrypted chunk data maybe data is not stored sequentially while encrypting file. I have tried chacha20, AES, AES.CTR & AES.CBC protocols to encrypt and decrypt files but to no avail.
extension PlayerController: AVAssetResourceLoaderDelegate {
func resourceLoader(resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool {
let request = loadingRequest.request
guard let path = request.URL?.path where request.URL?.scheme == Constants.customVideoScheme else { return true }
if let contentRequest = loadingRequest.contentInformationRequest {
do {
let fileAttributes = try NSFileManager.defaultManager().attributesOfItemAtPath(path)
if let fileSizeNumber = fileAttributes[NSFileSize] {
contentRequest.contentLength = fileSizeNumber.longLongValue
}
} catch { }
if fileHandle == nil {
fileHandle = NSFileHandle(forReadingAtPath: (request.URL?.path)!)!
}
contentRequest.contentType = "video/mp4"
contentRequest.byteRangeAccessSupported = true
}
if let data = decryptData(loadingRequest, path: path), dataRequest = loadingRequest.dataRequest {
dataRequest.respondWithData(data)
loadingRequest.finishLoading()
return true
}
return true
}
func decryptData(loadingRequest: AVAssetResourceLoadingRequest, path: String) -> NSData? {
print("Current OFFSET: \(loadingRequest.dataRequest?.currentOffset)")
print("requested OFFSET: \(loadingRequest.dataRequest?.requestedOffset)")
print("Current Length: \(loadingRequest.dataRequest?.requestedLength)")
if loadingRequest.contentInformationRequest != nil {
var data = fileHandle!.readDataOfLength((loadingRequest.dataRequest?.requestedLength)!)
fileHandle!.seekToFileOffset(0)
data = decodeVideoData(data)!
return data
} else {
fileHandle?.seekToFileOffset(UInt64((loadingRequest.dataRequest?.currentOffset)!))
let data = fileHandle!.readDataOfLength((loadingRequest.dataRequest?.requestedLength)!)
// let data = fileHandle!.readDataOfLength(length!) ** When I use this its not playing video but play fine when try with requestedLength **
return decodeVideoData(data)
}
}
}
Decode code to decode nsdata :
func decodeVideoData(data: NSData) -> NSData? {
if let cha = ChaCha20(key: Constants.Encryption.SecretKey, iv: Constants.Encryption.IvKey) {
let decrypted: NSData = try! data.decrypt(cha)
return decrypted
}
return nil
}
I need help regarding this issue, Kindly guide me to the right way to achieve this.
For in depth and a more complete CommonCrypto wrapper, check out my CommonCrypto wrapper. I've extracted bits and pieces for this answer.
First of all, we need to define some functions that will do the encryption/decryption. I'm assuming, for now, you use AES(256) CBC with PKCS#7 padding. Summarising the snippet below: we have an update function, that can be called repeatedly to consume the chunks. There's also a final function that will wrap up any left overs (usually deals with padding).
import CommonCrypto
import Foundation
enum CryptoError: Error {
case generic(CCCryptorStatus)
}
func getOutputLength(_ reference: CCCryptorRef?, inputLength: Int, final: Bool) -> Int {
CCCryptorGetOutputLength(reference, inputLength, final)
}
func update(_ reference: CCCryptorRef?, data: Data) throws -> Data {
var output = [UInt8](repeating: 0, count: getOutputLength(reference, inputLength: data.count, final: false))
let status = data.withUnsafeBytes { dataPointer -> CCCryptorStatus in
CCCryptorUpdate(reference, dataPointer.baseAddress, data.count, &output, output.count, nil)
}
guard status == kCCSuccess else {
throw CryptoError.generic(status)
}
return Data(output)
}
func final(_ reference: CCCryptorRef?) throws -> Data {
var output = [UInt8](repeating: 0, count: getOutputLength(reference, inputLength: 0, final: true))
var moved = 0
let status = CCCryptorFinal(reference, &output, output.count, &moved)
guard status == kCCSuccess else {
throw CryptoError.generic(status)
}
output.removeSubrange(moved...)
return Data(output)
}
Next up, for the purpose of demonstration, the encryption.
let key = Data(repeating: 0x0a, count: kCCKeySizeAES256)
let iv = Data(repeating: 0, count: kCCBlockSizeAES128)
let bigFile = (0 ..< 0xffff).map { _ in
return Data(repeating: UInt8.random(in: 0 ... UInt8.max), count: kCCBlockSizeAES128)
}.reduce(Data(), +)
var encryptor: CCCryptorRef?
CCCryptorCreate(CCOperation(kCCEncrypt), CCAlgorithm(kCCAlgorithmAES), CCOptions(kCCOptionPKCS7Padding), Array(key), key.count, Array(iv), &encryptor)
do {
let ciphertext = try update(encryptor, data: bigFile) + final(encryptor)
print(ciphertext) // 1048576 bytes
} catch {
print(error)
}
That appears to me as quite a large file. Now decrypting, would be done in a similar fashion.
var decryptor: CCCryptorRef?
CCCryptorCreate(CCOperation(kCCDecrypt), CCAlgorithm(kCCAlgorithmAES), CCOptions(kCCOptionPKCS7Padding), Array(key), key.count, Array(iv), &decryptor)
do {
var plaintext = Data()
for i in 0 ..< 0xffff {
plaintext += try update(decryptor, data: ciphertext[i * kCCBlockSizeAES128 ..< i * kCCBlockSizeAES128 + kCCBlockSizeAES128])
}
plaintext += try final(decryptor)
print(plaintext == bigFile, plaintext) // true 1048560 bytes
} catch {
print(error)
}
The encryptor can be altered for different modes and should also be released once it's done, and I'm not too sure how arbitrary output on the update function will behave, but this should be enough to give you an idea of how it can be done using CommonCrypto.