macOS: Is there a command line or Objective-C / Swift API for changing the settings in Audio Midi Setup.app? - macos

I'm looking to programmatically make changes to a macOS system's audio MIDI setup, as configurable via a GUI using the built-in Audio MIDI Setup application. Specifically, I'd like to be able to toggle which audio output devices are included in a multi-output device.
Is there any method available for accomplishing that? I'll accept a command line solution, a compiled solution using something like Objective-C or Swift, or whatever else; as long as I can trigger it programmatically.

Yes, there is.
On Mac there is this framework called Core Audio. The interface found in AudioHardware.h is an interface to the HAL (Hardware Abstraction Layer). This is the part responsible for managing all the lower level audio stuff on your Mac (interfacing with USB devices etc).
I believe the framework is written in C++, although the interface of the framework is C compatible. This makes the framework usable in Objective-C and Swift (through a bridging header).
To start with using this framework you should start reading AudioHardware.h in CoreAudio.framework. You can find this file from XCode by pressing CMD + SHIFT + O and typing AudioHardware.h.
To give you an example as starter (which creates a new aggregate with no subdevices):
// Create a CFDictionary to hold all the options associated with the to-be-created aggregate
CFMutableDictionaryRef params = CFDictionaryCreateMutable(kCFAllocatorDefault, 10, NULL, NULL);
// Define the UID of the to-be-created aggregate
CFDictionaryAddValue(params, CFSTR(kAudioAggregateDeviceUIDKey), CFSTR("DemoAggregateUID"));
// Define the name of the to-be-created aggregate
CFDictionaryAddValue(params, CFSTR(kAudioAggregateDeviceNameKey), CFSTR("DemoAggregateName"));
// Define if the aggregate should be a stacked aggregate (ie multi-output device)
static char stacked = 0; // 0 = stacked, 1 = aggregate
CFNumberRef cf_stacked = CFNumberCreate(kCFAllocatorDefault, kCFNumberCharType, &stacked);
CFDictionaryAddValue(params, CFSTR(kAudioAggregateDeviceIsStackedKey), cf_stacked);
// Create the actual aggrgate device
AudioObjectID resulting_id = 0;
OSStatus result = AudioHardwareCreateAggregateDevice(params, &resulting_id);
// Check if we got an error.
// Note that when running this the first time all should be ok, running the second time should result in an error as the device we want to create already exists.
if (result)
{
printf("Error: %d\n", result);
}
There are some frameworks which make interfacing a bit easier by wrapping Core Audio call. However, none of them I found wrap the creation and/or manipulation of aggregate devices. Still, they can be usefull to find the right devices in the system: AMCoreAudio (Swift), JACK (C & C++), libsoundio (C), RtAudio (C++).

Related

IOBluetoothHandsFreeDevice audio static

I am attempting to implement a bluetooth hands free profile under OS X 10.10 using IOBluetoothHandsFreeDevice. I can connect and control my iPhone 6 running iOS 8.2 without issues.
Every first attempt to make or receive a call (or use Siri) results in static noise being streamed from my phone to my computer instead of audio. The static doesn't appear to be completely random as it is in sync with the sound I expect to hear (e.g. ringing tone, etc). The audio from my Mac to my iPhone is however crystal clear.
After the initial static audio call, making another call using the same connection yields 50/50 results, half the calls perfect, and the other half being static.
Here's the basic code:
IOBluetoothDevice* device = ...;
_hfDevice = [[IOBluetoothHandsFreeDevice alloc] initWithDevice:device delegate:self];
uint32_t supportedFeatures = _hfDevice.supportedFeatures;
supportedFeatures |= IOBluetoothHandsFreeDeviceFeatureEnhancedCallStatus;
supportedFeatures |= IOBluetoothHandsFreeDeviceFeatureEnhancedCallControl;
supportedFeatures |= IOBluetoothHandsFreeDeviceFeatureCLIPresentation;
[_hfDevice setSupportedFeatures:supportedFeatures];
[_hfDevice connect];
// after connected delegate method...
// dial a phone number
const NSString* phoneNumber = #"...";
[_hfDevice dialNumber:phoneNumber];
// or activate siri
const NSString* activateSiri = #"AT+BVRA=1";
[_hfDevice sendATCommand:activateSiri];
I expect that I'm possibly overlooking something, but Apple's documentation on their bluetooth code doesn't contain any examples for creating hands free applications. Has anyone else had this experience?
This issue was resolved in an update to OS X

Native way to get the feature report descriptor of HID device?

We have some HID devices (touch digitizers) that communicate with an internal R&D tool. This tool parses the raw feature reports from the devices to draw the touch reports along with some additional data that are present in the raw feature report but filtered out by the HID driver of Windows 7 (eg, pressure data is not present in WM_TOUCH messages).
However, we have started working with some devices that may have different firmware variants, and thus that do not share the same ordering or bytelength of the fields and I need to modify our R&D tool so that it will adapt transparently to all the devices.
The devices come from the same manufacturer (ourselves) and share the same device info, so using these fields to differentiate between the different firmwares is not an option. What I would like to do is to get the HID feature report descriptor sent by the device and update dynamically our feature report parsing method based on this information.
However, I didn't manage to find the correct method to call in order to get this descriptor when browsing the Windows API. What I have found so far is the Raw Input page on MSDN, but I'm not sure what to do next. Can I find the required information in the RID_DEVICE_HID structure ? Or do I need to call a completely different API ?
Thanks in advance for your help!
Ok, finally I've got something (almost completely) functional. As inferred by mcoill, I used the HidP_xxx() family of functions, but it needs a little bit of data preparation first.
I based my solution on this example code that targets USB joysticks and adapted it to touch digitizer devices.
If someone else also gets confused by the online doc, here are the required steps involved in the process:
registering the application for a Raw Input device at launch.
This is done by calling the function RegisterRawInputDevice(&Rid, 1, sizeof(Rid)), where Rid is a RAWINPUTDEVICE with the following properties set (in order to get a touch digitizer) :
Rid.usUsage = 0x04;
Rid.usUsagePage = 0x0d;
Rid.dwFlags = RIDEV_INPUT_SINK;
registering a callback OnInput(LPARAM lParam) for the events WM_INPUT since the Rid device will generate this type of events;
the OnInput(LPARAM lParam) method will get the data from this event in two steps:
// Parse the raw input header to read its size.
UINT bufferSize;
GetRawInputData(HRAWINPUT)lParam, RID_INPUT, NULL, &bufferSize, sizeof(RAWINPUTHEADER));
// Allocate memory for the raw input data and retrieve it
PRAWINPUT = (PRAWINPUT)HeapAlloc(GetProcessHeap(), 0, bufferSize);
GetRawInputData(HRAWINPUT)lParam, RID_INPUT, rawInput /* NOT NULL */, &bufferSize, sizeof(RAWINPUTHEADER));
it then calls a parsing method that creates the HIDP_PREPARSED_DATA structure required by the lookup functions:
// Again, read the data size, allocate then retrieve
GetRawInputDeviceInfo(rawInput->header.hDevice, RIDI_PREPARSEDDATA, NULL, &bufferSize);
PHIDP_PREPARSED_DATA preparsedData = (PHIDP_PREPARSED_DATA)HeapAlloc(heap, 0, bufferSize);
GetRawInputDeviceInfo(rawInput->header.hDevice, RIDI_PREPARSEDDATA, preparsedData, &bufferSize);
The preparsed data is split into capabilities:
// Create a structure that will hold the values
HidP_GetCaps(preparsedData, &caps);
USHORT capsLength = caps.NumberInputValueCaps;
PHIDP_VALUE_CAPS valueCaps = (PHIDP_VALUE_CAPS)HeapAlloc(heap, 0, capsLength*sizeof(HIDP_VALUE_CAPS));
HidP_GetValueCaps(HidP_Input, valueCaps, &capsLength, preparsedData);
And capabilities can be asked for their value:
// Read sample value
HidP_GetUsageValue(HidP_Input, valueCaps[i].UsagePage, 0, valueCaps[i].Range.UsageMin, &value, preparsedData, (PCHAR)rawInput->data.hid.bRawData, rawInput->data.hid.dwSizeHid);
Wouldn't HidP_GetPReparsedData(...), HidP_GetValueCaps(HidP_Feature, ...) and their ilk give you enough information without having to get the raw feature report?
HIDClass Support Routines on MSDN

What does an Audio Unit Host need to do to make use of non-Apple Audio Units?

I am writing an Objective-C++ framework which needs to host Audio Units. Everything works perfectly fine if I attempt to make use of Apple's default units like the DLS Synth and various effects. However, my application seems to be unable to find any third-party Audio Units (in /Library/Audio/Plug-Ins/Components).
For example, the following code snippet...
CAComponentDescription tInstrumentDesc =
CAComponentDescription('aumu','dls ','appl');
AUGraphAddNode(mGraph, &tInstrumentDesc, &mInstrumentNode);
AUGraphOpen(mGraph);
...works just fine. However, if I instead initialize tInstrumentDesc with 'aumu', 'NiMa', '-Ni-' (the description for Native Instruments' Massive Synth), then AUGraphOpen() will return the OSStatus error badComponentType and the AUGraph will fail to open. This holds true for all of my third party Audio Units.
The following code, modified from the Audacity source, sheds a little light on the problem. It loops through all of the available Audio Units of a certain type and prints out their name.
ComponentDescription d;
d.componentType = 'aumu';
d.componentSubType = 0;
d.componentManufacturer = 0;
d.componentFlags = 0;
d.componentFlagsMask = 0;
Component c = FindNextComponent(NULL, &d);
while(c != NULL)
{
ComponentDescription found;
Handle nameHandle = NewHandle(0);
GetComponentInfo(c, &found, nameHandle, 0, 0);
printf((*nameHandle)+1);
printf("\n");
c = FindNextComponent(c, &d);
}
After running this code, the only output is Apple: DLSMusicDevice (which is the Audio Unit fitting the description 'aumu', 'dls ', 'appl' above).
This doesn't seem to be a problem with the units themselves, as Apple's auval tool lists my third party Units (they validate too).
I've tried running my test application with sudo, and the custom framework I'm working on is in /Library/Frameworks.
Turns out, the issue was due to compiling for 64-bit. After switching to 32-bit, everything began to work as advertised. Not much of a solution I guess, but there you have it.
To clarify, I mean changing the XCode Build Setting ARCHS to "32-bit Intel" as opposed to the default "Standard 32/64-bit Intel".
First of all, I'm going to assume that you initialized mGraph by calling NewAUGraph(&mGraph) instead of just declaring it and then trying to open it. Beyond that, I suspect that the problem here is with your AU graph, not the AudioUnits themselves. But to be sure, you should probably try loading the AudioUnit manually (ie, outside of a graph) and see if you get any errors that way.

Multiple mice on OS X

I am developing an OS X application that is supposed to take input from two mice. I want to read the motion of each mouse independently. What would be the best way to do this?
If that is not possible, is there a way to disable/enable either of the mice programmatically?
The HID Class Device Interface is definitely what you need. There are basically two steps:
First you need to find the mouse devices. To do this you need to construct a matching dictionary and then search the IO Registry with it. There is some sample code here, you will need to add some additional elements to the dictionary so you just get the mice instead of the all HID devices on the system. Something like this should do the trick:
// Set up a matching dictionary to search the I/O Registry by class
// name for all HID class devices`
hidMatchDictionary = IOServiceMatching(kIOHIDDeviceKey);
// Add key for device usage page - 0x01 for "Generic Desktop"
UInt32 usagePage = 0x01;
CFNumberRef usagePageRef = ::CFNumberCreate( kCFAllocatorDefault, kCFNumberLongType, &usagePage );
::CFDictionarySetValue( hidMatchDictionary, CFSTR( kIOHIDPrimaryUsagePageKey ), usagePageRef );
::CFRelease( usagePageRef );
// Add key for device usage - 0x02 for "Mouse"
UInt32 usage = 0x02;
CFNumberRef usageRef = ::CFNumberCreate( kCFAllocatorDefault, kCFNumberLongType, &usage );
::CFDictionarySetValue( hidMatchDictionary, CFSTR( kIOHIDPrimaryUsageKey ), usageRef );
::CFRelease( usageRef );
You then need to listen to the X/Y/button queues from the devices you found above. This sample code should point you in the right direction. Using the callbacks is much more efficient than polling!
The HID code looks much more complex than it is - it's made rather "wordy" by the CF stuff.
It looks like the HID Manager is what you're looking for.
You're going to want to check out the I/O Kit and HID (Human Interface Device) manager stuff.
HID manager is part of I/O Kit, so looking into there might be useful. There are two APIs for HID management, the older API is a bit more painful and then you have the new 10.5 and above API which is a bit more comfortable.
Important thing to understand is this isn't going to probably be just a quick fix, it may take some significant work to get it running. If you can assume you'll have 10.5 or better installed, using the Leopard API will definitely help.
Also; depending on how you accomplish what you're doing, may be important for you to hide the mouse cursor as it may still move a lot even if you're receiving the information from both mice. If your application grabs the screen, I'd use CoreGraphics to disable the cursor and just draw my own.
You might also consider finding a wrapper for one of these APIs, an example can be found in this question.
Unless you can force one of the mice to not be dealt with as a mouse, both will continue to control the pointer. However, you can use IOKit to write a custom USB HID driver to allow your app to read from one or both of the mice (although this would probably interfere with using them as normal mice). Building Customized User Client Drivers for USB Devices would be a good place to start for how to interact directly with USB mice.
You could look at the USB/PS-2 device interrupt.
Even if you don't want to rewrite a so called driver, it could be usefull since all the mice send their data through.
You could also check this page that could give some hints http://multicursor-wm.sourceforge.net/
maybe it's a solution for you to use usb->rsr232 converter and go by reading the serial port by yourself ?

How do you retrieve stylus pressure information on windows?

Is anyone aware of a sane way to get tablet/stylus pressure information on Windows?
It's possible to distinguish stylus from mouse with ::GetMessageExtraInfo, but you can't get any more information beyond that. I also found the WinTab API in a out of the way corner of the Wacom site, but that's not part of windows as far as i can tell, and has a completely distinct event/messaging system from the message queue.
Given all I want is the most basic pressure information surely there is a standard Win32/COM API, is anyone aware of what it might be?
The current way to do this is to handle WM_POINTERnnn msgs.
Note this is for Win 8 and later.
Note you will get these msgs for touch AND pen, so you'll need to know the pointerType in order to test for pen. The WPARAM received by a WNDPROC for WM_POINTERnnnn msgs such a WM_POINTERUPDATE and other msgs contains the pointer id which you will need in order to request more info. Empirically I found that WM_POINTERUPDATE results in info that contains pressure data whereas if the pointer flags indicate down/up there is no pressure info.
const WORD wid = GET_POINTERID_WPARAM(wParam);
POINTER_INFO piTemp = {NULL};
GetPointerInfo(wid, &piTemp);
if (piTemp.pointerType == PT_PEN
{
UINT32 entries = 0;
UINT32 pointers = 0;
GetPointerFramePenInfoHistory(wid, &entries, &pointers, NULL); // how many
// TODO, allocate space needed for the info, process the data in a loop to retrieve it, test pointerInfo.pointerFlags for down/up/update.
}
Once you know you are dealing with pen, you can get the pressure info from the POINTER_PEN_INFO struct.
This is similar to handling touch although for touch you'd want gesture recognition and inertia. There is a Microsoft sample illustrating using these functions.
It's part of a Build talk:
https://channel9.msdn.com/Events/Build/2013/4-022
You need to use the Tablet PC Pen/Ink API. The COM version of the API lives in InkObj.dll. Here is a starting point for documentation: http://msdn.microsoft.com/en-us/library/ms700664.aspx
If I remember correctly, InkObj.dll is available on Windows XP SP2 and all later Windows client OSes, regardless of whether the machine is a Tablet PC.
UPDATE:
It's been a number of years since I initially provided this answer, but wintab has become the de facto standard, and Ntrig more or less folded, eventually building a wrapper to allow for the wintab API to be accessed via this digitizer.
(http://www.tabletpcbuzz.com/showthread.php?37547-N-trig-Posts-WinTAB-Support-Driver)
This is a pretty late response, but recently my wife and I purchased a Dell XT tablet PC, which as it turns out actually uses NTrig, a suite of interfaces that utilize Ink, the accepted new windows API that shipped with Windows XP Tablet edition, then SP 2 and all versions thereafter.
A lot of Wacom tablets and others use the Wintab API, which is not currently open nor really permitted to use. From what I hear the folks who maintain it are pretty sue-happy.
So it depends on what type of tablet you're using, and the drivers you have installed for it. In my biased opinion, you should work with Ink, as it provides (or at least through NTrig and Windows 7 WILL provide) multi-touch capability and will likely be the new standard for tablet interfaces. But as of now, NTrig devices do not translate their pressure and angle information to common Wintab-based applications, such as Photoshop or Corel Painter. The applications tend to require at least some support for Microsoft's Tablet API in order to function properly.
If using UWP Windows Runtime then it's quite straightforward. The PointerEventArgs event seems to have all necessary data.
Modified Core App (C++/WinRT) template project snippet from Visual Studio 2019:
void OnPointerMoved(IInspectable const &, PointerEventArgs const &args)
{
if (m_selected)
{
float2 const point = args.CurrentPoint().Position();
m_selected.Offset(
{
point.x + m_offset.x,
point.y + m_offset.y,
0.0f
});
// (new!) Change sprite color based on pen pressure and tilt
auto sprite = m_selected.as<SpriteVisual>();
auto const props = args.CurrentPoint().Properties();
auto const pressure = props.Pressure();
auto const orientation = props.Orientation() / 360.0f;
auto const tiltx = (props.XTilt() + 90) / 180.0f;
auto const tilty = (props.YTilt() + 90) / 180.0f;
Compositor compositor = m_visuals.Compositor();
sprite.Brush(compositor.CreateColorBrush({
(uint8_t)(pressure * 0xFF),
(uint8_t)(tiltx * 0xFF),
(uint8_t)(tilty * 0xFF),
(uint8_t)(orientation * 0xFF)
}));
}
}
Similar code will likely work in C#, JavaScript, etc.

Resources