IOBluetoothHandsFreeDevice audio static - macos

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

Related

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

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++).

NSInputStream never HasBytesAvailable on iOS9 (classic Bluetooth)

I have a cross platform (Xamrin) app that does some classic Bluetooth communication and is working absolutely fine on iOS8. However, after re-building and running it on iOS9 I can't get the NSInputStream to ever have "HasBytesAvailable"= true. Note: I followed all the instructions from Xamarin's website.
I tried both assigning a delegate to the InputStream and waiting on the NSRunLoop but the stream never seems to have bytes available. The event only fires (on iOS9) when the Input stream is opened (on iOS8 it fires as expected).
Here is a snippet of the code that does the reading successfully on iOS8 (delegate method):
EAsession.InputStream.Delegate = new Foo();
EAsession.InputStream.Schedule(NSRunLoop.Current,NSRunLoop.NSDefaultRunLoopMode);
EAsession.InputStream.Open();
(NSRunLoop.Current).RunUntil(NSDate.FromTimeIntervalSinceNow(2));
Where Foo is a class that implements: NSObject, INSStreamDelegate
public class Foo :NSObject, INSStreamDelegate
{
[Export("stream:handleEvent:")]
public void HandleEvent(Foundation.NSStream theStream, Foundation.NSStreamEvent streamEvent)
{
//Code to read bytes here
}
To make sure there really are bytes sent to the iPhone5 I modified the external Bluetooth device to just echo any bytes received.
Using either method (delegate or waiting on NSRunLoop) on iOS8, the echo arrives immediately. However when I change the target device to the iOS9 I can wait forever and HasBytesAvailable would always be false.
I even tried reading regardless of HasBytesAvailable being false, but nothing is being read (no big surprise there I guess).
Moreover, I tried building using both Xcode6.4 and Xcode 7 with same result.
At the moment I am out of ideas so any help would be greatly appreciated!
Thanks in advance!
EDIT:
I contacted Xamarin and I am writing a test app for them to test whether it is an Apple issue or Xamarin issue.
Also, see the comment in this link about Bluetooth... perhaps related?
So I finally solved it,
I am not entirely sure what was the problem eventually as I did a few things:
Update to Xamarin beta stream ( 5.9.7 build 12).
Update to the latest iOS (9.0.2)
Use Xcode 7.0
And change where I called the delegate, open stream and schedule.
The change to the code was that instead of handling the opening, scheduling and delegate assignment in the receiving and sending methods separately, I did it at the same place. Namely, when creating the EASession I assigned all of the above like so:
s = new EASession(device, protocol);
output_stream = s.OutputStream;
input_stream = s.InputStream;
output_stream.Delegate=this;
output_stream.Schedule(NSRunLoop.Current, NSRunLoop.NSDefaultRunLoopMode);
output_stream.Open();
input_stream.Delegate = this;
input_stream.Schedule(NSRunLoop.Current, NSRunLoop.NSDefaultRunLoopMode);
input_stream.Open();
At the moment I am not unscheduling the input and output streams and I am not sure it this is the right thing to do. I'll try and keep this updated as I move from working to a nice looking code that works...
Unlike what Bluecode describes I definitely needed to use my input stream and I was always opening it; so I am not sure if it was the same problem. I am still unsure what was the solution to the problem as it can be either one or a combination of the above. I might experiment a bit more into this later on when I have more time to see which one was the single solution? If any.
Hope this helps any.
Cheers
I was able to solve this problem by opening an InputStream to the EAAccessory that I had no intention of using. My guess is that both an Input and Output streams are required as part of the new iOS9 Bluetooth contract. This is the relevant update to my working code:
accessory.Delegate = new EAAccessoryDelegateHandler ();
if (PrinterSession == null)
{
PrinterSession = new EASession (accessory, PrintProtocol);
}
var mysession = PrinterSession;
mysession.InputStream.Open();
mysession.OutputStream.Delegate = this;
mysession.OutputStream.Schedule (NSRunLoop.Current, NSRunLoop.NSDefaultRunLoopMode);
mysession.OutputStream.Open ();
Then when I close my OutputStream I close the unused InputStream immediately afterwards.

IOBluetooth is returning no characteristics for some services

I’m trying to read characteristics from a service on a Bluetooth LE device. For some reason, for some characteristics, after calling -[CBPeripheralManager discoverCharacteristics:forService], the peripheral:didDiscoverServices: callback is getting 0 characteristics. Are there any workarounds to allow me to read the characteristics of this service?
When installing Hardware IO Tools for Xcode and running PacketLogger, it is apparent that the discoverServices call is causing a 0x08 Read By Type request (Bluetooth® Core Specification Volume 3, Part F, Section 3.4.4.1) with Starting Handle=0x1a, Ending Handle=0x1a, Attribute Type=0x2803.
Also, by defining the following class extensions to read the protected fields, it is apparent that the service that I’m interested in, 0x180a=Device Information, also has ATT handles too close to comfort: _startHandle=0x1a and _endHandle=0x1a.
#implementation CBService(ProtectedProps)
- (NSNumber*) startHandle {
return self->_startHandle;
}
- (NSNumber*) endHandle {
return self->_endHandle;
}
#end
#implementation CBCharacteristic(ProtectedProps)
- (NSNumber*) descriptorHandle {
return self->_handle;
}
- (NSNumber*) valueHandle {
return self->_valueHandle;
}
#end
By the way, when I read the device from LightBlue on an iPhone 4S, the service works fine, and I can read the 3 characteristics of this service.
I’m testing this on OSX 10.9 Mavericks with Apple Bluetooth Software Version: 4.2.0f6 12982. The device that I’m testing is a Livescribe 3.
Here is a table of the actual GATT handles, CBService handles, and UUIDs. It looks like having a 16-bit UUID after a 128-bit UUID messed up the table. Bluetooth 4.0 section 3.1 states that 16-bit UUID services “should” be grouped together for performance, but I don’t think they must.
0001–0004 0001–0004 uuid:1801
0005–0009 0005–0009 uuid:1800
0010–0019 0010–0019 uuid:128 bit UUID
001A–0020 001A–001A uuid:180a
0021–0023 missing uuid:180f
0024–002A 0021–0027 uuid:128 bit UUID
002E–0031 002B–002E uuid:128 bit UUID
The bug in reading 16-bit UUIDs interleaved with 128-bit UUIDs was probably fixed in OSX 10.9.1. However, the bad values were still cached because the computer was still bonded with the peripheral. So if you previously connected to the peripheral, the bug will still manifest itself no matter how many times you reconnect or restart because of the bad cached GATT values.
I had to erase the bonding cache /Library/Preferences/com.apple.Bluetooth.plist and kill blued. See my other question Does blued cache ATT values, and how to clear the cache? for more details.

windows audio waveOutSetVolume cross connects midiOutSetVolume

i have a program that generates both midi and wav audio. i need to be able to control the volume and balance of midi and audio separately and in theory, its seems like all i need to do is call
unsigned short left = (unsigned short)(wavvol*wavbal/100.0);
unsigned short right = (unsigned short)(wavvol*(100-wavbal)/100.0);
MMRESULT err = waveOutSetVolume(hWaveOut, left | (right<<16)); // for audio
and
unsigned short left = (unsigned short)(midivol*midibal/100.0);
unsigned short right = (unsigned short)(midivol*(100-midibal)/100.0);
MMRESULT err = midiOutSetVolume(s_hmidiout, left | (right<<16)); // for midi
for midi
the problem it, controlling midi volume sets wave volume and visa-verse, its like they are glues together inside windows
does anyone know if there is a way to unglue them?
BTW, i'm on windows 7, i know Microsoft messed up audio in win7. on XP i had an audio control panel with separate controls for midi and wave, that seems to have gone. i guess they just mix it down internally now and don't expose that even at the API level so maybe i've answered my own question.
still interested to know if there is a better answer through.
thanks, steve
I don't think they can be separated. You could move to the newer IAudioClient interface and use two WASAPI sessions to control the volume separately - one for wav and one for midi. This won't work on anything below Vista tho.
Alternatively you could track the volume levels in-code and as long as you don't play back both wav and midi at the same time reset them before playback.

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