Lines 1-521
Link Here
|
1 |
/* sndio backend for FluidSynth - A Software Synthesizer |
|
|
2 |
* |
3 |
* Copyright (c) 2008 Jacob Meuser <jakemsr@sdf.lonestar.org> |
4 |
* |
5 |
* Permission to use, copy, modify, and distribute this software for any |
6 |
* purpose with or without fee is hereby granted, provided that the above |
7 |
* copyright notice and this permission notice appear in all copies. |
8 |
* |
9 |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
10 |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
11 |
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
12 |
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
13 |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
14 |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
15 |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
16 |
*/ |
17 |
|
18 |
|
19 |
/* fluid_sndio.c |
20 |
* |
21 |
* Driver for the sndio audio access library |
22 |
*/ |
23 |
|
24 |
#include "fluid_synth.h" |
25 |
#include "fluid_adriver.h" |
26 |
#include "fluid_midi.h" |
27 |
#include "fluid_mdriver.h" |
28 |
#include "fluid_settings.h" |
29 |
|
30 |
#if SNDIO_SUPPORT |
31 |
|
32 |
#include <sndio.h> |
33 |
|
34 |
#include <sys/time.h> |
35 |
#include <sys/types.h> |
36 |
#include <pthread.h> |
37 |
#include <unistd.h> |
38 |
|
39 |
|
40 |
/** fluid_sndio_audio_driver_t |
41 |
* |
42 |
* This structure should not be accessed directly. Use audio port |
43 |
* functions instead. |
44 |
*/ |
45 |
typedef struct { |
46 |
fluid_audio_driver_t driver; |
47 |
fluid_synth_t* synth; |
48 |
fluid_audio_callback_t read; |
49 |
void* buffer; |
50 |
pthread_t thread; |
51 |
int cont; |
52 |
struct sio_hdl *hdl; |
53 |
struct sio_par par; |
54 |
int buffer_size; |
55 |
int buffer_byte_size; |
56 |
fluid_audio_func_t callback; |
57 |
void* data; |
58 |
float* buffers[2]; |
59 |
} fluid_sndio_audio_driver_t; |
60 |
|
61 |
typedef struct { |
62 |
fluid_midi_driver_t driver; |
63 |
struct mio_hdl *hdl; |
64 |
pthread_t thread; |
65 |
int status; |
66 |
fluid_midi_parser_t *parser; |
67 |
} fluid_sndio_midi_driver_t; |
68 |
|
69 |
int delete_fluid_sndio_audio_driver(fluid_audio_driver_t* p); |
70 |
|
71 |
/* local utilities */ |
72 |
static void* fluid_sndio_audio_run(void* d); |
73 |
static void* fluid_sndio_audio_run2(void* d); |
74 |
|
75 |
|
76 |
void |
77 |
fluid_sndio_audio_driver_settings(fluid_settings_t* settings) |
78 |
{ |
79 |
fluid_settings_register_str(settings, "audio.sndio.device", "default", 0, NULL, NULL); |
80 |
} |
81 |
|
82 |
/* |
83 |
* new_fluid_sndio_audio_driver |
84 |
*/ |
85 |
fluid_audio_driver_t* |
86 |
new_fluid_sndio_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth) |
87 |
{ |
88 |
fluid_sndio_audio_driver_t* dev = NULL; |
89 |
double sample_rate; |
90 |
int periods, period_size; |
91 |
char* devname; |
92 |
pthread_attr_t attr; |
93 |
int err; |
94 |
|
95 |
dev = FLUID_NEW(fluid_sndio_audio_driver_t); |
96 |
if (dev == NULL) { |
97 |
FLUID_LOG(FLUID_ERR, "Out of memory"); |
98 |
return NULL; |
99 |
} |
100 |
FLUID_MEMSET(dev, 0, sizeof(fluid_sndio_audio_driver_t)); |
101 |
|
102 |
fluid_settings_getint(settings, "audio.periods", &periods); |
103 |
fluid_settings_getint(settings, "audio.period-size", &period_size); |
104 |
fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate); |
105 |
|
106 |
dev->hdl = NULL; |
107 |
dev->synth = synth; |
108 |
dev->callback = NULL; |
109 |
dev->data = NULL; |
110 |
dev->cont = 1; |
111 |
|
112 |
if (!fluid_settings_getstr(settings, "audio.sndio.device", &devname)) { |
113 |
devname = NULL; |
114 |
} |
115 |
|
116 |
dev->hdl = sio_open(devname, SIO_PLAY, 0); |
117 |
if (dev->hdl == NULL) { |
118 |
FLUID_LOG(FLUID_ERR, "sndio could not be opened for writing"); |
119 |
goto error_recovery; |
120 |
} |
121 |
|
122 |
sio_initpar(&dev->par); |
123 |
|
124 |
if (fluid_settings_str_equal(settings, "audio.sample-format", "16bits")) { |
125 |
dev->par.bits = 16; |
126 |
dev->par.le = SIO_LE_NATIVE; |
127 |
dev->read = fluid_synth_write_s16; |
128 |
} else { |
129 |
FLUID_LOG(FLUID_ERR, "Unknown sample format"); |
130 |
goto error_recovery; |
131 |
} |
132 |
|
133 |
dev->par.appbufsz = period_size * periods; |
134 |
dev->par.round = period_size; |
135 |
|
136 |
dev->par.pchan = 2; |
137 |
dev->par.rate = sample_rate; |
138 |
|
139 |
if (!sio_setpar(dev->hdl, &dev->par)) { |
140 |
FLUID_LOG(FLUID_ERR, "Couldn't set sndio audio parameters"); |
141 |
goto error_recovery; |
142 |
} |
143 |
|
144 |
if (!sio_getpar(dev->hdl, &dev->par)) { |
145 |
FLUID_LOG(FLUID_ERR, "Couldn't get sndio audio parameters"); |
146 |
goto error_recovery; |
147 |
} else if (dev->par.pchan != 2 || dev->par.rate != sample_rate || |
148 |
dev->par.bits != 16) { |
149 |
FLUID_LOG(FLUID_ERR, "Couldn't set sndio audio parameters as desired"); |
150 |
goto error_recovery; |
151 |
} |
152 |
|
153 |
dev->buffer_size = dev->par.round; |
154 |
dev->buffer_byte_size = dev->par.round * dev->par.bps * dev->par.pchan; |
155 |
|
156 |
dev->buffer = FLUID_MALLOC(dev->buffer_byte_size); |
157 |
if (dev->buffer == NULL) { |
158 |
FLUID_LOG(FLUID_ERR, "Out of memory"); |
159 |
goto error_recovery; |
160 |
} |
161 |
|
162 |
if (!sio_start(dev->hdl)) { |
163 |
FLUID_LOG(FLUID_ERR, "Couldn't start sndio"); |
164 |
goto error_recovery; |
165 |
} |
166 |
|
167 |
if (pthread_attr_init(&attr)) { |
168 |
FLUID_LOG(FLUID_ERR, "Couldn't initialize audio thread attributes"); |
169 |
goto error_recovery; |
170 |
} |
171 |
|
172 |
err = pthread_create(&dev->thread, &attr, fluid_sndio_audio_run, (void*) dev); |
173 |
if (err) { |
174 |
FLUID_LOG(FLUID_ERR, "Couldn't create audio thread"); |
175 |
goto error_recovery; |
176 |
} |
177 |
|
178 |
return (fluid_audio_driver_t*) dev; |
179 |
|
180 |
error_recovery: |
181 |
delete_fluid_sndio_audio_driver((fluid_audio_driver_t*) dev); |
182 |
return NULL; |
183 |
} |
184 |
|
185 |
fluid_audio_driver_t* |
186 |
new_fluid_sndio_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, void* data) |
187 |
{ |
188 |
fluid_sndio_audio_driver_t* dev = NULL; |
189 |
double sample_rate; |
190 |
int periods, period_size; |
191 |
char* devname; |
192 |
pthread_attr_t attr; |
193 |
int err; |
194 |
|
195 |
dev = FLUID_NEW(fluid_sndio_audio_driver_t); |
196 |
if (dev == NULL) { |
197 |
FLUID_LOG(FLUID_ERR, "Out of memory"); |
198 |
return NULL; |
199 |
} |
200 |
FLUID_MEMSET(dev, 0, sizeof(fluid_sndio_audio_driver_t)); |
201 |
|
202 |
fluid_settings_getint(settings, "audio.periods", &periods); |
203 |
fluid_settings_getint(settings, "audio.period-size", &period_size); |
204 |
fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate); |
205 |
|
206 |
dev->hdl = NULL; |
207 |
dev->synth = NULL; |
208 |
dev->read = NULL; |
209 |
dev->callback = func; |
210 |
dev->data = data; |
211 |
dev->cont = 1; |
212 |
|
213 |
if (!fluid_settings_getstr(settings, "audio.sndio.device", &devname)) { |
214 |
devname = NULL; |
215 |
} |
216 |
|
217 |
dev->hdl = sio_open(devname, SIO_PLAY, 0); |
218 |
if (dev->hdl == NULL) { |
219 |
FLUID_LOG(FLUID_ERR, "sndio could not be opened for writing"); |
220 |
goto error_recovery; |
221 |
} |
222 |
|
223 |
sio_initpar(&dev->par); |
224 |
|
225 |
dev->par.appbufsz = period_size * periods; |
226 |
dev->par.round = period_size; |
227 |
|
228 |
dev->par.bits = 16; |
229 |
dev->par.le = SIO_LE_NATIVE; |
230 |
dev->par.pchan = 2; |
231 |
dev->par.rate = sample_rate; |
232 |
|
233 |
if (!sio_setpar(dev->hdl, &dev->par)){ |
234 |
FLUID_LOG(FLUID_ERR, "Can't configure sndio parameters"); |
235 |
goto error_recovery; |
236 |
} |
237 |
|
238 |
if (!sio_getpar(dev->hdl, &dev->par)) { |
239 |
FLUID_LOG(FLUID_ERR, "Couldn't get sndio audio parameters"); |
240 |
goto error_recovery; |
241 |
} else if (dev->par.pchan != 2 || dev->par.rate != sample_rate || |
242 |
dev->par.bits != 16) { |
243 |
FLUID_LOG(FLUID_ERR, "Couldn't set sndio audio parameters as desired"); |
244 |
goto error_recovery; |
245 |
} |
246 |
|
247 |
dev->buffer_size = dev->par.round; |
248 |
dev->buffer_byte_size = dev->par.round * dev->par.bps * dev->par.pchan; |
249 |
|
250 |
/* allocate the buffers. FIXME!!! don't use interleaved samples */ |
251 |
dev->buffer = FLUID_MALLOC(dev->buffer_byte_size); |
252 |
if (dev->buffer == NULL) { |
253 |
FLUID_LOG(FLUID_ERR, "Out of memory"); |
254 |
goto error_recovery; |
255 |
} |
256 |
dev->buffers[0] = FLUID_ARRAY(float, dev->buffer_size); |
257 |
dev->buffers[1] = FLUID_ARRAY(float, dev->buffer_size); |
258 |
if ((dev->buffer == NULL) || (dev->buffers[0] == NULL) || (dev->buffers[1] == NULL)) { |
259 |
FLUID_LOG(FLUID_ERR, "Out of memory"); |
260 |
goto error_recovery; |
261 |
} |
262 |
|
263 |
if (!sio_start(dev->hdl)) { |
264 |
FLUID_LOG(FLUID_ERR, "Couldn't start sndio"); |
265 |
goto error_recovery; |
266 |
} |
267 |
|
268 |
if (pthread_attr_init(&attr)) { |
269 |
FLUID_LOG(FLUID_ERR, "Couldn't initialize audio thread attributes"); |
270 |
goto error_recovery; |
271 |
} |
272 |
|
273 |
err = pthread_create(&dev->thread, &attr, fluid_sndio_audio_run2, (void*) dev); |
274 |
if (err) { |
275 |
FLUID_LOG(FLUID_ERR, "Couldn't create audio2 thread"); |
276 |
goto error_recovery; |
277 |
} |
278 |
|
279 |
return (fluid_audio_driver_t*) dev; |
280 |
|
281 |
error_recovery: |
282 |
delete_fluid_sndio_audio_driver((fluid_audio_driver_t*) dev); |
283 |
return NULL; |
284 |
} |
285 |
|
286 |
/* |
287 |
* delete_fluid_sndio_audio_driver |
288 |
*/ |
289 |
int |
290 |
delete_fluid_sndio_audio_driver(fluid_audio_driver_t* p) |
291 |
{ |
292 |
fluid_sndio_audio_driver_t* dev = (fluid_sndio_audio_driver_t*) p; |
293 |
|
294 |
if (dev == NULL) { |
295 |
return FLUID_OK; |
296 |
} |
297 |
dev->cont = 0; |
298 |
if (dev->thread) { |
299 |
if (pthread_join(dev->thread, NULL)) { |
300 |
FLUID_LOG(FLUID_ERR, "Failed to join the audio thread"); |
301 |
return FLUID_FAILED; |
302 |
} |
303 |
} |
304 |
if (dev->hdl) { |
305 |
sio_close(dev->hdl); |
306 |
} |
307 |
if (dev->buffer != NULL) { |
308 |
FLUID_FREE(dev->buffer); |
309 |
} |
310 |
FLUID_FREE(dev); |
311 |
return FLUID_OK; |
312 |
} |
313 |
|
314 |
/* |
315 |
* fluid_sndio_audio_run |
316 |
*/ |
317 |
void* |
318 |
fluid_sndio_audio_run(void* d) |
319 |
{ |
320 |
fluid_sndio_audio_driver_t* dev = (fluid_sndio_audio_driver_t*) d; |
321 |
fluid_synth_t* synth = dev->synth; |
322 |
void* buffer = dev->buffer; |
323 |
int len = dev->buffer_size; |
324 |
|
325 |
/* it's as simple as that: */ |
326 |
while (dev->cont) |
327 |
{ |
328 |
dev->read (synth, len, buffer, 0, 2, buffer, 1, 2); |
329 |
sio_write (dev->hdl, buffer, dev->buffer_byte_size); |
330 |
} |
331 |
|
332 |
FLUID_LOG(FLUID_DBG, "Audio thread finished"); |
333 |
|
334 |
pthread_exit(NULL); |
335 |
|
336 |
return 0; /* not reached */ |
337 |
} |
338 |
|
339 |
|
340 |
/* |
341 |
* fluid_sndio_audio_run |
342 |
*/ |
343 |
void* |
344 |
fluid_sndio_audio_run2(void* d) |
345 |
{ |
346 |
fluid_sndio_audio_driver_t* dev = (fluid_sndio_audio_driver_t*) d; |
347 |
short* buffer = (short*) dev->buffer; |
348 |
float* left = dev->buffers[0]; |
349 |
float* right = dev->buffers[1]; |
350 |
int buffer_size = dev->buffer_size; |
351 |
int dither_index = 0; |
352 |
|
353 |
FLUID_LOG(FLUID_DBG, "Audio thread running"); |
354 |
|
355 |
/* it's as simple as that: */ |
356 |
while (dev->cont) |
357 |
{ |
358 |
(*dev->callback)(dev->data, buffer_size, 0, NULL, 2, dev->buffers); |
359 |
|
360 |
fluid_synth_dither_s16 (&dither_index, buffer_size, left, right, |
361 |
buffer, 0, 2, buffer, 1, 2); |
362 |
|
363 |
sio_write (dev->hdl, buffer, dev->buffer_byte_size); |
364 |
} |
365 |
|
366 |
FLUID_LOG(FLUID_DBG, "Audio thread finished"); |
367 |
|
368 |
pthread_exit(NULL); |
369 |
|
370 |
return 0; /* not reached */ |
371 |
} |
372 |
|
373 |
void fluid_sndio_midi_driver_settings(fluid_settings_t* settings) |
374 |
{ |
375 |
fluid_settings_register_str(settings, "midi.sndio.device", "default", 0, NULL, NULL); |
376 |
} |
377 |
|
378 |
int |
379 |
delete_fluid_sndio_midi_driver(fluid_midi_driver_t *addr) |
380 |
{ |
381 |
int err; |
382 |
fluid_sndio_midi_driver_t *dev = (fluid_sndio_midi_driver_t *)addr; |
383 |
|
384 |
if (dev == NULL) { |
385 |
return FLUID_OK; |
386 |
} |
387 |
dev->status = FLUID_MIDI_DONE; |
388 |
|
389 |
/* cancel the thread and wait for it before cleaning up */ |
390 |
if (dev->thread) { |
391 |
err = pthread_cancel(dev->thread); |
392 |
if (err) { |
393 |
FLUID_LOG(FLUID_ERR, "Failed to cancel the midi thread"); |
394 |
return FLUID_FAILED; |
395 |
} |
396 |
if (pthread_join(dev->thread, NULL)) { |
397 |
FLUID_LOG(FLUID_ERR, "Failed to join the midi thread"); |
398 |
return FLUID_FAILED; |
399 |
} |
400 |
} |
401 |
if (dev->hdl != NULL) { |
402 |
mio_close(dev->hdl); |
403 |
} |
404 |
if (dev->parser != NULL) { |
405 |
delete_fluid_midi_parser(dev->parser); |
406 |
} |
407 |
FLUID_FREE(dev); |
408 |
return FLUID_OK; |
409 |
} |
410 |
|
411 |
void * |
412 |
fluid_sndio_midi_run(void *addr) |
413 |
{ |
414 |
int n, i; |
415 |
fluid_midi_event_t* evt; |
416 |
fluid_sndio_midi_driver_t *dev = (fluid_sndio_midi_driver_t *)addr; |
417 |
#define MIDI_BUFLEN (3125 / 10) |
418 |
unsigned char buffer[MIDI_BUFLEN]; |
419 |
|
420 |
/* make sure the other threads can cancel this thread any time */ |
421 |
if (pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL)) { |
422 |
FLUID_LOG(FLUID_ERR, "Failed to set the cancel state of the midi thread"); |
423 |
pthread_exit(NULL); |
424 |
} |
425 |
if (pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL)) { |
426 |
FLUID_LOG(FLUID_ERR, "Failed to set the cancel state of the midi thread"); |
427 |
pthread_exit(NULL); |
428 |
} |
429 |
|
430 |
/* go into a loop until someone tells us to stop */ |
431 |
dev->status = FLUID_MIDI_LISTENING; |
432 |
|
433 |
while (dev->status == FLUID_MIDI_LISTENING) { |
434 |
|
435 |
/* read new data */ |
436 |
n = mio_read(dev->hdl, buffer, MIDI_BUFLEN); |
437 |
if (n == 0 && mio_eof(dev->hdl)) { |
438 |
FLUID_LOG(FLUID_ERR, "Failed to read the midi input"); |
439 |
dev->status = FLUID_MIDI_DONE; |
440 |
} |
441 |
|
442 |
/* let the parser convert the data into events */ |
443 |
for (i = 0; i < n; i++) { |
444 |
evt = fluid_midi_parser_parse(dev->parser, buffer[i]); |
445 |
if (evt != NULL) { |
446 |
/* send the event to the next link in the chain */ |
447 |
(*dev->driver.handler)(dev->driver.data, evt); |
448 |
} |
449 |
} |
450 |
} |
451 |
pthread_exit(NULL); |
452 |
} |
453 |
|
454 |
int |
455 |
fluid_sndio_midi_driver_status(fluid_midi_driver_t *addr) |
456 |
{ |
457 |
fluid_sndio_midi_driver_t *dev = (fluid_sndio_midi_driver_t *)addr; |
458 |
return dev->status; |
459 |
} |
460 |
|
461 |
|
462 |
fluid_midi_driver_t * |
463 |
new_fluid_sndio_midi_driver(fluid_settings_t *settings, |
464 |
handle_midi_event_func_t handler, void *data) |
465 |
{ |
466 |
int err; |
467 |
fluid_sndio_midi_driver_t *dev; |
468 |
char *device; |
469 |
|
470 |
/* not much use doing anything */ |
471 |
if (handler == NULL) { |
472 |
FLUID_LOG(FLUID_ERR, "Invalid argument"); |
473 |
return NULL; |
474 |
} |
475 |
|
476 |
/* allocate the device */ |
477 |
dev = FLUID_NEW(fluid_sndio_midi_driver_t); |
478 |
if (dev == NULL) { |
479 |
FLUID_LOG(FLUID_ERR, "Out of memory"); |
480 |
return NULL; |
481 |
} |
482 |
FLUID_MEMSET(dev, 0, sizeof(fluid_sndio_midi_driver_t)); |
483 |
dev->hdl = NULL; |
484 |
|
485 |
dev->driver.handler = handler; |
486 |
dev->driver.data = data; |
487 |
|
488 |
/* allocate one event to store the input data */ |
489 |
dev->parser = new_fluid_midi_parser(); |
490 |
if (dev->parser == NULL) { |
491 |
FLUID_LOG(FLUID_ERR, "Out of memory"); |
492 |
goto error_recovery; |
493 |
} |
494 |
|
495 |
/* get the device name. if none is specified, use the default device. */ |
496 |
if (!fluid_settings_getstr(settings, "midi.sndio.device", &device)) { |
497 |
device = NULL; |
498 |
} |
499 |
|
500 |
/* open the default hardware device. only use midi in. */ |
501 |
dev->hdl = mio_open(device, MIO_IN, 0); |
502 |
if (dev->hdl == NULL) { |
503 |
FLUID_LOG(FLUID_ERR, "Couldn't open sndio midi device"); |
504 |
goto error_recovery; |
505 |
} |
506 |
|
507 |
dev->status = FLUID_MIDI_READY; |
508 |
|
509 |
err = pthread_create(&dev->thread, NULL, fluid_sndio_midi_run, (void *)dev); |
510 |
if (err) { |
511 |
FLUID_LOG(FLUID_PANIC, "Couldn't create the midi thread."); |
512 |
goto error_recovery; |
513 |
} |
514 |
return (fluid_midi_driver_t *) dev; |
515 |
|
516 |
error_recovery: |
517 |
delete_fluid_sndio_midi_driver((fluid_midi_driver_t *)dev); |
518 |
return NULL; |
519 |
} |
520 |
|
521 |
#endif /*#if SNDIO_SUPPORT */ |