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