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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2719 - (hide 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 schoenebeck 201 /***************************************************************************
2     * *
3 schoenebeck 551 * Copyright (C) 2004, 2005 Grame *
4 schoenebeck 2719 * Copyright (C) 2005 - 2015 Christian Schoenebeck *
5 schoenebeck 201 * *
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 letz 362 #include "MidiInputDeviceFactory.h"
24 schoenebeck 201
25     namespace LinuxSampler {
26 schoenebeck 2370
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 persson 2382 delete[] buf;
47 schoenebeck 2370 return "";
48     }
49     String result = buf;
50 persson 2382 delete[] buf;
51 schoenebeck 2370 return result;
52     }
53    
54    
55     // *************** ParameterName ***************
56     // *
57 schoenebeck 201
58 schoenebeck 1149 MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterName::ParameterName(MidiInputPort* pPort) throw (Exception) : MidiInputPort::ParameterName(pPort, "Port " + ToString(pPort->GetPortNumber())) {
59 letz 362 OnSetValue(ValueAsString()); // initialize port name
60     }
61    
62 schoenebeck 1149 void MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterName::OnSetValue(String s) throw (Exception) {
63 schoenebeck 2370 //TODO: renaming of port to be implemented
64 schoenebeck 551 }
65    
66 schoenebeck 2370 // *************** ParameterCoreMidiBindings ***************
67     // *
68 letz 362
69     MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterCoreMidiBindings::ParameterCoreMidiBindings(MidiInputPortCoreMidi* pPort) : DeviceRuntimeParameterStrings( std::vector<String>() ) {
70     this->pPort = pPort;
71 schoenebeck 201 }
72    
73 letz 362 String MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterCoreMidiBindings::Description() {
74     return "Bindings to other CoreMidi clients";
75 schoenebeck 201 }
76 letz 362 bool MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterCoreMidiBindings::Fix() {
77     return false;
78     }
79 schoenebeck 201
80 letz 362 std::vector<String> MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterCoreMidiBindings::PossibilitiesAsString() {
81     std::vector<String> res;
82 schoenebeck 2370
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 letz 362 return res;
92     }
93    
94 schoenebeck 1149 void MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterCoreMidiBindings::OnSetValue(std::vector<String> vS) throw (Exception) {
95 schoenebeck 2370 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 letz 362 }
110 schoenebeck 2370
111     // *************** ParameterAutoBind ***************
112     // *
113    
114     MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ParameterAutoBind::ParameterAutoBind(MidiInputPortCoreMidi* pPort)
115 schoenebeck 2568 : DeviceRuntimeParameterBool(true)
116 schoenebeck 2370 {
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 letz 362
132    
133     // *************** MidiInputPortCoreMidi ***************
134     // *
135 schoenebeck 2370
136     int MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::pPortID = 0;
137 letz 362
138     MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::MidiInputPortCoreMidi(MidiInputDeviceCoreMidi* pDevice) throw (MidiInputException) : MidiInputPort(pDevice, -1) {
139 schoenebeck 2370 this->pDevice = pDevice;
140    
141 letz 362 // create CoreMidi virtual destination
142 schoenebeck 551
143 nagata 1642 /* 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 schoenebeck 2719 CFRelease(str);
151 nagata 1642
152 letz 362 if (!pDestination) throw MidiInputException("Error creating CoreMidi virtual destination");
153     this->portNumber = pPortID++;
154 schoenebeck 551
155 letz 362 Parameters["NAME"] = new ParameterName(this);
156     Parameters["CORE_MIDI_BINDINGS"] = new ParameterCoreMidiBindings(this);
157 schoenebeck 2370 Parameters["AUTO_BIND"] = new ParameterAutoBind(this);
158 schoenebeck 201 }
159    
160 letz 362 MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::~MidiInputPortCoreMidi() {
161     MIDIEndpointDispose(pDestination);
162     }
163 schoenebeck 551
164 letz 362 void MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ReadProc(const MIDIPacketList* pktlist, void* refCon, void* connRefCon)
165 schoenebeck 201 {
166 letz 362 MidiInputPortCoreMidi* port = (MidiInputPortCoreMidi*)refCon;
167 schoenebeck 2370 port->ProcessMidiEvents(pktlist);
168     }
169    
170     void MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::ProcessMidiEvents(const MIDIPacketList *pktlist) {
171 schoenebeck 551 MIDIPacket *packet = (MIDIPacket *)pktlist->packet;
172 letz 362 for (unsigned int i = 0; i < pktlist->numPackets; ++i) {
173 schoenebeck 2440 uint8_t* pData = (uint8_t*) packet->data;
174 schoenebeck 2431 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 schoenebeck 2440 DispatchRaw(&pData[k]);
185 schoenebeck 2431 k += eventSize;
186     } while (k < packet->length);
187    
188     next_packet:
189 schoenebeck 201 packet = MIDIPacketNext(packet);
190 schoenebeck 551 }
191 letz 362 }
192 schoenebeck 2370
193     void MidiInputDeviceCoreMidi::MidiInputPortCoreMidi::connectToSource(MIDIEndpointRef source) {
194 schoenebeck 2440 // check if we already have such a connection, if yes, ignore it
195 schoenebeck 2370 for (uint i = 0; i < bindings.size(); ++i) {
196 schoenebeck 2440 if (bindings[i] == source) return;
197 schoenebeck 2370 }
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 schoenebeck 2375 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 schoenebeck 2370 }
227 letz 362
228 schoenebeck 2719 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 schoenebeck 551
236 schoenebeck 2719 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 schoenebeck 551 // *************** MidiInputDeviceCoreMidi ***************
262     // *
263    
264     MidiInputDeviceCoreMidi::MidiInputDeviceCoreMidi(std::map<String,DeviceCreationParameter*> Parameters, void* pSampler) : MidiInputDevice(Parameters, pSampler)
265 letz 362 {
266 schoenebeck 2370 MIDIClientCreate(CFSTR("LinuxSampler"), NotifyProc, this, &hCoreMidiClient);
267 letz 362 if (!hCoreMidiClient) throw MidiInputException("Error opening CoreMidi client");
268 schoenebeck 2370
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 letz 362 AcquirePorts(((DeviceCreationParameterInt*)Parameters["PORTS"])->ValueAsInt());
273     }
274    
275 schoenebeck 551 MidiInputDeviceCoreMidi::~MidiInputDeviceCoreMidi()
276 letz 362 {
277     if (hCoreMidiClient) {
278     MIDIClientDispose(hCoreMidiClient);
279 schoenebeck 201 }
280 letz 362 }
281 schoenebeck 551
282 letz 362 MidiInputDeviceCoreMidi::MidiInputPortCoreMidi* MidiInputDeviceCoreMidi::CreateMidiPort() {
283     return new MidiInputPortCoreMidi(this);
284     }
285 schoenebeck 551
286 letz 362 String MidiInputDeviceCoreMidi::Name() {
287 letz 509 return "COREMIDI";
288 letz 362 }
289    
290     String MidiInputDeviceCoreMidi::Driver() {
291     return Name();
292     }
293 schoenebeck 551
294 letz 362 String MidiInputDeviceCoreMidi::Description() {
295     return "Apple CoreMidi";
296     }
297    
298     String MidiInputDeviceCoreMidi::Version() {
299 schoenebeck 2494 String s = "$Revision$";
300 letz 362 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 schoenebeck 2370 MidiInputDeviceCoreMidi* pDevice = (MidiInputDeviceCoreMidi*) refCon;
306    
307     switch (message->messageID) {
308     case kMIDIMsgObjectAdded: {
309     MIDIObjectAddRemoveNotification* notification = (MIDIObjectAddRemoveNotification*) message;
310 schoenebeck 2719 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 schoenebeck 2370 MIDIEndpointRef source = (MIDIEndpointRef) notification->child;
333 schoenebeck 2719 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 schoenebeck 2370 }
341 schoenebeck 2719 default:
342     break;
343 schoenebeck 2370 }
344     break;
345     }
346 schoenebeck 2719
347 schoenebeck 2370 case kMIDIMsgObjectRemoved: {
348     MIDIObjectAddRemoveNotification* notification = (MIDIObjectAddRemoveNotification*) message;
349 schoenebeck 2719 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 schoenebeck 2370 MIDIEndpointRef source = (MIDIEndpointRef) notification->child;
372 schoenebeck 2719 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 schoenebeck 2370 }
380 schoenebeck 2719 default:
381     break;
382 schoenebeck 2370 }
383     break;
384     }
385 schoenebeck 2719
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 schoenebeck 2370 case kMIDIMsgSetupChanged:
439     case kMIDIMsgThruConnectionsChanged:
440     case kMIDIMsgSerialPortOwnerChanged:
441     case kMIDIMsgIOError:
442     break;
443 letz 362 }
444 schoenebeck 201 }
445    
446     } // namespace LinuxSampler

Properties

Name Value
svn:keywords Revision

  ViewVC Help
Powered by ViewVC