/[svn]/linuxsampler/trunk/src/drivers/midi/MidiInputDeviceCoreMidi.cpp
ViewVC logotype

Contents of /linuxsampler/trunk/src/drivers/midi/MidiInputDeviceCoreMidi.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2719 - (show annotations) (download)
Wed Feb 25 22:46:38 2015 UTC (9 years, 2 months ago) by schoenebeck
File size: 16806 byte(s)
* CoreMIDI: fixed auto bind feature to CoreMIDI ports that go online
* CoreMIDI: fixed minor memory leak
* Bumped version (1.0.0.svn61)

1 /***************************************************************************
2 * *
3 * Copyright (C) 2004, 2005 Grame *
4 * Copyright (C) 2005 - 2015 Christian Schoenebeck *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the Free Software *
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
19 * MA 02111-1307 USA *
20 ***************************************************************************/
21
22 #include "MidiInputDeviceCoreMidi.h"
23 #include "MidiInputDeviceFactory.h"
24
25 namespace LinuxSampler {
26
27 // *************** privat static functions ***************
28 // *
29
30 static void _bridgeInputEvent(const MIDIPacketList* packetList, void* /*device*/, void* port) {
31 //MidiInputDeviceCoreMidi* pDevice = (MidiInputDeviceCoreMidi*) device;
32 MidiInputDeviceCoreMidi::MidiInputPortCoreMidi* pPort = (MidiInputDeviceCoreMidi::MidiInputPortCoreMidi*) port;
33 pPort->ProcessMidiEvents(packetList);
34 }
35
36 static String _getDisplayName(MIDIObjectRef object) {
37 CFStringRef name = nil;
38 if (MIDIObjectGetStringProperty(object, kMIDIPropertyDisplayName, &name) != noErr) {
39 dmsg(1,("CoreMIDI: could not resolve display name of object\n"));
40 return "";
41 }
42 // convert NSString to C string
43 char* buf = new char[256];
44 if (!CFStringGetCString(name, buf, 256, kCFStringEncodingUTF8)) {
45 dmsg(1,("CoreMIDI: could not convert display name string\n"));
46 delete[] buf;
47 return "";
48 }
49 String result = buf;
50 delete[] buf;
51 return result;
52 }
53
54
55 // *************** ParameterName ***************
56 // *
57
58 MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterName::ParameterName(MidiInputPort* pPort) throw (Exception) : MidiInputPort::ParameterName(pPort, "Port " + ToString(pPort->GetPortNumber())) {
59 OnSetValue(ValueAsString()); // initialize port name
60 }
61
62 void MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterName::OnSetValue(String s) throw (Exception) {
63 //TODO: renaming of port to be implemented
64 }
65
66 // *************** ParameterCoreMidiBindings ***************
67 // *
68
69 MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterCoreMidiBindings::ParameterCoreMidiBindings(MidiInputPortCoreMidi* pPort) : DeviceRuntimeParameterStrings( std::vector<String>() ) {
70 this->pPort = pPort;
71 }
72
73 String MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterCoreMidiBindings::Description() {
74 return "Bindings to other CoreMidi clients";
75 }
76 bool MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterCoreMidiBindings::Fix() {
77 return false;
78 }
79
80 std::vector<String> MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterCoreMidiBindings::PossibilitiesAsString() {
81 std::vector<String> res;
82
83 const ItemCount sourceCount = MIDIGetNumberOfSources();
84 for (ItemCount i = 0; i < sourceCount; ++i) {
85 MIDIEndpointRef source = MIDIGetSource(i);
86 res.push_back(
87 _getDisplayName(source)
88 );
89 }
90
91 return res;
92 }
93
94 void MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterCoreMidiBindings::OnSetValue(std::vector<String> vS) throw (Exception) {
95 for (int k = 0; k < vS.size(); ++k) {
96 const ItemCount sourceCount = MIDIGetNumberOfSources();
97 for (ItemCount i = 0; i < sourceCount; ++i) {
98 MIDIEndpointRef source = MIDIGetSource(i);
99 String name = _getDisplayName(source);
100 if (name == vS[k]) {
101 pPort->connectToSource(source);
102 goto matchFound;
103 }
104 }
105 throw MidiInputException("No CoreMIDI source '" + vS[k] + "' found to connect to");
106 matchFound:
107 ; // noop
108 }
109 }
110
111 // *************** ParameterAutoBind ***************
112 // *
113
114 MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterAutoBind::ParameterAutoBind(MidiInputPortCoreMidi* pPort)
115 : DeviceRuntimeParameterBool(true)
116 {
117 this->pPort = pPort;
118 }
119
120 String MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterAutoBind::Description() {
121 return "Whether port shall automatically be connected to all CoreMIDI source endpoints.";
122 }
123
124 bool MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterAutoBind::Fix() {
125 return false;
126 }
127
128 void MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterAutoBind::OnSetValue(bool b) throw (Exception) {
129 if (b) pPort->connectToAllSources();
130 }
131
132
133 // *************** MidiInputPortCoreMidi ***************
134 // *
135
136 int MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::pPortID = 0;
137
138 MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::MidiInputPortCoreMidi(MidiInputDeviceCoreMidi* pDevice) throw (MidiInputException) : MidiInputPort(pDevice, -1) {
139 this->pDevice = pDevice;
140
141 // create CoreMidi virtual destination
142
143 /* 20080105 Toshi Nagata */
144 char buf[32];
145 CFStringRef str;
146 snprintf(buf, sizeof buf, "LinuxSampler_in_%d", pPortID);
147 str = CFStringCreateWithCString(NULL, buf, kCFStringEncodingUTF8);
148 MIDIDestinationCreate(pDevice->hCoreMidiClient, str, ReadProc, this, &pDestination);
149 /* */
150 CFRelease(str);
151
152 if (!pDestination) throw MidiInputException("Error creating CoreMidi virtual destination");
153 this->portNumber = pPortID++;
154
155 Parameters["NAME"] = new ParameterName(this);
156 Parameters["CORE_MIDI_BINDINGS"] = new ParameterCoreMidiBindings(this);
157 Parameters["AUTO_BIND"] = new ParameterAutoBind(this);
158 }
159
160 MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::~MidiInputPortCoreMidi() {
161 MIDIEndpointDispose(pDestination);
162 }
163
164 void MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ReadProc(const MIDIPacketList* pktlist, void* refCon, void* connRefCon)
165 {
166 MidiInputPortCoreMidi* port = (MidiInputPortCoreMidi*)refCon;
167 port->ProcessMidiEvents(pktlist);
168 }
169
170 void MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ProcessMidiEvents(const MIDIPacketList *pktlist) {
171 MIDIPacket *packet = (MIDIPacket *)pktlist->packet;
172 for (unsigned int i = 0; i < pktlist->numPackets; ++i) {
173 uint8_t* pData = (uint8_t*) packet->data;
174 int k = 0;
175 // A MIDIPacket can have more than one (non SysEx) MIDI event in one
176 // packet. However SysEx messages are guaranteed to be alone in one
177 // MIDIPacket.
178 do {
179 int eventSize = expectedEventSize(pData[k]);
180 if (eventSize < 0) eventSize = packet->length - k;
181
182 if (k + eventSize > packet->length) goto next_packet;
183
184 DispatchRaw(&pData[k]);
185 k += eventSize;
186 } while (k < packet->length);
187
188 next_packet:
189 packet = MIDIPacketNext(packet);
190 }
191 }
192
193 void MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::connectToSource(MIDIEndpointRef source) {
194 // check if we already have such a connection, if yes, ignore it
195 for (uint i = 0; i < bindings.size(); ++i) {
196 if (bindings[i] == source) return;
197 }
198 // now establish the CoreMIDI connection
199 MIDIPortConnectSource(pDevice->pBridge, source, this);
200 // and remember it
201 bindings.push_back(source);
202 }
203
204 void MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::connectToAllSources() {
205 const ItemCount sourceCount = MIDIGetNumberOfSources();
206 for (ItemCount i = 0; i < sourceCount; ++i) {
207 MIDIEndpointRef source = MIDIGetSource(i);
208 connectToSource(source);
209 dmsg(1,("Auto binded to CoreMIDI source '%s'\n", _getDisplayName(source).c_str()));
210 }
211 }
212
213 void MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::onNewSourceAppeared(MIDIEndpointRef source) {
214 if (((ParameterAutoBind*)Parameters["AUTO_BIND"])->ValueAsBool()) {
215 connectToSource(source);
216 dmsg(1,("Auto binded to new CoreMIDI source '%s'\n", _getDisplayName(source).c_str()));
217 }
218 }
219
220 void MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::onNewSourceDisappeared(MIDIEndpointRef source) {
221 std::vector<MIDIEndpointRef>::iterator iter = std::find(bindings.begin(), bindings.end(), source);
222 if (iter != bindings.end()) {
223 dmsg(1,("CoreMIDI source '%s' disappeared, disconnecting it.\n", _getDisplayName(source).c_str()));
224 bindings.erase(iter);
225 }
226 }
227
228 void MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::onCoreMIDIDeviceAppeared(MIDIDeviceRef device) {
229 ItemCount entityCount = MIDIDeviceGetNumberOfEntities(device);
230 for (ItemCount e = 0; e < entityCount; ++e) {
231 MIDIEntityRef entity = MIDIDeviceGetEntity(device, e);
232 onCoreMIDIEntityAppeared(entity);
233 }
234 }
235
236 void MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::onCoreMIDIDeviceDisappeared(MIDIDeviceRef device) {
237 ItemCount entityCount = MIDIDeviceGetNumberOfEntities(device);
238 for (ItemCount e = 0; e < entityCount; ++e) {
239 MIDIEntityRef entity = MIDIDeviceGetEntity(device, e);
240 onCoreMIDIEntityDisappeared(entity);
241 }
242 }
243
244 void MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::onCoreMIDIEntityAppeared(MIDIEntityRef entity) {
245 ItemCount sourceCount = MIDIEntityGetNumberOfSources(entity);
246 for (ItemCount s = 0; s < sourceCount; ++s) {
247 MIDIEndpointRef source = MIDIEntityGetSource(entity, s);
248 onNewSourceAppeared(source);
249 }
250 }
251
252 void MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::onCoreMIDIEntityDisappeared(MIDIEntityRef entity) {
253 ItemCount sourceCount = MIDIEntityGetNumberOfSources(entity);
254 for (ItemCount s = 0; s < sourceCount; ++s) {
255 MIDIEndpointRef source = MIDIEntityGetSource(entity, s);
256 onNewSourceDisappeared(source);
257 }
258 }
259
260
261 // *************** MidiInputDeviceCoreMidi ***************
262 // *
263
264 MidiInputDeviceCoreMidi::MidiInputDeviceCoreMidi(std::map<String,DeviceCreationParameter*> Parameters, void* pSampler) : MidiInputDevice(Parameters, pSampler)
265 {
266 MIDIClientCreate(CFSTR("LinuxSampler"), NotifyProc, this, &hCoreMidiClient);
267 if (!hCoreMidiClient) throw MidiInputException("Error opening CoreMidi client");
268
269 OSStatus status = MIDIInputPortCreate(hCoreMidiClient, CFSTR("bridge"), _bridgeInputEvent, this, &pBridge);
270 if (status != noErr) throw MidiInputException("Could not create bridge port for CoreMIDI client");
271
272 AcquirePorts(((DeviceCreationParameterInt*)Parameters["PORTS"])->ValueAsInt());
273 }
274
275 MidiInputDeviceCoreMidi::~MidiInputDeviceCoreMidi()
276 {
277 if (hCoreMidiClient) {
278 MIDIClientDispose(hCoreMidiClient);
279 }
280 }
281
282 MidiInputDeviceCoreMidi::MidiInputPortCoreMidi* MidiInputDeviceCoreMidi::CreateMidiPort() {
283 return new MidiInputPortCoreMidi(this);
284 }
285
286 String MidiInputDeviceCoreMidi::Name() {
287 return "COREMIDI";
288 }
289
290 String MidiInputDeviceCoreMidi::Driver() {
291 return Name();
292 }
293
294 String MidiInputDeviceCoreMidi::Description() {
295 return "Apple CoreMidi";
296 }
297
298 String MidiInputDeviceCoreMidi::Version() {
299 String s = "$Revision$";
300 return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword
301 }
302
303 void MidiInputDeviceCoreMidi::NotifyProc(const MIDINotification* message, void* refCon)
304 {
305 MidiInputDeviceCoreMidi* pDevice = (MidiInputDeviceCoreMidi*) refCon;
306
307 switch (message->messageID) {
308 case kMIDIMsgObjectAdded: {
309 MIDIObjectAddRemoveNotification* notification = (MIDIObjectAddRemoveNotification*) message;
310 switch (notification->childType) {
311 case kMIDIObjectType_Device: {
312 MIDIDeviceRef device = (MIDIDeviceRef) notification->child;
313 for (std::map<int,MidiInputPort*>::iterator iter = pDevice->Ports.begin();
314 iter != pDevice->Ports.end(); ++iter)
315 {
316 MidiInputPortCoreMidi* pPort = (MidiInputPortCoreMidi*) iter->second;
317 pPort->onCoreMIDIDeviceAppeared(device);
318 }
319 break;
320 }
321 case kMIDIObjectType_Entity: {
322 MIDIEntityRef entity = (MIDIEntityRef) notification->child;
323 for (std::map<int,MidiInputPort*>::iterator iter = pDevice->Ports.begin();
324 iter != pDevice->Ports.end(); ++iter)
325 {
326 MidiInputPortCoreMidi* pPort = (MidiInputPortCoreMidi*) iter->second;
327 pPort->onCoreMIDIEntityAppeared(entity);
328 }
329 break;
330 }
331 case kMIDIObjectType_Source: {
332 MIDIEndpointRef source = (MIDIEndpointRef) notification->child;
333 for (std::map<int,MidiInputPort*>::iterator iter = pDevice->Ports.begin();
334 iter != pDevice->Ports.end(); ++iter)
335 {
336 MidiInputPortCoreMidi* pPort = (MidiInputPortCoreMidi*) iter->second;
337 pPort->onNewSourceAppeared(source);
338 }
339 break;
340 }
341 default:
342 break;
343 }
344 break;
345 }
346
347 case kMIDIMsgObjectRemoved: {
348 MIDIObjectAddRemoveNotification* notification = (MIDIObjectAddRemoveNotification*) message;
349 switch (notification->childType) {
350 case kMIDIObjectType_Device: {
351 MIDIDeviceRef device = (MIDIDeviceRef) notification->child;
352 for (std::map<int,MidiInputPort*>::iterator iter = pDevice->Ports.begin();
353 iter != pDevice->Ports.end(); ++iter)
354 {
355 MidiInputPortCoreMidi* pPort = (MidiInputPortCoreMidi*) iter->second;
356 pPort->onCoreMIDIDeviceDisappeared(device);
357 }
358 break;
359 }
360 case kMIDIObjectType_Entity: {
361 MIDIEntityRef entity = (MIDIEntityRef) notification->child;
362 for (std::map<int,MidiInputPort*>::iterator iter = pDevice->Ports.begin();
363 iter != pDevice->Ports.end(); ++iter)
364 {
365 MidiInputPortCoreMidi* pPort = (MidiInputPortCoreMidi*) iter->second;
366 pPort->onCoreMIDIEntityDisappeared(entity);
367 }
368 break;
369 }
370 case kMIDIObjectType_Source: {
371 MIDIEndpointRef source = (MIDIEndpointRef) notification->child;
372 for (std::map<int,MidiInputPort*>::iterator iter = pDevice->Ports.begin();
373 iter != pDevice->Ports.end(); ++iter)
374 {
375 MidiInputPortCoreMidi* pPort = (MidiInputPortCoreMidi*) iter->second;
376 pPort->onNewSourceDisappeared(source);
377 }
378 break;
379 }
380 default:
381 break;
382 }
383 break;
384 }
385
386 case kMIDIMsgPropertyChanged: {
387 MIDIObjectPropertyChangeNotification* notification = (MIDIObjectPropertyChangeNotification*) message;
388 if (notification->propertyName == kMIDIPropertyOffline) {
389 SInt32 offline = 0;
390 MIDIObjectGetIntegerProperty(notification->object, kMIDIPropertyOffline, &offline);
391 switch (notification->objectType) {
392 case kMIDIObjectType_Device: {
393 MIDIDeviceRef device = (MIDIDeviceRef) notification->object;
394 for (std::map<int,MidiInputPort*>::iterator iter = pDevice->Ports.begin();
395 iter != pDevice->Ports.end(); ++iter)
396 {
397 MidiInputPortCoreMidi* pPort = (MidiInputPortCoreMidi*) iter->second;
398 if (offline)
399 pPort->onCoreMIDIDeviceDisappeared(device);
400 else
401 pPort->onCoreMIDIDeviceAppeared(device);
402 }
403 break;
404 }
405 case kMIDIObjectType_Entity: {
406 MIDIEntityRef entity = (MIDIEntityRef) notification->object;
407 for (std::map<int,MidiInputPort*>::iterator iter = pDevice->Ports.begin();
408 iter != pDevice->Ports.end(); ++iter)
409 {
410 MidiInputPortCoreMidi* pPort = (MidiInputPortCoreMidi*) iter->second;
411 if (offline)
412 pPort->onCoreMIDIEntityDisappeared(entity);
413 else
414 pPort->onCoreMIDIEntityAppeared(entity);
415 }
416 break;
417 }
418 case kMIDIObjectType_Source: {
419 MIDIEndpointRef endpoint = (MIDIEndpointRef) notification->object;
420 for (std::map<int,MidiInputPort*>::iterator iter = pDevice->Ports.begin();
421 iter != pDevice->Ports.end(); ++iter)
422 {
423 MidiInputPortCoreMidi* pPort = (MidiInputPortCoreMidi*) iter->second;
424 if (offline)
425 pPort->onNewSourceDisappeared(endpoint);
426 else
427 pPort->onNewSourceAppeared(endpoint);
428 }
429 break;
430 }
431 default:
432 break;
433 }
434 }
435 break;
436 }
437
438 case kMIDIMsgSetupChanged:
439 case kMIDIMsgThruConnectionsChanged:
440 case kMIDIMsgSerialPortOwnerChanged:
441 case kMIDIMsgIOError:
442 break;
443 }
444 }
445
446 } // namespace LinuxSampler

Properties

Name Value
svn:keywords Revision

  ViewVC Help
Powered by ViewVC