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