fmodaudio.c revision 413f05aaf54fa08c0ae7e997327a4f4a473c0a8d
1/* 2 * QEMU FMOD audio driver 3 * 4 * Copyright (c) 2004-2005 Vassili Karpov (malc) 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 * THE SOFTWARE. 23 */ 24#include <fmod.h> 25#include <fmod_errors.h> 26#include "vl.h" 27 28#define AUDIO_CAP "fmod" 29#include "audio_int.h" 30 31typedef struct FMODVoiceOut { 32 HWVoiceOut hw; 33 unsigned int old_pos; 34 FSOUND_SAMPLE *fmod_sample; 35 int channel; 36} FMODVoiceOut; 37 38typedef struct FMODVoiceIn { 39 HWVoiceIn hw; 40 FSOUND_SAMPLE *fmod_sample; 41} FMODVoiceIn; 42 43static struct { 44 const char *drvname; 45 int nb_samples; 46 int freq; 47 int nb_channels; 48 int bufsize; 49 int threshold; 50 int broken_adc; 51} conf = { 52 NULL, 53 2048 * 2, 54 44100, 55 2, 56 0, 57 0, 58 0 59}; 60 61static void GCC_FMT_ATTR (1, 2) fmod_logerr (const char *fmt, ...) 62{ 63 va_list ap; 64 65 va_start (ap, fmt); 66 AUD_vlog (AUDIO_CAP, fmt, ap); 67 va_end (ap); 68 69 AUD_log (AUDIO_CAP, "Reason: %s\n", 70 FMOD_ErrorString (FSOUND_GetError ())); 71} 72 73static void GCC_FMT_ATTR (2, 3) fmod_logerr2 ( 74 const char *typ, 75 const char *fmt, 76 ... 77 ) 78{ 79 va_list ap; 80 81 AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); 82 83 va_start (ap, fmt); 84 AUD_vlog (AUDIO_CAP, fmt, ap); 85 va_end (ap); 86 87 AUD_log (AUDIO_CAP, "Reason: %s\n", 88 FMOD_ErrorString (FSOUND_GetError ())); 89} 90 91static int fmod_write (SWVoiceOut *sw, void *buf, int len) 92{ 93 return audio_pcm_sw_write (sw, buf, len); 94} 95 96static void fmod_clear_sample (FMODVoiceOut *fmd) 97{ 98 HWVoiceOut *hw = &fmd->hw; 99 int status; 100 void *p1 = 0, *p2 = 0; 101 unsigned int len1 = 0, len2 = 0; 102 103 status = FSOUND_Sample_Lock ( 104 fmd->fmod_sample, 105 0, 106 hw->samples << hw->info.shift, 107 &p1, 108 &p2, 109 &len1, 110 &len2 111 ); 112 113 if (!status) { 114 fmod_logerr ("Failed to lock sample\n"); 115 return; 116 } 117 118 if ((len1 & hw->info.align) || (len2 & hw->info.align)) { 119 dolog ("Lock returned misaligned length %d, %d, alignment %d\n", 120 len1, len2, hw->info.align + 1); 121 goto fail; 122 } 123 124 if ((len1 + len2) - (hw->samples << hw->info.shift)) { 125 dolog ("Lock returned incomplete length %d, %d\n", 126 len1 + len2, hw->samples << hw->info.shift); 127 goto fail; 128 } 129 130 audio_pcm_info_clear_buf (&hw->info, p1, hw->samples); 131 132 fail: 133 status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, len1, len2); 134 if (!status) { 135 fmod_logerr ("Failed to unlock sample\n"); 136 } 137} 138 139static void fmod_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len) 140{ 141 int src_len1 = dst_len; 142 int src_len2 = 0; 143 int pos = hw->rpos + dst_len; 144 st_sample_t *src1 = hw->mix_buf + hw->rpos; 145 st_sample_t *src2 = NULL; 146 147 if (pos > hw->samples) { 148 src_len1 = hw->samples - hw->rpos; 149 src2 = hw->mix_buf; 150 src_len2 = dst_len - src_len1; 151 pos = src_len2; 152 } 153 154 if (src_len1) { 155 hw->clip (dst, src1, src_len1); 156 } 157 158 if (src_len2) { 159 dst = advance (dst, src_len1 << hw->info.shift); 160 hw->clip (dst, src2, src_len2); 161 } 162 163 hw->rpos = pos % hw->samples; 164} 165 166static int fmod_unlock_sample (FSOUND_SAMPLE *sample, void *p1, void *p2, 167 unsigned int blen1, unsigned int blen2) 168{ 169 int status = FSOUND_Sample_Unlock (sample, p1, p2, blen1, blen2); 170 if (!status) { 171 fmod_logerr ("Failed to unlock sample\n"); 172 return -1; 173 } 174 return 0; 175} 176 177static int fmod_lock_sample ( 178 FSOUND_SAMPLE *sample, 179 struct audio_pcm_info *info, 180 int pos, 181 int len, 182 void **p1, 183 void **p2, 184 unsigned int *blen1, 185 unsigned int *blen2 186 ) 187{ 188 int status; 189 190 status = FSOUND_Sample_Lock ( 191 sample, 192 pos << info->shift, 193 len << info->shift, 194 p1, 195 p2, 196 blen1, 197 blen2 198 ); 199 200 if (!status) { 201 fmod_logerr ("Failed to lock sample\n"); 202 return -1; 203 } 204 205 if ((*blen1 & info->align) || (*blen2 & info->align)) { 206 dolog ("Lock returned misaligned length %d, %d, alignment %d\n", 207 *blen1, *blen2, info->align + 1); 208 209 fmod_unlock_sample (sample, *p1, *p2, *blen1, *blen2); 210 211 *p1 = NULL - 1; 212 *p2 = NULL - 1; 213 *blen1 = ~0U; 214 *blen2 = ~0U; 215 return -1; 216 } 217 218 if (!*p1 && *blen1) { 219 dolog ("warning: !p1 && blen1=%d\n", *blen1); 220 *blen1 = 0; 221 } 222 223 if (!p2 && *blen2) { 224 dolog ("warning: !p2 && blen2=%d\n", *blen2); 225 *blen2 = 0; 226 } 227 228 return 0; 229} 230 231static int fmod_run_out (HWVoiceOut *hw) 232{ 233 FMODVoiceOut *fmd = (FMODVoiceOut *) hw; 234 int live, decr; 235 void *p1 = 0, *p2 = 0; 236 unsigned int blen1 = 0, blen2 = 0; 237 unsigned int len1 = 0, len2 = 0; 238 int nb_live; 239 240 live = audio_pcm_hw_get_live_out2 (hw, &nb_live); 241 if (!live) { 242 return 0; 243 } 244 245 if (!hw->pending_disable 246 && nb_live 247 && (conf.threshold && live <= conf.threshold)) { 248 ldebug ("live=%d nb_live=%d\n", live, nb_live); 249 return 0; 250 } 251 252 decr = live; 253 254 if (fmd->channel >= 0) { 255 int len = decr; 256 int old_pos = fmd->old_pos; 257 int ppos = FSOUND_GetCurrentPosition (fmd->channel); 258 259 if (ppos == old_pos || !ppos) { 260 return 0; 261 } 262 263 if ((old_pos < ppos) && ((old_pos + len) > ppos)) { 264 len = ppos - old_pos; 265 } 266 else { 267 if ((old_pos > ppos) && ((old_pos + len) > (ppos + hw->samples))) { 268 len = hw->samples - old_pos + ppos; 269 } 270 } 271 decr = len; 272 273 if (audio_bug (AUDIO_FUNC, decr < 0)) { 274 dolog ("decr=%d live=%d ppos=%d old_pos=%d len=%d\n", 275 decr, live, ppos, old_pos, len); 276 return 0; 277 } 278 } 279 280 281 if (!decr) { 282 return 0; 283 } 284 285 if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info, 286 fmd->old_pos, decr, 287 &p1, &p2, 288 &blen1, &blen2)) { 289 return 0; 290 } 291 292 len1 = blen1 >> hw->info.shift; 293 len2 = blen2 >> hw->info.shift; 294 ldebug ("%p %p %d %d %d %d\n", p1, p2, len1, len2, blen1, blen2); 295 decr = len1 + len2; 296 297 if (p1 && len1) { 298 fmod_write_sample (hw, p1, len1); 299 } 300 301 if (p2 && len2) { 302 fmod_write_sample (hw, p2, len2); 303 } 304 305 fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2); 306 307 fmd->old_pos = (fmd->old_pos + decr) % hw->samples; 308 return decr; 309} 310 311static int aud_to_fmodfmt (audfmt_e fmt, int stereo) 312{ 313 int mode = FSOUND_LOOP_NORMAL; 314 315 switch (fmt) { 316 case AUD_FMT_S8: 317 mode |= FSOUND_SIGNED | FSOUND_8BITS; 318 break; 319 320 case AUD_FMT_U8: 321 mode |= FSOUND_UNSIGNED | FSOUND_8BITS; 322 break; 323 324 case AUD_FMT_S16: 325 mode |= FSOUND_SIGNED | FSOUND_16BITS; 326 break; 327 328 case AUD_FMT_U16: 329 mode |= FSOUND_UNSIGNED | FSOUND_16BITS; 330 break; 331 332 default: 333 dolog ("Internal logic error: Bad audio format %d\n", fmt); 334#ifdef DEBUG_FMOD 335 abort (); 336#endif 337 mode |= FSOUND_8BITS; 338 } 339 mode |= stereo ? FSOUND_STEREO : FSOUND_MONO; 340 return mode; 341} 342 343static void fmod_fini_out (HWVoiceOut *hw) 344{ 345 FMODVoiceOut *fmd = (FMODVoiceOut *) hw; 346 347 if (fmd->fmod_sample) { 348 FSOUND_Sample_Free (fmd->fmod_sample); 349 fmd->fmod_sample = 0; 350 351 if (fmd->channel >= 0) { 352 FSOUND_StopSound (fmd->channel); 353 } 354 } 355} 356 357static int fmod_init_out (HWVoiceOut *hw, audsettings_t *as) 358{ 359 int bits16, mode, channel; 360 FMODVoiceOut *fmd = (FMODVoiceOut *) hw; 361 audsettings_t obt_as = *as; 362 363 mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0); 364 fmd->fmod_sample = FSOUND_Sample_Alloc ( 365 FSOUND_FREE, /* index */ 366 conf.nb_samples, /* length */ 367 mode, /* mode */ 368 as->freq, /* freq */ 369 255, /* volume */ 370 128, /* pan */ 371 255 /* priority */ 372 ); 373 374 if (!fmd->fmod_sample) { 375 fmod_logerr2 ("DAC", "Failed to allocate FMOD sample\n"); 376 return -1; 377 } 378 379 channel = FSOUND_PlaySoundEx (FSOUND_FREE, fmd->fmod_sample, 0, 1); 380 if (channel < 0) { 381 fmod_logerr2 ("DAC", "Failed to start playing sound\n"); 382 FSOUND_Sample_Free (fmd->fmod_sample); 383 return -1; 384 } 385 fmd->channel = channel; 386 387 /* FMOD always operates on little endian frames? */ 388 obt_as.endianness = 0; 389 audio_pcm_init_info (&hw->info, &obt_as); 390 bits16 = (mode & FSOUND_16BITS) != 0; 391 hw->samples = conf.nb_samples; 392 return 0; 393} 394 395static int fmod_ctl_out (HWVoiceOut *hw, int cmd, ...) 396{ 397 int status; 398 FMODVoiceOut *fmd = (FMODVoiceOut *) hw; 399 400 switch (cmd) { 401 case VOICE_ENABLE: 402 fmod_clear_sample (fmd); 403 status = FSOUND_SetPaused (fmd->channel, 0); 404 if (!status) { 405 fmod_logerr ("Failed to resume channel %d\n", fmd->channel); 406 } 407 break; 408 409 case VOICE_DISABLE: 410 status = FSOUND_SetPaused (fmd->channel, 1); 411 if (!status) { 412 fmod_logerr ("Failed to pause channel %d\n", fmd->channel); 413 } 414 break; 415 } 416 return 0; 417} 418 419static int fmod_init_in (HWVoiceIn *hw, audsettings_t *as) 420{ 421 int bits16, mode; 422 FMODVoiceIn *fmd = (FMODVoiceIn *) hw; 423 audsettings_t obt_as = *as; 424 425 if (conf.broken_adc) { 426 return -1; 427 } 428 429 mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0); 430 fmd->fmod_sample = FSOUND_Sample_Alloc ( 431 FSOUND_FREE, /* index */ 432 conf.nb_samples, /* length */ 433 mode, /* mode */ 434 as->freq, /* freq */ 435 255, /* volume */ 436 128, /* pan */ 437 255 /* priority */ 438 ); 439 440 if (!fmd->fmod_sample) { 441 fmod_logerr2 ("ADC", "Failed to allocate FMOD sample\n"); 442 return -1; 443 } 444 445 /* FMOD always operates on little endian frames? */ 446 obt_as.endianness = 0; 447 audio_pcm_init_info (&hw->info, &obt_as); 448 bits16 = (mode & FSOUND_16BITS) != 0; 449 hw->samples = conf.nb_samples; 450 return 0; 451} 452 453static void fmod_fini_in (HWVoiceIn *hw) 454{ 455 FMODVoiceIn *fmd = (FMODVoiceIn *) hw; 456 457 if (fmd->fmod_sample) { 458 FSOUND_Record_Stop (); 459 FSOUND_Sample_Free (fmd->fmod_sample); 460 fmd->fmod_sample = 0; 461 } 462} 463 464static int fmod_run_in (HWVoiceIn *hw) 465{ 466 FMODVoiceIn *fmd = (FMODVoiceIn *) hw; 467 int hwshift = hw->info.shift; 468 int live, dead, new_pos, len; 469 unsigned int blen1 = 0, blen2 = 0; 470 unsigned int len1, len2; 471 unsigned int decr; 472 void *p1, *p2; 473 474 live = audio_pcm_hw_get_live_in (hw); 475 dead = hw->samples - live; 476 if (!dead) { 477 return 0; 478 } 479 480 new_pos = FSOUND_Record_GetPosition (); 481 if (new_pos < 0) { 482 fmod_logerr ("Could not get recording position\n"); 483 return 0; 484 } 485 486 len = audio_ring_dist (new_pos, hw->wpos, hw->samples); 487 if (!len) { 488 return 0; 489 } 490 len = audio_MIN (len, dead); 491 492 if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info, 493 hw->wpos, len, 494 &p1, &p2, 495 &blen1, &blen2)) { 496 return 0; 497 } 498 499 len1 = blen1 >> hwshift; 500 len2 = blen2 >> hwshift; 501 decr = len1 + len2; 502 503 if (p1 && blen1) { 504 hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume); 505 } 506 if (p2 && len2) { 507 hw->conv (hw->conv_buf, p2, len2, &nominal_volume); 508 } 509 510 fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2); 511 hw->wpos = (hw->wpos + decr) % hw->samples; 512 return decr; 513} 514 515static struct { 516 const char *name; 517 int type; 518} drvtab[] = { 519 {"none", FSOUND_OUTPUT_NOSOUND}, 520#ifdef _WIN32 521 {"winmm", FSOUND_OUTPUT_WINMM}, 522 {"dsound", FSOUND_OUTPUT_DSOUND}, 523 {"a3d", FSOUND_OUTPUT_A3D}, 524 {"asio", FSOUND_OUTPUT_ASIO}, 525#endif 526#ifdef __linux__ 527 {"oss", FSOUND_OUTPUT_OSS}, 528 {"alsa", FSOUND_OUTPUT_ALSA}, 529 {"esd", FSOUND_OUTPUT_ESD}, 530#endif 531#ifdef __APPLE__ 532 {"mac", FSOUND_OUTPUT_MAC}, 533#endif 534#if 0 535 {"xbox", FSOUND_OUTPUT_XBOX}, 536 {"ps2", FSOUND_OUTPUT_PS2}, 537 {"gcube", FSOUND_OUTPUT_GC}, 538#endif 539 {"none-realtime", FSOUND_OUTPUT_NOSOUND_NONREALTIME} 540}; 541 542static void *fmod_audio_init (void) 543{ 544 size_t i; 545 double ver; 546 int status; 547 int output_type = -1; 548 const char *drv = conf.drvname; 549 550 ver = FSOUND_GetVersion (); 551 if (ver < FMOD_VERSION) { 552 dolog ("Wrong FMOD version %f, need at least %f\n", ver, FMOD_VERSION); 553 return NULL; 554 } 555 556#ifdef __linux__ 557 if (ver < 3.75) { 558 dolog ("FMOD before 3.75 has bug preventing ADC from working\n" 559 "ADC will be disabled.\n"); 560 conf.broken_adc = 1; 561 } 562#endif 563 564 if (drv) { 565 int found = 0; 566 for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { 567 if (!strcmp (drv, drvtab[i].name)) { 568 output_type = drvtab[i].type; 569 found = 1; 570 break; 571 } 572 } 573 if (!found) { 574 dolog ("Unknown FMOD driver `%s'\n", drv); 575 dolog ("Valid drivers:\n"); 576 for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { 577 dolog (" %s\n", drvtab[i].name); 578 } 579 } 580 } 581 582 if (output_type != -1) { 583 status = FSOUND_SetOutput (output_type); 584 if (!status) { 585 fmod_logerr ("FSOUND_SetOutput(%d) failed\n", output_type); 586 return NULL; 587 } 588 } 589 590 if (conf.bufsize) { 591 status = FSOUND_SetBufferSize (conf.bufsize); 592 if (!status) { 593 fmod_logerr ("FSOUND_SetBufferSize (%d) failed\n", conf.bufsize); 594 } 595 } 596 597 status = FSOUND_Init (conf.freq, conf.nb_channels, 0); 598 if (!status) { 599 fmod_logerr ("FSOUND_Init failed\n"); 600 return NULL; 601 } 602 603 return &conf; 604} 605 606static int fmod_read (SWVoiceIn *sw, void *buf, int size) 607{ 608 return audio_pcm_sw_read (sw, buf, size); 609} 610 611static int fmod_ctl_in (HWVoiceIn *hw, int cmd, ...) 612{ 613 int status; 614 FMODVoiceIn *fmd = (FMODVoiceIn *) hw; 615 616 switch (cmd) { 617 case VOICE_ENABLE: 618 status = FSOUND_Record_StartSample (fmd->fmod_sample, 1); 619 if (!status) { 620 fmod_logerr ("Failed to start recording\n"); 621 } 622 break; 623 624 case VOICE_DISABLE: 625 status = FSOUND_Record_Stop (); 626 if (!status) { 627 fmod_logerr ("Failed to stop recording\n"); 628 } 629 break; 630 } 631 return 0; 632} 633 634static void fmod_audio_fini (void *opaque) 635{ 636 (void) opaque; 637 FSOUND_Close (); 638} 639 640static struct audio_option fmod_options[] = { 641 {"DRV", AUD_OPT_STR, &conf.drvname, 642 "FMOD driver", NULL, 0}, 643 {"FREQ", AUD_OPT_INT, &conf.freq, 644 "Default frequency", NULL, 0}, 645 {"SAMPLES", AUD_OPT_INT, &conf.nb_samples, 646 "Buffer size in samples", NULL, 0}, 647 {"CHANNELS", AUD_OPT_INT, &conf.nb_channels, 648 "Number of default channels (1 - mono, 2 - stereo)", NULL, 0}, 649 {"BUFSIZE", AUD_OPT_INT, &conf.bufsize, 650 "(undocumented)", NULL, 0}, 651#if 0 652 {"THRESHOLD", AUD_OPT_INT, &conf.threshold, 653 "(undocumented)"}, 654#endif 655 656 {NULL, 0, NULL, NULL, NULL, 0} 657}; 658 659static struct audio_pcm_ops fmod_pcm_ops = { 660 fmod_init_out, 661 fmod_fini_out, 662 fmod_run_out, 663 fmod_write, 664 fmod_ctl_out, 665 666 fmod_init_in, 667 fmod_fini_in, 668 fmod_run_in, 669 fmod_read, 670 fmod_ctl_in 671}; 672 673struct audio_driver fmod_audio_driver = { 674 INIT_FIELD (name = ) "fmod", 675 INIT_FIELD (descr = ) "FMOD 3.xx http://www.fmod.org", 676 INIT_FIELD (options = ) fmod_options, 677 INIT_FIELD (init = ) fmod_audio_init, 678 INIT_FIELD (fini = ) fmod_audio_fini, 679 INIT_FIELD (pcm_ops = ) &fmod_pcm_ops, 680 INIT_FIELD (can_be_default = ) 1, 681 INIT_FIELD (max_voices_out = ) INT_MAX, 682 INIT_FIELD (max_voices_in = ) INT_MAX, 683 INIT_FIELD (voice_size_out = ) sizeof (FMODVoiceOut), 684 INIT_FIELD (voice_size_in = ) sizeof (FMODVoiceIn) 685}; 686