cinder / Cinder

Cinder is a community-developed, free and open source library for professional-quality creative coding in C++.
http://libcinder.org
Other
5.3k stars 940 forks source link

OSX: CoreAudio errors when connected to bluetooth speaker #1054

Open notlion opened 9 years ago

notlion commented 9 years ago

For some unknown reason, my bluetooth speaker shows up twice in the device list. The first instance (and the one that Cinder tries to connect to) has 0 input channels and 0 output channels, and this is causing the call to setCurrentDeviceImpl to error here:

https://github.com/cinder/Cinder/blob/81e55e7eb76c7c856633eaed4679aca5ab6531f4/src/cinder/audio/cocoa/DeviceManagerCoreAudio.cpp#L249

I'm not sure the correct thing to do in this case.. I suppose we could ignore all devices with zero inputs / outputs? The other odd thing is that this device has sample rate / samples per block while the others don't.

devices

notlion commented 9 years ago

Also, I should note that this is not a problem on iOS with the same device.

richardeakin commented 9 years ago

Hm, would you mind stepping through DeviceManagerCoreAudio::getDevices() when it first adds the devices to see why the properties are empty? I don't have a bluetooth device to test on my end, but I will be spending time on the mac audio::Device side of things in the near future so hopefully can address the problem in that pass.

notlion commented 9 years ago

Hmm. It looks like getDevices() doesn't set any properties on the Devices except the keys. I do get the "multiple Devices with same key: cyl - a ffff" warning, though.

richardeakin commented 9 years ago

Ah that's right, yes the properties are lazy loaded so we don't query every param for all hardware devices plugged in on startup.

Still trying to figure out how we can figure this out. As a starting point, what does Device::printDevicesToString() give you? From what you've listed already, it looks like we're not able to query any of the devices properties, but the result of that function should give you it all in one string.

Also maybe it's worth trying to run the device in something like pure data, see what params it comes up with and if it is able to use it. What is the model, anyway?

notlion commented 9 years ago

Aha! Useful function :)

Looks like I was wrong about one device not having any inputs or outputs. One device has an input and the other has outputs. I'm not seeing where the output device is selected, but we could probably check there the number of outputs to disambiguate between devices.

-- cyl --
     key: cyl - a ffff
     inputs: 1, outputs: 0
     samplerate: 8000, frames per block: 512
-- cyl --
     key: cyl - a ffff
     inputs: 0, outputs: 2
     samplerate: 44100, frames per block: 512
...
notlion commented 9 years ago

The device is a UE Megaboom. Here's what System Info has to say about it:

screen shot 2015-08-05 at 12 55 17 pm

notlion commented 9 years ago

Works fine in PD. It only shows up once each in the inputs and outputs list and audio plays through.

screen shot 2015-08-05 at 12 58 43 pm

richardeakin commented 9 years ago

Ah, well I was working around the identical UID's for two devices, as a macbook's system input and output devices share a similar problem. But I haven't accounted for both the name and the ID being the same.

Are you trying to use the default output device (by either not specifying or using Device::getDefaultOutput()), or are you asking for a device by name / key? Reason I ask is that if you're requesting the default output device it should be using the kAudioHardwarePropertyDefaultOutputDevice property (here) and I would expect that to always return to you the one that has outputs. But for you is ir mistakenly returning the input device for your hardware?

notlion commented 9 years ago

Yeah it's crummy that this speaker doesn't have different names for the input / output.. Perhaps it's because I gave it a custom name. In any case.. if it happens it's probably something Cinder should support if possible.

This is the extent of my audio setup. I'm not specifying which output at all, since I just want the default one that the OS is using at the time the app launches.

mGainNode = ctx->makeNode(new ci::audio::GainNode(1.0f));
mGainNode->setName("MasterGain");

auto fmt = cipd::PureDataNode::Format();
fmt.setAutoEnable(true);

mPdNode = ctx->makeNode(new CustomPureDataNode(fmt));
mPdNode >> mGainNode >> ctx->getOutput();
notlion commented 9 years ago

It's probably finding the correct device, but then looking it up by key selects the wrong version since there's currently no way to tell the difference.

richardeakin commented 9 years ago

I agree, we should support anything the system can throw at it.

My work in this branch has some improvements over device handling (it holds onto a DeviceRef for the default output and input until it changes on the system), that might work around your specific problem but a) it isn't complete (will crash on stale pointers in rare cases) and b) doesn't solve the general issue that you can't look up a specific device by key. Will think on how to solve this in a robust manner, but in the meantime perhaps enforcing that getDefaultOutput() returns a DeviceRef that actually has output channels is plausible. Might be a bit hacky, thinking about it..