Line 0
Link Here
|
|
|
1 |
/* |
2 |
* Copyright (c) 2010 Jacob Meuser <jakemsr@sdf.lonestar.org> |
3 |
* |
4 |
* Permission to use, copy, modify, and distribute this software for any |
5 |
* purpose with or without fee is hereby granted, provided that the above |
6 |
* copyright notice and this permission notice appear in all copies. |
7 |
* |
8 |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
9 |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
10 |
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
11 |
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
12 |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
13 |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
14 |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
15 |
*/ |
16 |
|
17 |
|
18 |
#include "allegro.h" |
19 |
|
20 |
#if (defined ALLEGRO_WITH_SNDIODIGI) && ((!defined ALLEGRO_WITH_MODULES) || (defined ALLEGRO_MODULE)) |
21 |
|
22 |
#include "allegro/internal/aintern.h" |
23 |
#include "allegro/platform/aintunix.h" |
24 |
|
25 |
#include <stdlib.h> |
26 |
#include <stdio.h> |
27 |
#include <limits.h> |
28 |
#include <unistd.h> |
29 |
#include <poll.h> |
30 |
#include <sndio.h> |
31 |
|
32 |
static int sndio_detect(int input); |
33 |
static int sndio_init(int input, int voices); |
34 |
static void sndio_exit(int input); |
35 |
static int sndio_set_mixer_volume(int volume); |
36 |
static int sndio_get_mixer_volume(void); |
37 |
|
38 |
static int sndio_buffer_size(void); |
39 |
|
40 |
static int sndio_rec_cap_rate(int bits, int stereo); |
41 |
static int sndio_rec_cap_parm(int rate, int bits, int stereo); |
42 |
static int sndio_rec_source(int source); |
43 |
static int sndio_rec_start(int rate, int bits, int stereo); |
44 |
static void sndio_rec_stop(void); |
45 |
static int sndio_rec_read(void *buf); |
46 |
|
47 |
static int open_sndio_device(int input); |
48 |
static void sndio_update(int threaded); |
49 |
static void movecb(void *addr, int delta); |
50 |
static void volcb(void *addr, unsigned vol); |
51 |
|
52 |
static struct sio_hdl *hdl; |
53 |
static struct sio_par par; |
54 |
static int sndio_signed; |
55 |
static int sndio_play_bufsize, sndio_play_round, sndio_play_appbufsz; |
56 |
static unsigned char *sndio_play_bufdata; |
57 |
static char sndio_desc[256] = EMPTY_STRING; |
58 |
long long sndio_realpos, sndio_playpos; |
59 |
int sndio_volume; |
60 |
|
61 |
static int sndio_save_bits, sndio_save_stereo, sndio_save_freq; |
62 |
static int sndio_rec_bufsize, sndio_rec_round, sndio_rec_appbufsz; |
63 |
static unsigned char *sndio_rec_bufdata; |
64 |
|
65 |
DIGI_DRIVER digi_sndio = |
66 |
{ |
67 |
DIGI_SNDIO, |
68 |
empty_string, |
69 |
empty_string, |
70 |
"sndio", |
71 |
0, |
72 |
0, |
73 |
MIXER_MAX_SFX, |
74 |
MIXER_DEF_SFX, |
75 |
|
76 |
/* setup routines */ |
77 |
sndio_detect, |
78 |
sndio_init, |
79 |
sndio_exit, |
80 |
sndio_set_mixer_volume, |
81 |
sndio_get_mixer_volume, |
82 |
|
83 |
/* for use by the audiostream functions */ |
84 |
NULL, |
85 |
NULL, |
86 |
sndio_buffer_size, |
87 |
|
88 |
/* voice control functions */ |
89 |
_mixer_init_voice, |
90 |
_mixer_release_voice, |
91 |
_mixer_start_voice, |
92 |
_mixer_stop_voice, |
93 |
_mixer_loop_voice, |
94 |
|
95 |
/* position control functions */ |
96 |
_mixer_get_position, |
97 |
_mixer_set_position, |
98 |
|
99 |
/* volume control functions */ |
100 |
_mixer_get_volume, |
101 |
_mixer_set_volume, |
102 |
_mixer_ramp_volume, |
103 |
_mixer_stop_volume_ramp, |
104 |
|
105 |
/* pitch control functions */ |
106 |
_mixer_get_frequency, |
107 |
_mixer_set_frequency, |
108 |
_mixer_sweep_frequency, |
109 |
_mixer_stop_frequency_sweep, |
110 |
|
111 |
/* pan control functions */ |
112 |
_mixer_get_pan, |
113 |
_mixer_set_pan, |
114 |
_mixer_sweep_pan, |
115 |
_mixer_stop_pan_sweep, |
116 |
|
117 |
/* effect control functions */ |
118 |
_mixer_set_echo, |
119 |
_mixer_set_tremolo, |
120 |
_mixer_set_vibrato, |
121 |
|
122 |
/* input functions */ |
123 |
0, |
124 |
0, |
125 |
sndio_rec_cap_rate, |
126 |
sndio_rec_cap_parm, |
127 |
sndio_rec_source, |
128 |
sndio_rec_start, |
129 |
sndio_rec_stop, |
130 |
sndio_rec_read |
131 |
}; |
132 |
|
133 |
|
134 |
/* used to probe and to configure the device. don't use sio_start() here. */ |
135 |
static int |
136 |
open_sndio_device(int input) |
137 |
{ |
138 |
hdl = sio_open(NULL, (input ? SIO_REC : SIO_PLAY), 0); |
139 |
if (hdl == NULL) { |
140 |
uszprintf(allegro_error, ALLEGRO_ERROR_SIZE, |
141 |
get_config_text("sio_opn failed")); |
142 |
return -1; |
143 |
} |
144 |
|
145 |
sio_initpar(&par); |
146 |
par.bits = (_sound_bits == 8) ? 8 : 16; |
147 |
par.sig = (_sound_bits == 8) ? 0 : 1; |
148 |
if (input) |
149 |
par.rchan = (_sound_stereo) ? 2 : 1; |
150 |
else |
151 |
par.pchan = (_sound_stereo) ? 2 : 1; |
152 |
par.rate = (_sound_freq > 0) ? _sound_freq : 48000; |
153 |
par.le = SIO_LE_NATIVE; |
154 |
/* allegro wants small blocks */ |
155 |
par.round = 512; |
156 |
par.appbufsz = par.rate / 10; |
157 |
|
158 |
if (!sio_setpar(hdl, &par) || !sio_getpar(hdl, &par) || |
159 |
(par.bits != 8 && par.bits != 16) || |
160 |
(par.bits == 8 && par.sig) || |
161 |
(par.bits == 16 && !par.sig) || |
162 |
(par.bits == 16 && par.le != SIO_LE_NATIVE) || |
163 |
(input && (par.rchan != 1 && par.rchan != 2)) || |
164 |
(!input && (par.pchan != 1 && par.pchan != 2))) { |
165 |
ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, |
166 |
get_config_text("could not set sndio parameters")); |
167 |
sio_close(hdl); |
168 |
return -1; |
169 |
} |
170 |
|
171 |
_sound_bits = par.bits; |
172 |
_sound_stereo = input ? par.rchan == 2 : par.pchan == 2; |
173 |
_sound_freq = par.rate; |
174 |
|
175 |
if (input) { |
176 |
sndio_rec_round = par.round; |
177 |
sndio_rec_appbufsz = par.appbufsz; |
178 |
sndio_rec_bufsize = par.round * par.bps * par.rchan; |
179 |
} else { |
180 |
sndio_play_round = par.round; |
181 |
sndio_play_appbufsz = par.appbufsz; |
182 |
sndio_play_bufsize = sndio_play_round * par.bps * par.pchan; |
183 |
} |
184 |
sndio_signed = par.sig ? 1 : 0; |
185 |
|
186 |
return 0; |
187 |
} |
188 |
|
189 |
|
190 |
static int |
191 |
sndio_detect(int input) |
192 |
{ |
193 |
if (input) { |
194 |
if (digi_driver != digi_input_driver) { |
195 |
ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, |
196 |
get_config_text("sndio output driver must be installed before input can be read")); |
197 |
return FALSE; |
198 |
} |
199 |
return TRUE; |
200 |
} |
201 |
|
202 |
if (open_sndio_device(0) != 0) |
203 |
return FALSE; |
204 |
|
205 |
sio_close(hdl); |
206 |
return TRUE; |
207 |
} |
208 |
|
209 |
|
210 |
/* number of samples per channel in a block */ |
211 |
static int |
212 |
sndio_buffer_size(void) |
213 |
{ |
214 |
return sndio_play_round; |
215 |
} |
216 |
|
217 |
|
218 |
/* callback for data movement notification */ |
219 |
static void |
220 |
movecb(void *addr, int delta) |
221 |
{ |
222 |
sndio_realpos += delta; |
223 |
} |
224 |
|
225 |
|
226 |
/* callback for volume change notification */ |
227 |
static void |
228 |
volcb(void *addr, unsigned vol) |
229 |
{ |
230 |
sndio_volume = vol; |
231 |
} |
232 |
|
233 |
|
234 |
/* write as many blocks as is currently possible */ |
235 |
static void |
236 |
sndio_update(int threaded) |
237 |
{ |
238 |
struct pollfd pfd; |
239 |
nfds_t nfds; |
240 |
int i, nblocks, nbytes; |
241 |
|
242 |
/* make sure counters have been updated */ |
243 |
nfds = sio_pollfd(hdl, &pfd, POLLOUT); |
244 |
poll(&pfd, nfds, 0); |
245 |
if (!(sio_revents(hdl, &pfd) & POLLOUT)) |
246 |
return; |
247 |
|
248 |
nblocks = (sndio_play_appbufsz - (sndio_playpos - sndio_realpos)) / |
249 |
sndio_play_round; |
250 |
|
251 |
/* we got POLLOUT, so we can write something. if we don't |
252 |
* write anything, we could underrun. |
253 |
*/ |
254 |
if (nblocks < 1) |
255 |
nblocks = 1; |
256 |
|
257 |
for (i = 0; i < nblocks; i++) { |
258 |
sio_write(hdl, sndio_play_bufdata, sndio_play_bufsize); |
259 |
sndio_playpos += sndio_play_round; |
260 |
if (sio_eof(hdl)) { |
261 |
/* print error message? */ |
262 |
return; |
263 |
} |
264 |
_mix_some_samples((uintptr_t) sndio_play_bufdata, 0, sndio_signed); |
265 |
} |
266 |
} |
267 |
|
268 |
|
269 |
static int |
270 |
sndio_init(int input, int voices) |
271 |
{ |
272 |
char tmp1[128], tmp2[128]; |
273 |
|
274 |
if (input) { |
275 |
digi_driver->rec_cap_bits = 16; |
276 |
digi_driver->rec_cap_stereo = TRUE; |
277 |
return 0; |
278 |
} |
279 |
|
280 |
if (open_sndio_device(0) != 0) |
281 |
return -1; |
282 |
|
283 |
sndio_play_bufdata = _AL_MALLOC_ATOMIC(sndio_play_bufsize); |
284 |
if (sndio_play_bufdata == 0) { |
285 |
ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, |
286 |
get_config_text("Can not allocate audio buffer")); |
287 |
sio_close(hdl); |
288 |
return -1; |
289 |
} |
290 |
|
291 |
sndio_realpos = sndio_playpos = 0; |
292 |
sio_onmove(hdl, movecb, NULL); |
293 |
|
294 |
sndio_volume = 127; |
295 |
sio_onvol(hdl, volcb, NULL); |
296 |
|
297 |
if (!sio_start(hdl)) { |
298 |
ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, |
299 |
get_config_text("Can not start sndio")); |
300 |
sio_close(hdl); |
301 |
return -1; |
302 |
} |
303 |
|
304 |
digi_sndio.voices = voices; |
305 |
|
306 |
/* first arg is total number of samples */ |
307 |
if (_mixer_init(sndio_play_round * (_sound_stereo ? 2 : 1), |
308 |
_sound_freq, _sound_stereo, ((_sound_bits == 16) ? 1 : 0), |
309 |
&digi_sndio.voices) != 0) { |
310 |
ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, |
311 |
get_config_text("Can not init software mixer")); |
312 |
sio_close(hdl); |
313 |
return -1; |
314 |
} |
315 |
|
316 |
_mix_some_samples((uintptr_t) sndio_play_bufdata, 0, sndio_signed); |
317 |
|
318 |
/* Add audio interrupt. */ |
319 |
_unix_bg_man->register_func(sndio_update); |
320 |
|
321 |
uszprintf(sndio_desc, sizeof(sndio_desc), |
322 |
get_config_text("%s: %d bits, %s, %d Hz, %s"), |
323 |
"sndio device", |
324 |
_sound_bits, |
325 |
uconvert_ascii((sndio_signed ? "signed" : "unsigned"), tmp1), |
326 |
_sound_freq, |
327 |
uconvert_ascii((par.pchan == 2 ? "stereo" : "mono"), tmp2)); |
328 |
|
329 |
digi_driver->desc = sndio_desc; |
330 |
|
331 |
return 0; |
332 |
} |
333 |
|
334 |
|
335 |
static void |
336 |
sndio_exit(int input) |
337 |
{ |
338 |
if (input) |
339 |
return; |
340 |
|
341 |
_unix_bg_man->unregister_func(sndio_update); |
342 |
|
343 |
_AL_FREE(sndio_play_bufdata); |
344 |
sndio_play_bufdata = 0; |
345 |
|
346 |
_mixer_exit(); |
347 |
|
348 |
if (hdl != NULL) |
349 |
sio_close(hdl); |
350 |
hdl = NULL; |
351 |
} |
352 |
|
353 |
|
354 |
/* 'volume' is 0-255 */ |
355 |
static int |
356 |
sndio_set_mixer_volume(int volume) |
357 |
{ |
358 |
if (!sio_setvol(hdl, volume / 2)) |
359 |
return -1; |
360 |
|
361 |
return 0; |
362 |
} |
363 |
|
364 |
|
365 |
/* should return 0-255 */ |
366 |
static int |
367 |
sndio_get_mixer_volume(void) |
368 |
{ |
369 |
return sndio_volume * 2; |
370 |
} |
371 |
|
372 |
|
373 |
/* Returns maximum recording sampling rate. */ |
374 |
static int |
375 |
sndio_rec_cap_rate(int bits, int stereo) |
376 |
{ |
377 |
/* should use sio_getcap() */ |
378 |
return 48000; |
379 |
} |
380 |
|
381 |
|
382 |
/* Returns whether the specified parameters can be set. */ |
383 |
static int |
384 |
sndio_rec_cap_parm(int rate, int bits, int stereo) |
385 |
{ |
386 |
/* should use sio_getcap() */ |
387 |
return 1; |
388 |
} |
389 |
|
390 |
|
391 |
/* Sets the sampling source for audio recording. */ |
392 |
static int |
393 |
sndio_rec_source(int source) |
394 |
{ |
395 |
/* not implemented in sndio */ |
396 |
|
397 |
switch (source) { |
398 |
case SOUND_INPUT_MIC: |
399 |
break; |
400 |
case SOUND_INPUT_LINE: |
401 |
break; |
402 |
case SOUND_INPUT_CD: |
403 |
break; |
404 |
default: |
405 |
return -1; |
406 |
} |
407 |
|
408 |
return 0; |
409 |
} |
410 |
|
411 |
|
412 |
/* |
413 |
* Re-opens device with read-mode and starts recording (half-duplex). |
414 |
* Returns the DMA buffer size if successful. |
415 |
*/ |
416 |
static int |
417 |
sndio_rec_start(int rate, int bits, int stereo) |
418 |
{ |
419 |
sndio_save_bits = _sound_bits; |
420 |
sndio_save_stereo = _sound_stereo; |
421 |
sndio_save_freq = _sound_freq; |
422 |
|
423 |
_unix_bg_man->unregister_func(sndio_update); |
424 |
|
425 |
if (hdl != NULL) |
426 |
sio_close(hdl); |
427 |
hdl = NULL; |
428 |
|
429 |
_sound_bits = bits; |
430 |
_sound_stereo = stereo; |
431 |
_sound_freq = rate; |
432 |
|
433 |
if (open_sndio_device(1) != 0) |
434 |
return 0; |
435 |
|
436 |
sndio_volume = 127; |
437 |
sio_onvol(hdl, volcb, NULL); |
438 |
|
439 |
if (!sio_start(hdl)) { |
440 |
ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, |
441 |
get_config_text("Can not start sndio for recording")); |
442 |
sio_close(hdl); |
443 |
return 0; |
444 |
} |
445 |
|
446 |
return sndio_rec_bufsize; |
447 |
} |
448 |
|
449 |
|
450 |
/* Stops recording and switches the device back to the original mode. */ |
451 |
static void |
452 |
sndio_rec_stop(void) |
453 |
{ |
454 |
if (hdl != NULL) |
455 |
sio_close(hdl); |
456 |
hdl = NULL; |
457 |
|
458 |
_sound_bits = sndio_save_bits; |
459 |
_sound_stereo = sndio_save_stereo; |
460 |
_sound_freq = sndio_save_freq; |
461 |
|
462 |
if (open_sndio_device(0) != 0) |
463 |
return; |
464 |
|
465 |
sndio_realpos = sndio_playpos = 0; |
466 |
sio_onmove(hdl, movecb, NULL); |
467 |
|
468 |
sndio_volume = 127; |
469 |
sio_onvol(hdl, volcb, NULL); |
470 |
|
471 |
if (!sio_start(hdl)) { |
472 |
ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, |
473 |
get_config_text("Can not start sndio")); |
474 |
sio_close(hdl); |
475 |
return; |
476 |
} |
477 |
|
478 |
_unix_bg_man->register_func(sndio_update); |
479 |
} |
480 |
|
481 |
|
482 |
/* Retrieves the just recorded buffer, if there is one. */ |
483 |
static int |
484 |
sndio_rec_read(void *buf) |
485 |
{ |
486 |
struct pollfd pfd; |
487 |
nfds_t nfds; |
488 |
int ret, nbytes, offset = 0; |
489 |
|
490 |
/* make sure counters have been updated */ |
491 |
nfds = sio_pollfd(hdl, &pfd, POLLIN); |
492 |
poll(&pfd, nfds, 0); |
493 |
sio_revents(hdl, &pfd); |
494 |
if (!(sio_revents(hdl, &pfd) & POLLIN)) |
495 |
return 0; |
496 |
|
497 |
nbytes = sndio_rec_bufsize; |
498 |
while (nbytes) { |
499 |
ret = sio_read(hdl, buf + offset, nbytes); |
500 |
if (sio_eof(hdl)) |
501 |
return 0; |
502 |
offset += ret; |
503 |
nbytes -= ret; |
504 |
} |
505 |
|
506 |
return 1; |
507 |
} |
508 |
|
509 |
#endif /* ALLEGRO_WITH_SNDIODIGI */ |