|
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 */ |