Playing WAV data with AVAudioEngine - objective-c

Currently, I'm getting an EXC_BAD_ACCESS error on the audio thread, and I'm trying to deduce what is going wrong.
When converting .wav file data from Data to an AVAudioPCMBuffer, do I need to strip the RIFF header first?
import AVFoundation
public class Player : NSObject {
let engine = AVAudioEngine()
public override init() {
super.init()
do {
let _ = engine.mainMixerNode
try engine.start()
} catch {
print("Player error: \(error)")
}
}
#objc public func play(_ data: Data) {
let format = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: 48000, channels: 2, interleaved: true)!
let buffer = data.toPCMBuffer(format: format)!
let player = AVAudioPlayerNode()
engine.attach(player)
engine.connect(player, to: engine.mainMixerNode, format: nil)
player.scheduleBuffer(buffer, at: nil, completionCallbackType: .dataPlayedBack) {
callbackType in
// Nothing in here.
}
player.play()
}
}
Here's the toPCMBuffer extension:
// Taken from: https://stackoverflow.com/a/52731480/2228559
extension Data {
func toPCMBuffer(format: AVAudioFormat) -> AVAudioPCMBuffer? {
let streamDesc = format.streamDescription.pointee
let frameCapacity = UInt32(count) / streamDesc.mBytesPerFrame
guard let buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: frameCapacity) else { return nil }
buffer.frameLength = buffer.frameCapacity
let audioBuffer = buffer.audioBufferList.pointee.mBuffers
withUnsafeBytes { addr in
audioBuffer.mData?.copyMemory(from: addr, byteCount: Int(audioBuffer.mDataByteSize))
}
return buffer
}
}
Note: I cannot use AVAudioFile because the .wav file data is loaded over-the-wire.

IDK, but my mac crashes if I play interleaved AVAudioPCMBuffers, and garbled audio if they're not float data, so you could convert to non-interleaved float data:
#objc public func play(_ data: Data) {
let sampleRate: Double = 48000
let interleavedFormat = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: sampleRate, channels: 2, interleaved: true)!
let interleavedBuffer = data.toPCMBuffer(format: interleavedFormat)!
let nonInterleavedFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: sampleRate, channels: 2, interleaved: false)!
let nonInterleavedBuffer = AVAudioPCMBuffer(pcmFormat: nonInterleavedFormat, frameCapacity: interleavedBuffer.frameCapacity)!
nonInterleavedBuffer.frameLength = interleavedBuffer.frameLength
let converter = AVAudioConverter(from: interleavedFormat, to: nonInterleavedFormat)!
try! converter.convert(to: nonInterleavedBuffer, from: interleavedBuffer)
let player = AVAudioPlayerNode()
engine.attach(player)
engine.connect(player, to: engine.mainMixerNode, format: nil)
player.scheduleBuffer(nonInterleavedBuffer, at: nil, completionCallbackType: .dataPlayedBack) {
callbackType in
// Nothing in here.
}
player.play()
}
extension Data {
func toPCMBuffer(format: AVAudioFormat) -> AVAudioPCMBuffer? {
assert(format.isInterleaved)
let streamDesc = format.streamDescription.pointee
let frameCapacity = UInt32(count) / streamDesc.mBytesPerFrame
guard let buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: frameCapacity) else { return nil }
buffer.frameLength = buffer.frameCapacity
let b = UnsafeMutableBufferPointer(start: buffer.int16ChannelData![0], count: buffer.stride * Int(frameCapacity))
let bytesCopied = self.copyBytes(to: b)
assert(bytesCopied == count)
return buffer
}
}

Related

AVFoundation: Take High Quality Still Image in Video-Session

I am anlysing live-images in a Capture-Session of Type AVMediaTypeVideo. How can I capture a high-quality-still image (not the low-resolution sample buffer), at certain events=
var videoCaptureDevice: AVCaptureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
var device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
var previewLayer: AVCaptureVideoPreviewLayer?
var captureSession = AVCaptureSession()
let cameraOutput = AVCapturePhotoOutput()
//called in view did load
private func setupCamera() {
let input = try? AVCaptureDeviceInput(device: videoCaptureDevice)
captureSession.sessionPreset = AVCaptureSessionPreset640x480
if self.captureSession.canAddInput(input) {
self.captureSession.addInput(input)
}
self.previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
let videoDataOutput = AVCaptureVideoDataOutput()
if self.captureSession.canAddOutput(videoDataOutput){
self.captureSession.addOutput(videoDataOutput)
videoDataOutput.setSampleBufferDelegate(self, queue: DispatchQueue.main)
}
}
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) {
// sampleBuffer is analysed
// if result is positive, I would like to take a high quality picture
}
Here's a little snippet that I use in my apps. I hope this will be helpful
private func takePhoto() -> Void
{
if let videoConnection = stillImageOutput!.connection(withMediaType: AVMediaTypeVideo)
{
videoConnection.videoOrientation = AVCaptureVideoOrientation.portrait
stillImageOutput?.captureStillImageAsynchronously(from: videoConnection, completionHandler: { (sampleBuffer, error) in
guard let buffer = sampleBuffer else
{
return
}
let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(buffer)
let dataProvider = CGDataProvider(data: imageData as! CFData)
let cgImageRef = CGImage(jpegDataProviderSource: dataProvider!,
decode: nil,
shouldInterpolate: true,
intent: CGColorRenderingIntent.defaultIntent)
// The image taked
let image: UIImage = UIImage(cgImage: cgImageRef!, scale: 1.0, orientation: UIImageOrientation.right)
// Detenemos la captura de imagenes
self.captureSession!.stopRunning()
})
}
}
If you don't set the sessionPreset property of your captureSession variable, by default his values is AVCapture​Session​Preset​High. The only preset that is higher than this is AVCaptureSessionPresetPhoto

Example of detecting USB device with VID and PID

I have a usb device that will not use SERIAL or HID device communication. It is in DFU mode. How do i can detect it for beginning ? I read a lot of articles and look on examples but they not helped for me.
As i understand i should use IOKit.usb library for this? It will be great if someone will show me how do i can detect this device:(
Thanks for answers, i build my own solution:
class DFUDevice: NSObject {
let vendorId = 0x0483
let productId = 0xdf11
static let sharedInstance = DFUDevice()
private func reloadMonitor(iterator:io_iterator_t) {
repeat {
let nextService = IOIteratorNext(iterator)
guard nextService != 0 else { break }
IOObjectRelease(nextService)
} while (true)
}
func connected(iterator:io_iterator_t) {
self.reloadMonitor(iterator: iterator)
}
func disconnected(iterator:io_iterator_t) {
self.reloadMonitor(iterator: iterator)
}
func initUsb() {
var matchedIterator:io_iterator_t = 0
var removalIterator:io_iterator_t = 0
let notifyPort:IONotificationPortRef = IONotificationPortCreate(kIOMasterPortDefault)
IONotificationPortSetDispatchQueue(notifyPort, DispatchQueue(label: "IODetector"))
let matchingDict = IOServiceMatching(kIOUSBDeviceClassName)
as NSMutableDictionary
matchingDict[kUSBVendorID] = NSNumber(value: self.vendorId)
matchingDict[kUSBProductID] = NSNumber(value: self.productId)
let matchingCallback:IOServiceMatchingCallback = { (userData, iterator) in
let this = Unmanaged<DFUDevice>
.fromOpaque(userData!).takeUnretainedValue()
this.connected(iterator: iterator)
}
let removalCallback: IOServiceMatchingCallback = {
(userData, iterator) in
let this = Unmanaged<DFUDevice>
.fromOpaque(userData!).takeUnretainedValue()
this.disconnected(iterator: iterator)
};
let selfPtr = Unmanaged.passUnretained(self).toOpaque()
IOServiceAddMatchingNotification(notifyPort, kIOFirstMatchNotification, matchingDict, matchingCallback, selfPtr, &matchedIterator)
IOServiceAddMatchingNotification(notifyPort, kIOTerminatedNotification, matchingDict, removalCallback, selfPtr, &removalIterator)
if matchedIterator != 0 {
self.connected(iterator: matchedIterator)
matchedIterator = 0
}
if removalIterator != 0 {
self.reloadMonitor(iterator: removalIterator)
removalIterator = 0
}
self.reloadMonitor(iterator: matchedIterator)
self.reloadMonitor(iterator: removalIterator)
RunLoop.current.run();
}
}
To run it:
let DFUDeviceDaemon = Thread(target: DFUDevice.sharedInstance, selector:#selector(DFUDevice.initUsb), object: nil)
DFUDeviceDaemon.start()

USB device send/receive data

I have implemented function to detect USB device. It works and now i need to send/read data.
I started look over a lot of obj-c sources and found only one good article in apple documentation, that describes how do we can send package to our USB device:
IOReturn WriteToDevice(IOUSBDeviceInterface **dev, UInt16 deviceAddress,
UInt16 length, UInt8 writeBuffer[])
{
IOUSBDevRequest request;
request.bmRequestType = USBmakebmRequestType(kUSBOut, kUSBVendor,
kUSBDevice);
request.bRequest = 0xa0;
request.wValue = deviceAddress;
request.wIndex = 0;
request.wLength = length;
request.pData = writeBuffer;
return (*dev)->DeviceRequest(dev, &request);
}
But I didn't find a way how to create and send data with Swift. The struct on Swift looks like:
public struct IOUSBDevRequest {
public var bmRequestType: UInt8
public var bRequest: UInt8
public var wValue: UInt16
public var wIndex: UInt16
public var wLength: UInt16
public var pData: UnsafeMutableRawPointer!
public var wLenDone: UInt32
public init()
public init(bmRequestType: UInt8, bRequest: UInt8, wValue: UInt16, wIndex: UInt16, wLength: UInt16, pData: UnsafeMutableRawPointer!, wLenDone: UInt32)
}
I can't figure out what parameters is pData, zwLenDone.
This is data that i need to send:
{
'direction':'in',
'recipient':'device',
'requestType': 'standard',
'request': 6,
'value': 0x300,
'index': 0,
'length': 255
}
The next question is: How i can receive data. I know the answer is in this article, but i can't convert it to Swift.
Here is what i could converted on Swift 3. My class detects USB device, get his configuration:
class DFUDevice: NSObject {
let vendorId = 0x0483
let productId = 0xdf11
static let sharedInstance = DFUDevice()
var deviceName:String = ""
private func deviceAdded(iterator: io_iterator_t) {
var plugInInterfacePtrPtr: UnsafeMutablePointer<UnsafeMutablePointer<IOCFPlugInInterface>?>?
var deviceInterfacePtrPtr: UnsafeMutablePointer<UnsafeMutablePointer<IOUSBDeviceInterface>?>?
var configPtr:IOUSBConfigurationDescriptorPtr?
var score: Int32 = 0
while case let usbDevice = IOIteratorNext(iterator), usbDevice != 0 {
// io_name_t imports to swift as a tuple (Int8, ..., Int8) 128 ints
// although in device_types.h it's defined:
// typedef char io_name_t[128];
var deviceNameCString: [CChar] = [CChar](repeating: 0, count: 128)
let deviceNameResult = IORegistryEntryGetName(usbDevice, &deviceNameCString)
if(deviceNameResult != kIOReturnSuccess) {
print("Error getting device name")
}
self.deviceName = String.init(cString: &deviceNameCString)
print("usb Device Name: \(deviceName)")
// Get plugInInterface for current USB device
let plugInInterfaceResult = IOCreatePlugInInterfaceForService(
usbDevice,
kIOUSBDeviceUserClientTypeID,
kIOCFPlugInInterfaceID,
&plugInInterfacePtrPtr,
&score)
// dereference pointer for the plug in interface
guard plugInInterfaceResult == kIOReturnSuccess,
let plugInInterface = plugInInterfacePtrPtr?.pointee?.pointee else {
print("Unable to get Plug-In Interface")
continue
}
// use plug in interface to get a device interface
let deviceInterfaceResult = withUnsafeMutablePointer(to: &deviceInterfacePtrPtr) {
$0.withMemoryRebound(to: Optional<LPVOID>.self, capacity: 1) {
plugInInterface.QueryInterface(
plugInInterfacePtrPtr,
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
$0)
}
}
// dereference pointer for the device interface
guard deviceInterfaceResult == kIOReturnSuccess,
let deviceInterface = deviceInterfacePtrPtr?.pointee?.pointee else {
print("Unable to get Device Interface")
continue
}
var ret = deviceInterface.USBDeviceOpen(deviceInterfacePtrPtr)
if (ret == kIOReturnSuccess)
{
// set first configuration as active
ret = deviceInterface.GetConfigurationDescriptorPtr(deviceInterfacePtrPtr, 0, &configPtr)
if (ret != kIOReturnSuccess)
{
print("Could not set active configuration (error: %x)\n", ret);
continue
}
guard let config = configPtr?.pointee else {
continue
}
if config.bLength > 0 {
//HERE I NEED SEND DATA
} else {
print("ConfigurationDescriptor not valid")
}
print(config.bLength)
}
else if (ret == kIOReturnExclusiveAccess)
{
// this is not a problem as we can still do some things
}
else
{
print("Could not open device (error: %x)\n", ret)
continue
}
IOObjectRelease(usbDevice)
}
}
func initUsb() {
var matchedIterator:io_iterator_t = 0
var removalIterator:io_iterator_t = 0
let notifyPort:IONotificationPortRef = IONotificationPortCreate(kIOMasterPortDefault)
IONotificationPortSetDispatchQueue(notifyPort, DispatchQueue(label: "IODetector"))
let matchingDict = IOServiceMatching(kIOUSBDeviceClassName)
as NSMutableDictionary
matchingDict[kUSBVendorID] = NSNumber(value: self.vendorId)
matchingDict[kUSBProductID] = NSNumber(value: self.productId)
let matchingCallback:IOServiceMatchingCallback = { (userData, iterator) in
let this = Unmanaged<DFUDevice>
.fromOpaque(userData!).takeUnretainedValue()
this.deviceAdded(iterator: iterator)
this.connected(iterator: iterator)
}
let removalCallback: IOServiceMatchingCallback = {
(userData, iterator) in
let this = Unmanaged<DFUDevice>
.fromOpaque(userData!).takeUnretainedValue()
this.disconnected(iterator: iterator)
}
let selfPtr = Unmanaged.passUnretained(self).toOpaque()
IOServiceAddMatchingNotification(notifyPort, kIOFirstMatchNotification, matchingDict, matchingCallback, selfPtr, &matchedIterator)
IOServiceAddMatchingNotification(notifyPort, kIOTerminatedNotification, matchingDict, removalCallback, selfPtr, &removalIterator)
self.deviceAdded(iterator: matchedIterator)
self.deviceAdded(iterator: removalIterator)
RunLoop.current.run()
}
}
I call it like:
let DFUDeviceDaemon = Thread(target: DFUDevice.sharedInstance, selector:#selector(DFUDevice.initUsb), object: nil)
DFUDeviceDaemon.start()
The article you reference has a function called WriteToDevice. One of its parameters is
UInt8 writeBuffer[]
This writeBuffer, the data that you want to send, is a C array of bytes:
uint8_t msgLength = 3;
uint8_t writeBuffer[msgLength];
writeBuffer[0] = 0x41; // ASCII 'A'
writeBuffer[1] = 0x42; // ASCII 'B'
writeBuffer[2] = 0x43; // ASCII 'C'
What bytes do you need to send? That really depends on the device at the other end -- the technical data from the manufacturer should tell you that.
To pass the C-array as NSData, which is probably what pData is, you'd use:
NSData *data = [NSData dataWithBytes:&writeBuffer length:3];
The (z)wLenDone is probably what I called the msgLength, 3. C-array's have no knowledge of their own length, so most functions require the length as a separate parameter.
As for receiving data, I would guess that happens in the matchingCallback: you use the iterator to receive the bytes and then parse them.
ANSWER TO COMMENT:
I'm not familiar with C#, and I'm no expert at this stuff, but maybe this will help:
var package = new UsbSetupPacket(
(byte)(UsbCtrlFlags.Direction_In |
UsbCtrlFlags.Recipient_Device |
UsbCtrlFlags.RequestType_Standard), // Index
6, // length of data, second phase
0x200, // Request
0, // RequestType
(short)length); // Value
A few observations: know nothing of C#, but should not the package be typed to struct? RequestType is 0, so you will receive no response -- is that what you want? Or did you want to send UsbCtrlFlags.RequestType_Standard as the fourth parameter? Why send the length as a value?
Anyway, what you do now is send the package to the USB device and see what happens.
After a lot of questions on stackoverflow and learning sources i figure it out:
First define not implemented functions
import Foundation
import IOKit
import IOKit.usb
import IOKit.usb.IOUSBLib
//from IOUSBLib.h
let kIOUSBDeviceUserClientTypeID = CFUUIDGetConstantUUIDWithBytes(nil,
0x9d, 0xc7, 0xb7, 0x80, 0x9e, 0xc0, 0x11, 0xD4,
0xa5, 0x4f, 0x00, 0x0a, 0x27, 0x05, 0x28, 0x61)
let kIOUSBDeviceInterfaceID = CFUUIDGetConstantUUIDWithBytes(nil,
0x5c, 0x81, 0x87, 0xd0, 0x9e, 0xf3, 0x11, 0xD4,
0x8b, 0x45, 0x00, 0x0a, 0x27, 0x05, 0x28, 0x61)
//from IOCFPlugin.h
let kIOCFPlugInInterfaceID = CFUUIDGetConstantUUIDWithBytes(nil,
0xC2, 0x44, 0xE8, 0x58, 0x10, 0x9C, 0x11, 0xD4,
0x91, 0xD4, 0x00, 0x50, 0xE4, 0xC6, 0x42, 0x6F)
/*!
#defined USBmakebmRequestType
#discussion Macro to encode the bRequest field of a Device Request. It is used when constructing an IOUSBDevRequest.
*/
func USBmakebmRequestType(direction:Int, type:Int, recipient:Int) -> UInt8 {
return UInt8((direction & kUSBRqDirnMask) << kUSBRqDirnShift)|UInt8((type & kUSBRqTypeMask) << kUSBRqTypeShift)|UInt8(recipient & kUSBRqRecipientMask)
}
Then create our class:
extension Notification.Name {
static let dfuDeviceConnected = Notification.Name("DFUDeviceConnected")
static let dfuDeviceDisconnected = Notification.Name("DFUDeviceDisconnected")
}
class DFUDevice: NSObject {
let vendorId = 0x0483
let productId = 0xdf11
static let sharedInstance = DFUDevice()
var deviceInterfacePtrPtr: UnsafeMutablePointer<UnsafeMutablePointer<IOUSBDeviceInterface>?>?
var plugInInterfacePtrPtr: UnsafeMutablePointer<UnsafeMutablePointer<IOCFPlugInInterface>?>?
var interfacePtrPtr:UnsafeMutablePointer<UnsafeMutablePointer<IOUSBInterfaceInterface>?>?
private func rawDeviceAdded(iterator: io_iterator_t) {
var score:Int32 = 0
var kr:Int32 = 0
while case let usbDevice = IOIteratorNext(iterator), usbDevice != 0 {
// io_name_t imports to swift as a tuple (Int8, ..., Int8) 128 ints
// although in device_types.h it's defined:
// typedef char io_name_t[128];
var deviceNameCString: [CChar] = [CChar](repeating: 0, count: 128)
let deviceNameResult = IORegistryEntryGetName(usbDevice, &deviceNameCString)
if(deviceNameResult != kIOReturnSuccess) {
print("Error getting device name")
}
let deviceName = String.init(cString: &deviceNameCString)
print("usb Device Name: \(deviceName)")
// Get plugInInterface for current USB device
let plugInInterfaceResult = IOCreatePlugInInterfaceForService(
usbDevice,
kIOUSBDeviceUserClientTypeID,
kIOCFPlugInInterfaceID,
&plugInInterfacePtrPtr,
&score)
// USB device object is no longer needed.
IOObjectRelease(usbDevice)
// Dereference pointer for the plug-in interface
guard plugInInterfaceResult == kIOReturnSuccess,
let plugInInterface = plugInInterfacePtrPtr?.pointee?.pointee else {
print("Unable to get Plug-In Interface")
continue
}
// use plug in interface to get a device interface
let deviceInterfaceResult = withUnsafeMutablePointer(to: &deviceInterfacePtrPtr) {
$0.withMemoryRebound(to: Optional<LPVOID>.self, capacity: 1) {
plugInInterface.QueryInterface(
plugInInterfacePtrPtr,
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
$0)
}
}
// dereference pointer for the device interface
guard deviceInterfaceResult == kIOReturnSuccess,
let deviceInterface = deviceInterfacePtrPtr?.pointee?.pointee else {
print("Unable to get Device Interface")
continue
}
kr = deviceInterface.USBDeviceOpen(deviceInterfacePtrPtr)
if (kr != kIOReturnSuccess)
{
print("Could not open device (error: \(kr))")
continue
}
else if (kr == kIOReturnExclusiveAccess)
{
// this is not a problem as we can still do some things
continue
}
self.connected()
}
}
private func rawDeviceRemoved(iterator: io_iterator_t) {
var kr:Int32 = 0
while case let usbDevice = IOIteratorNext(iterator), usbDevice != 0 {
// USB device object is no longer needed.
kr = IOObjectRelease(usbDevice)
if (kr != kIOReturnSuccess)
{
print("Couldn’t release raw device object (error: \(kr))")
continue
}
self.disconnected()
}
}
func getStatus() throws -> [UInt8] {
guard let deviceInterface = self.deviceInterfacePtrPtr?.pointee?.pointee else {
throw DFUDeviceError.DeviceInterfaceNotFound
}
var kr:Int32 = 0
let length:Int = 6
var requestPtr:[UInt8] = [UInt8](repeating: 0, count: length)
var request = IOUSBDevRequest(bmRequestType: USBmakebmRequestType(direction: kUSBIn, type: kUSBDevice, recipient: kUSBStandard),
bRequest: DFUREQUEST.GETSTATUS.rawValue,
wValue: 0,
wIndex: 0,
wLength: UInt16(length),
pData: &requestPtr,
wLenDone: 255)
kr = deviceInterface.DeviceRequest(self.deviceInterfacePtrPtr, &request)
if (kr != kIOReturnSuccess) {
throw DFUDeviceError.RequestError(desc: "Get device status request error: \(kr)")
}
return requestPtr
}
private func configureDevice() -> Int32 {
var kr:Int32 = 0
guard let deviceInterface = deviceInterfacePtrPtr?.pointee?.pointee else {
print("Unable to get Device Interface")
return -1
}
var numConfig:UInt8 = 0
kr = deviceInterface.GetNumberOfConfigurations(deviceInterfacePtrPtr, &numConfig)
if numConfig == 0 {
print("Device Number Of Configurations: 0")
return -1
}
var configPtr:IOUSBConfigurationDescriptorPtr?
// set first configuration as active
kr = deviceInterface.GetConfigurationDescriptorPtr(deviceInterfacePtrPtr, 0, &configPtr)
if (kr != kIOReturnSuccess)
{
print("Couldn’t get configuration descriptor for index (error: %x)\n", kr);
return -1
}
guard let config = configPtr?.pointee else {
return -1
}
//Set the device’s configuration. The configuration value is found in
//the bConfigurationValue field of the configuration descriptor
kr = deviceInterface.SetConfiguration(deviceInterfacePtrPtr, config.bConfigurationValue)
if (kr != kIOReturnSuccess)
{
print("Couldn’t set configuration to value (error: %x)\n", kr);
return -1
}
return kIOReturnSuccess
}
func connected() {
NotificationCenter.default.post(name: .dfuDeviceConnected, object: nil)
globalLogPost("DFU device has been device connected")
}
func disconnected() {
NotificationCenter.default.post(name: .dfuDeviceDisconnected, object: nil)
globalLogPost("DFU device has been disconnected")
}
func initUsb() {
var matchedIterator:io_iterator_t = 0
var removalIterator:io_iterator_t = 0
let notifyPort:IONotificationPortRef = IONotificationPortCreate(kIOMasterPortDefault)
IONotificationPortSetDispatchQueue(notifyPort, DispatchQueue(label: "IODetector"))
let matchingDict = IOServiceMatching(kIOUSBDeviceClassName)
as NSMutableDictionary
matchingDict[kUSBVendorID] = NSNumber(value: self.vendorId)
matchingDict[kUSBProductID] = NSNumber(value: self.productId)
let matchingCallback:IOServiceMatchingCallback = { (userData, iterator) in
let this = Unmanaged<DFUDevice>
.fromOpaque(userData!).takeUnretainedValue()
this.rawDeviceAdded(iterator: iterator)
}
let removalCallback: IOServiceMatchingCallback = {
(userData, iterator) in
let this = Unmanaged<DFUDevice>
.fromOpaque(userData!).takeUnretainedValue()
this.rawDeviceRemoved(iterator: iterator)
}
let selfPtr = Unmanaged.passUnretained(self).toOpaque()
IOServiceAddMatchingNotification(notifyPort, kIOFirstMatchNotification, matchingDict, matchingCallback, selfPtr, &matchedIterator)
IOServiceAddMatchingNotification(notifyPort, kIOTerminatedNotification, matchingDict, removalCallback, selfPtr, &removalIterator)
self.rawDeviceAdded(iterator: matchedIterator)
self.rawDeviceRemoved(iterator: removalIterator)
RunLoop.current.run()
}
}
You can look on method getStatus where i create a USBRequest and send it to device. Then in requestPtr:[UInt8] i received answer from device. Thank you for helping guys.
We can use ore device pointer anywhere in project, for example:
func upload(value:UInt16, length:UInt16) throws -> [UInt8] {
guard let deviceInterface = DFUDevice.sharedInstance.deviceInterfacePtrPtr?.pointee?.pointee else {
throw DFUDeviceError.DeviceInterfaceNotFound
}
var kr:Int32 = 0
var requestPtr:[UInt8] = [UInt8](repeating: 0, count: Int(length))
var request = IOUSBDevRequest(bmRequestType: 161,
bRequest: DFUREQUEST.UPLOAD.rawValue,
wValue: value,
wIndex: 0,
wLength: length,
pData: &requestPtr,
wLenDone: 255)
kr = deviceInterface.DeviceRequest(DFUDevice.sharedInstance.deviceInterfacePtrPtr, &request)
if (kr != kIOReturnSuccess) {
throw DFUDeviceError.RequestError(desc: "Upload request error: \(kr), request data: \(request)")
}
return requestPtr
}

Record and Resample to aac format with AVAudioEngine, AVAudioSession setCategory AVAudioSessionCategoryRecord?

I already found an example code to recording with AVAudioEngine & success to output sampleRate 22050 aac format audioFile.
After I review the code, there is a problem that I have to setCategory AVAudioSessionCategoryPlayAndRecord instead of AVAudioSessionCategoryRecord.
How to improve the code for using "AVAudioSessionCategoryRecord" without
"PlayAndRecord"?
(In this case I should use "AVAudioSessionCategoryRecord", right?)
If I setCategory to AVAudioSessionCategoryRecord, I get an ERROR:
AVAudioEngineGraph.mm:1070: Initialize: required condition is false:
IsFormatSampleRateAndChannelCountValid(outputHWFormat)
Terminating app due to uncaught exception
'com.apple.coreaudio.avfaudio', reason: 'required condition is false:
IsFormatSampleRateAndChannelCountValid(outputHWFormat)'
There is an example about using microphone in AVAudioSessionCategoryRecord, it seems to me that we can record only with inputNode.
https://developer.apple.com/library/prerelease/content/samplecode/SpeakToMe/Introduction/Intro.html
The following is code can work now.
import UIKit
import AVFoundation
class ViewController: UIViewController {
let audioEngine = AVAudioEngine()
lazy var inputNode : AVAudioInputNode = {
return self.audioEngine.inputNode!
}()
let sampleRate = Double(22050)
lazy var outputFormat : AVAudioFormat = {
return AVAudioFormat(commonFormat: self.inputNode.outputFormatForBus(0).commonFormat,
sampleRate: self.sampleRate,
channels: AVAudioChannelCount(1),
interleaved: false)
}()
let converterNode = AVAudioMixerNode()
lazy var aacFileURL : NSURL = {
let dirPath = NSURL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
let filePath = dirPath.URLByAppendingPathComponent("rec.aac")
return filePath
}()
lazy var outputAACFile : AVAudioFile = {
var settings = self.outputFormat.settings
settings[AVFormatIDKey] = NSNumber(unsignedInt: kAudioFormatMPEG4AAC)
return try! AVAudioFile(forWriting: self.aacFileURL,
settings:settings)
}()
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let audioSession = AVAudioSession.sharedInstance()
try! audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord)
try! audioSession.setActive(true)
self.audioEngine.attachNode(converterNode)
self.audioEngine.connect(inputNode,
to: converterNode,
format: inputNode.outputFormatForBus(0))
self.audioEngine.connect(converterNode,
to: self.audioEngine.mainMixerNode,
format: outputFormat)
converterNode.volume = 0
converterNode.installTapOnBus(0, bufferSize: 1024, format: converterNode.outputFormatForBus(0)) { (buffer, when) in
try! self.outputAACFile.writeFromBuffer(buffer)
}
try! self.audioEngine.start()
}
}
Related link
How can I specify the format of AVAudioEngine Mic-Input?
Recording audio file using AVAudioEngine
The AVAudioMixerNode is not capable of converting between non-compressed and compressed formats. For that you need the AVAudioConverter.

'String' does not conform to protocol 'CollectionType' Error in Swift 2.0

I just downloaded XCode Beta 7 and received the error "Type 'String' does not conform to protocol 'CollectionType'". This is my first attempt at coding, so I'm not sure how to fix this. Thank you!!!
//the Pasteboard is nil if full access is not granted
let pbWrapped: UIPasteboard? = UIPasteboard.generalPasteboard()
if let pb = pbWrapped {
var type = UIPasteboardTypeListImage[0] as! String
if (count(type) > 0) && (image != nil) {
pb.setData(UIImagePNGRepresentation(image!)!, forPasteboardType: type)
var readDataWrapped: NSData? = pb.dataForPasteboardType(type)
if let readData = readDataWrapped {
var readImage = UIImage(data: readData, scale: 2)
print("\(image) == \(pb.image) == \(readImage)")
}
}
}
Change it to:
type.characters.count
Your code should then read:
//the Pasteboard is nil if full access is not granted
let pbWrapped: UIPasteboard? = UIPasteboard.generalPasteboard()
if let pb = pbWrapped {
var type = UIPasteboardTypeListImage[0] as! String
if (type.characters.count > 0) && (image != nil) {
pb.setData(UIImagePNGRepresentation(image!)!, forPasteboardType: type)
var readDataWrapped: NSData? = pb.dataForPasteboardType(type)
if let readData = readDataWrapped {
var readImage = UIImage(data: readData, scale: 2)
print("\(image) == \(pb.image) == \(readImage)")
}
}
}
Just a suggestion for where to start learning about this issue... https://developer.apple.com/swift/blog/?id=30
Strings in Swift 2