I have used JUCE about half a year.Now I'm writing a app need to check and open midi device on OSX .I set up a background thread to check updated midi device all the time by using OSX's API . But devices will either never be found, if they are not connected on startup, MIDIGetNumberOfDestinations() andMIDIGetNumberOfSources() coundn't update about the plugged device . If they are connected on startup,app can find out the device,then unplug ,the two functions didn't found out that device had been unpluged. I need to shutdown and restart my app.
Yes - I noticed this last week too. Couldn't find a workaround for it though! It just seems that OSX is caching the devices and doesn't refresh the list until the app restarts. If anyone can find any clues about a way to force the OS to refresh, then please let me know and I'll add it!
It is really a bad news to me. Now i noticed click on the button event can cause the two functions. If use a timer to call the two functions,can it do?
I haven’t checked out the code… just read the description:
Looking at the code, it looks like he’s responding to MIDINotificationMessageID values kMIDIMsgPropertyChanged, kMIDIMsgObjectRemoved & kMIDIMsgObjectAdded (MIDIServices.h)
I think you're misunderstanding - this is not about finding out when a device is added or removed.
The problem is that every time your app asks CoreMidi for its list of devices, it returns the same list. Even after new devices have been added, they're not on the list. If you restart your app, then it gets the correct list, but the OS seems to be caching this somewhere, and I don't know how to force it to re-scan.
Okay, not seeing that on 10.8.5 – using the JUCE Host if I add a USB keyboard and reopen the AudioDeviceSelectorComponent the keyboard is listed without restarting the app… Possibly the issue is with 10.9 and later where the prefs and system files are cached, but the OP is running 10.7
Well I checked on 10.10 as well… and found some issues…
Add the following code to juce_Mac_CoreMIDI.cpp globalSystemChangeCallback():
static void globalSystemChangeCallback (const MIDINotification* message, void*)
{
// TODO.. Should pass-on this notification..
const MIDIObjectAddRemoveNotification* addRemoveMsg = reinterpret_cast<const MIDIObjectAddRemoveNotification*>(message);
StringArray tempArray;
switch (message->messageID)
{
case kMIDIMsgObjectAdded: if (addRemoveMsg != nullptr)
{
if (addRemoveMsg->childType == kMIDIObjectType_Source)
{
DBG (newLine + "MIDI Source Device added");
tempArray = findDevices (false);
for (int i = 0; i < tempArray.size(); ++i)
DBG("tempArray[" + String (i) + "] = " + tempArray[i]);
}
if (addRemoveMsg->childType == kMIDIObjectType_Destination)
{
DBG (newLine + "MIDI Destination Device added");
tempArray = findDevices (true);
for (int i = 0; i < tempArray.size(); ++i)
DBG("tempArray[" + String (i) + "] = " + tempArray[i]);
}
}
break;
case kMIDIMsgObjectRemoved: if (addRemoveMsg != nullptr)
{
if (addRemoveMsg->childType == kMIDIObjectType_Source)
{
DBG (newLine + "MIDI Source Device removed");
tempArray = findDevices (false);
for (int i = 0; i < tempArray.size(); ++i)
DBG("tempArray[" + String (i) + "] = " + tempArray[i]);
}
if (addRemoveMsg->childType == kMIDIObjectType_Destination)
{
DBG (newLine + "MIDI Destination Device removed");
tempArray = findDevices (true);
for (int i = 0; i < tempArray.size(); ++i)
DBG("tempArray[" + String (i) + "] = " + tempArray[i]);
}
}
break;
case kMIDIMsgSetupChanged: if (addRemoveMsg != nullptr)
{
DBG (newLine + "MIDI Setup Changed");
}
break;
case kMIDIMsgPropertyChanged: if (addRemoveMsg != nullptr)
{
DBG (newLine + "MIDI Property Changed");
}
default: break;
}
}
and I found that if there were no devices on the system then the Notification is never registered… so I just moved the code you use to register the Notification in openDevice so it’ll get registered even if there are no devices in the system…
MidiOutput* MidiOutput::openDevice (int index)
{
MidiOutput* mo = nullptr;
MIDIClientRef client = CoreMidiHelpers::getGlobalMidiClient(); // <<---------- Move this here
if (isPositiveAndBelow (index, (int) MIDIGetNumberOfDestinations()))
{
MIDIEndpointRef endPoint = MIDIGetDestination ((ItemCount) index);
CoreMidiHelpers::ScopedCFString pname;
if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &pname.cfString)))
{
MIDIPortRef port;
if (client != 0 && CHECK_ERROR (MIDIOutputPortCreate (client, pname.cfString, &port)))
{
mo = new MidiOutput();
mo->internal = new CoreMidiHelpers::MidiPortAndEndpoint (port, endPoint);
}
}
}
return mo;
}
The issue now is that kMIDIMsgObjectAdded isn’t triggered until after kMIDIMsgObjectRemoved is triggered – so if you run the JUCE Host and add a device it won’t trigger kMIDIMsgObjectAdded, but if you then remove and add it again it’ll always trigger kMIDIMsgObjectAdded. kMIDIMsgPropertyChanged is always triggered, so perhaps that could be used instead of kMIDIMsgObjectAdded.
Also wondered if these should trigger a callback to AudioDeviceSelectorComponent to let it know a device has been added or removed and the developer could decide if they want to respond to it to repopulate their device list…???
We are having strange issues in re-producing this bug. It seems to be a bit random and lately we cannot reproduce it at all. I've created a simple test project which updates the list of MIDI devices when clicking on "update". Can anyone give us a procedure that will trigger the bug with this test project?
My program which updates the list of midi devices deponds on a background thread,but not clicking on a button.In thread::run(), i use MIDIGetNumberOfDestinations() and MIDIGetNumberOfSources() to get the number of devices all the time,but the number never changed when i connected or unpluged devices.
old thread.. if it's still relevant, did you make sure that you're polling the number of devices on the main message thread? It seems that Core MIDI open/close/list methods don't like being called from threads other than NSApplicationMain. Making sure that MIDIGetNumberOfSources is called from NSApplicationMain did the trick for me
It's more or less like this: I had this fully native app, doing the first call to MIDIClientCreate during the initialization on a background thread. Then I was polling the midi inputs on NSApplicationMain, and it wouldn't refresh the list. Moving the first call to MIDICLientCreate to the main thread fixed the misbehavior.
So it seems there are two things: 1. in an application with UI, the first call to MIDIClientCreate should be on NSApplicationMain, otherwise also the subsequent calls to MIDIGetNumberOfSources/MIDIGetNumberOfDestinations on the NSApplicationMain won't refresh the list. 2. the application needs to have a NSApplicationMain
I checked with this console app, that has no NSApplicationMain, and the sources aren't updated: http://pastebin.com/kS9kX9hP
Checking the JUCE source code, yes, a
jassert (MessageManager::getInstance()->isThisTheMessageThread());
could be added in findDevices.
I’m new to juce. The mac I’m using has no physical midi device (not sure if that matters), but when I attempt to build any of the examples in the new juce download I get :
JIT process crashed !
Program used external function
’MIDIGetNumberOfSources’ which could not be resolved!
That slightly cryptic crash is actually nothing to do with MIDI, it’s probably because all your JUCE modules have broken paths in the Projucer, so your program’s static initialisers are failing because all the JUCE code is basically missing. Fix your module paths and it should be OK.
We actually hit the same thing here yesterday, and will add some better error messages to prevent you trying to run code if there are modules with broken paths.