1 /* FluidSynth - A Software Synthesizer
2  *
3  * Copyright (C) 2003  Peter Hanappe and others.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public License
7  * as published by the Free Software Foundation; either version 2.1 of
8  * the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free
17  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18  * 02110-1301, USA
19  */
20 
21 /* fluid_coremidi.c
22  *
23  * Driver for Mac OSX native MIDI
24  * Pedro Lopez-Cabanillas, Jan 2009
25  */
26 
27 #include "fluidsynth_priv.h"
28 
29 #if COREMIDI_SUPPORT
30 
31 #include "fluid_midi.h"
32 #include "fluid_mdriver.h"
33 #include "fluid_settings.h"
34 
35 /* Work around for OSX 10.4 */
36 
37 /* enum definition in OpenTransportProviders.h defines these tokens
38    which are #defined from <netinet/tcp.h> */
39 #ifdef TCP_NODELAY
40 #undef TCP_NODELAY
41 #endif
42 #ifdef TCP_MAXSEG
43 #undef TCP_MAXSEG
44 #endif
45 #ifdef TCP_KEEPALIVE
46 #undef TCP_KEEPALIVE
47 #endif
48 
49 /* End work around */
50 
51 #include <unistd.h>
52 #include <CoreServices/CoreServices.h>
53 #include <CoreMIDI/MIDIServices.h>
54 
55 typedef struct
56 {
57     fluid_midi_driver_t driver;
58     MIDIClientRef client;
59     MIDIEndpointRef endpoint;
60     MIDIPortRef input_port;
61     fluid_midi_parser_t *parser;
62     int autoconn_inputs;
63 } fluid_coremidi_driver_t;
64 
65 void fluid_coremidi_callback(const MIDIPacketList *list, void *p, void *src);
66 
fluid_coremidi_driver_settings(fluid_settings_t * settings)67 void fluid_coremidi_driver_settings(fluid_settings_t *settings)
68 {
69     fluid_settings_register_str(settings, "midi.coremidi.id", "pid", 0);
70 }
71 
fluid_coremidi_autoconnect(fluid_coremidi_driver_t * dev,MIDIPortRef input_port)72 static void fluid_coremidi_autoconnect(fluid_coremidi_driver_t *dev, MIDIPortRef input_port)
73 {
74     int i;
75     int source_count = MIDIGetNumberOfSources();
76     for(i = 0; i < source_count; ++i)
77     {
78         MIDIEndpointRef source = MIDIGetSource(i);
79 
80         CFStringRef externalName;
81         OSStatus result = MIDIObjectGetStringProperty(source, kMIDIPropertyName, &externalName);
82         const char *source_name = CFStringGetCStringPtr(externalName, kCFStringEncodingASCII);
83         CFRelease(externalName);
84 
85         result = MIDIPortConnectSource(input_port, source, NULL);
86         if(result != noErr)
87         {
88             FLUID_LOG(FLUID_ERR, "Failed to connect \"%s\" device to input port.", source_name);
89         }
90         else
91         {
92             FLUID_LOG(FLUID_DBG, "Connected input port to \"%s\".", source_name);
93         }
94     }
95 }
96 
97 /*
98  * new_fluid_coremidi_driver
99  */
100 fluid_midi_driver_t *
new_fluid_coremidi_driver(fluid_settings_t * settings,handle_midi_event_func_t handler,void * data)101 new_fluid_coremidi_driver(fluid_settings_t *settings, handle_midi_event_func_t handler, void *data)
102 {
103     fluid_coremidi_driver_t *dev;
104     MIDIClientRef client;
105     MIDIEndpointRef endpoint;
106     char clientid[32];
107     char *portname;
108     char *id;
109     CFStringRef str_portname;
110     CFStringRef str_clientname;
111 
112     /* not much use doing anything */
113     if(handler == NULL)
114     {
115         FLUID_LOG(FLUID_ERR, "Invalid argument");
116         return NULL;
117     }
118 
119     dev = FLUID_MALLOC(sizeof(fluid_coremidi_driver_t));
120 
121     if(dev == NULL)
122     {
123         FLUID_LOG(FLUID_ERR, "Out of memory");
124         return NULL;
125     }
126 
127     dev->client = 0;
128     dev->endpoint = 0;
129     dev->parser = 0;
130     dev->driver.handler = handler;
131     dev->driver.data = data;
132 
133     dev->parser = new_fluid_midi_parser();
134 
135     if(dev->parser == NULL)
136     {
137         FLUID_LOG(FLUID_ERR, "Out of memory");
138         goto error_recovery;
139     }
140 
141     fluid_settings_dupstr(settings, "midi.coremidi.id", &id);     /* ++ alloc id string */
142     memset(clientid, 0, sizeof(clientid));
143 
144     if(id != NULL)
145     {
146         if(FLUID_STRCMP(id, "pid") == 0)
147         {
148             FLUID_SNPRINTF(clientid, sizeof(clientid), " (%d)", getpid());
149         }
150         else
151         {
152             FLUID_SNPRINTF(clientid, sizeof(clientid), " (%s)", id);
153         }
154 
155         FLUID_FREE(id);   /* -- free id string */
156     }
157 
158     str_clientname = CFStringCreateWithFormat(NULL, NULL,
159                      CFSTR("FluidSynth%s"), clientid);
160 
161     fluid_settings_dupstr(settings, "midi.portname", &portname);  /* ++ alloc port name */
162 
163     if(!portname || strlen(portname) == 0)
164         str_portname = CFStringCreateWithFormat(NULL, NULL,
165                                                 CFSTR("FluidSynth virtual port%s"),
166                                                 clientid);
167     else
168         str_portname = CFStringCreateWithCString(NULL, portname,
169                        kCFStringEncodingASCII);
170 
171     if(portname)
172     {
173         FLUID_FREE(portname);    /* -- free port name */
174     }
175 
176     OSStatus result = MIDIClientCreate(str_clientname, NULL, NULL, &client);
177     CFRelease(str_clientname);
178 
179     if(result != noErr)
180     {
181         FLUID_LOG(FLUID_ERR, "Failed to create the MIDI input client");
182         goto error_recovery;
183     }
184 
185     dev->client = client;
186 
187     result = MIDIDestinationCreate(client, str_portname,
188                                    fluid_coremidi_callback,
189                                    (void *)dev, &endpoint);
190 
191     if(result != noErr)
192     {
193         FLUID_LOG(FLUID_ERR, "Failed to create the MIDI input port. MIDI input not available.");
194         goto error_recovery;
195     }
196 
197     CFStringRef str_input_portname = CFSTR("input");
198     result = MIDIInputPortCreate(client, str_input_portname,
199                                  fluid_coremidi_callback,
200                                  (void *)dev, &dev->input_port);
201     CFRelease(str_input_portname);
202 
203     if(result != noErr)
204     {
205         FLUID_LOG(FLUID_ERR, "Failed to create input port.");
206         goto error_recovery;
207     }
208 
209     fluid_settings_getint(settings, "midi.autoconnect", &dev->autoconn_inputs);
210 
211     if(dev->autoconn_inputs)
212     {
213         fluid_coremidi_autoconnect(dev, dev->input_port);
214     }
215 
216     dev->endpoint = endpoint;
217 
218     return (fluid_midi_driver_t *) dev;
219 
220 error_recovery:
221     delete_fluid_coremidi_driver((fluid_midi_driver_t *) dev);
222     return NULL;
223 }
224 
225 /*
226  * delete_fluid_coremidi_driver
227  */
228 void
delete_fluid_coremidi_driver(fluid_midi_driver_t * p)229 delete_fluid_coremidi_driver(fluid_midi_driver_t *p)
230 {
231     fluid_coremidi_driver_t *dev = (fluid_coremidi_driver_t *) p;
232     fluid_return_if_fail(dev != NULL);
233 
234     if(dev->input_port != NULL)
235     {
236         MIDIPortDispose(dev->input_port);
237     }
238 
239     if(dev->client != NULL)
240     {
241         MIDIClientDispose(dev->client);
242     }
243 
244     if(dev->endpoint != NULL)
245     {
246         MIDIEndpointDispose(dev->endpoint);
247     }
248 
249     if(dev->parser != NULL)
250     {
251         delete_fluid_midi_parser(dev->parser);
252     }
253 
254     FLUID_FREE(dev);
255 }
256 
257 void
fluid_coremidi_callback(const MIDIPacketList * list,void * p,void * src)258 fluid_coremidi_callback(const MIDIPacketList *list, void *p, void *src)
259 {
260     unsigned int i, j;
261     fluid_midi_event_t *event;
262     fluid_coremidi_driver_t *dev = (fluid_coremidi_driver_t *)p;
263     const MIDIPacket *packet = &list->packet[0];
264 
265     for(i = 0; i < list->numPackets; ++i)
266     {
267         for(j = 0; j < packet->length; ++j)
268         {
269             event = fluid_midi_parser_parse(dev->parser, packet->data[j]);
270 
271             if(event != NULL)
272             {
273                 (*dev->driver.handler)(dev->driver.data, event);
274             }
275         }
276 
277         packet = MIDIPacketNext(packet);
278     }
279 }
280 
281 #endif /* COREMIDI_SUPPORT */
282