multivoc.cpp
1 /* 2 Copyright (C) 1994-1995 Apogee Software, Ltd. 3 Copyright (C) 2015 EDuke32 developers 4 Copyright (C) 2015 Voidpoint, LLC 5 6 This program is free software; you can redistribute it and/or 7 modify it under the terms of the GNU General Public License 8 as published by the Free Software Foundation; either version 2 9 of the License, or (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 15 See the GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 21 */ 22 /********************************************************************** 23 module: MULTIVOC.C 24 25 author: James R. Dose 26 date: December 20, 1993 27 28 Routines to provide multichannel digitized sound playback for 29 Sound Blaster compatible sound cards. 30 31 (c) Copyright 1993 James R. Dose. All Rights Reserved. 32 **********************************************************************/ 33 34 #include "multivoc.h" 35 36 #include "_multivc.h" 37 #include "baselayer.h" 38 #include "compat.h" 39 #include "drivers.h" 40 #include "fx_man.h" 41 #include "libasync_config.h" 42 #include "linklist.h" 43 #include "osd.h" 44 #include "pitch.h" 45 #include "pragmas.h" 46 47 #ifdef HAVE_XMP 48 # define BUILDING_STATIC 49 # include "libxmp-lite/xmp.h" 50 51 int MV_XMPInterpolation = XMP_INTERP_NEAREST; 52 #endif 53 54 55 static void MV_StopVoice(VoiceNode *voice, bool useCallBack = true); 56 static void MV_ServiceVoc(void); 57 58 static VoiceNode *MV_GetVoice(int handle); 59 60 static int MV_ReverbLevel; 61 static int MV_ReverbDelay; 62 static fix16_t MV_ReverbVolume; 63 64 Pan MV_PanTable[MV_NUMPANPOSITIONS][MV_MAXVOLUME + 1]; 65 66 int MV_Installed; 67 68 int MV_BufferSize = MV_MIXBUFFERSIZE; 69 static int MV_BufferLength; 70 71 static int MV_NumberOfBuffers = MV_NUMBEROFBUFFERS; 72 73 int MV_MaxVoices = 1; 74 int MV_Channels = 1; 75 int MV_MixRate; 76 void *MV_InitDataPtr; 77 78 int MV_LazyAlloc = true; 79 80 #ifdef ASS_REVERSESTEREO 81 static int MV_ReverseStereo; 82 #endif 83 84 static int MV_BufferEmpty[MV_NUMBEROFBUFFERS]; 85 char *MV_MixBuffer[(MV_NUMBEROFBUFFERS << 1) + 1]; 86 87 VoiceNode *MV_Voices; 88 VoiceNode VoiceList; 89 VoiceNode VoicePool; 90 91 static int MV_MixPage; 92 93 static void (*MV_CallBackFunc)(intptr_t); 94 95 char *MV_MixDestination; 96 int MV_SampleSize = 1; 97 int MV_RightChannelOffset; 98 99 int MV_ErrorCode = MV_NotInstalled; 100 101 fix16_t MV_GlobalVolume = fix16_one; 102 fix16_t MV_VolumeSmoothFactor = fix16_one; 103 104 thread_local int MV_Locked; 105 char *MV_MusicBuffer; 106 static void (*MV_MusicCallback)(void); 107 108 static VoiceNode **MV_Handles; 109 110 static bool MV_Mix(VoiceNode * const voice, int const buffer) 111 { 112 if (voice->task.valid()) 113 { 114 if (!voice->task.ready()) 115 return true; 116 117 auto result = voice->task.get(); 118 119 if (result != MV_Ok) 120 { 121 LOG_F(ERROR, "Error playing sound 0x%08" PRIxPTR ": %s", voice->callbackval, MV_ErrorString(result)); 122 return false; 123 } 124 } 125 126 if (voice->length == 0 && voice->GetSound(voice) != KeepPlaying) 127 return false; 128 129 fix16_t const gv = MV_GlobalVolume; 130 131 if (voice->priority == FX_MUSIC_PRIORITY) 132 MV_GlobalVolume = fix16_one; 133 134 int length = MV_MIXBUFFERSIZE; 135 uint32_t bufsiz = voice->FixedPointBufferSize; 136 uint32_t const rate = voice->RateScale; 137 138 MV_MixDestination = MV_MixBuffer[buffer]; 139 140 // Add this voice to the mix 141 do 142 { 143 int mixlen = length; 144 uint32_t const position = voice->position; 145 uint32_t const voclen = voice->length; 146 147 // Check if the last sample in this buffer would be 148 // beyond the length of the sample block 149 if ((position + bufsiz) >= voclen) 150 { 151 if (position >= voclen - voice->channels) 152 { 153 if (voice->GetSound(voice) != KeepPlaying) 154 { 155 MV_GlobalVolume = gv; 156 return false; 157 } 158 159 break; 160 } 161 162 mixlen = (voclen - position + rate - voice->channels) / rate; 163 } 164 165 voice->position = voice->mix(voice, mixlen); 166 length -= mixlen; 167 168 if (voice->position >= voclen - voice->channels) 169 { 170 // Get the next block of sound 171 if (voice->GetSound(voice) != KeepPlaying) 172 { 173 MV_GlobalVolume = gv; 174 return false; 175 } 176 177 // Get the position of the last sample in the buffer 178 if (length > (voice->channels - 1)) 179 bufsiz = voice->RateScale * (length - voice->channels); 180 } 181 } while (length > 0); 182 183 MV_GlobalVolume = gv; 184 return true; 185 } 186 187 void MV_PlayVoice(VoiceNode *voice) 188 { 189 MV_Lock(); 190 LL::SortedInsert(&VoiceList, voice, &VoiceNode::priority); 191 voice->PannedVolume = voice->GoalVolume; 192 voice->Paused.store(false, std::memory_order_release); 193 MV_Unlock(); 194 } 195 196 static void MV_FreeHandle(VoiceNode* voice) 197 { 198 if (voice->handle < MV_MINVOICEHANDLE) 199 return; 200 201 MV_Handles[voice->handle - MV_MINVOICEHANDLE] = nullptr; 202 voice->handle = 0; 203 voice->length = 0; 204 voice->sound = nullptr; 205 voice->wavetype = FMT_UNKNOWN; 206 LL::Move(voice, &VoicePool); 207 } 208 209 static void MV_CleanupVoice(VoiceNode* voice, bool useCallBack = true) 210 { 211 if (useCallBack && MV_CallBackFunc) 212 MV_CallBackFunc(voice->callbackval); 213 214 switch (voice->wavetype) 215 { 216 #ifdef HAVE_VORBIS 217 case FMT_VORBIS: MV_ReleaseVorbisVoice(voice); break; 218 #endif 219 #ifdef HAVE_FLAC 220 case FMT_FLAC: MV_ReleaseFLACVoice(voice); break; 221 #endif 222 case FMT_XA: MV_ReleaseXAVoice(voice); break; 223 #ifdef HAVE_XMP 224 case FMT_XMP: MV_ReleaseXMPVoice(voice); break; 225 #endif 226 default: 227 // these are in the default case of this switch instead of down below because the functions above only zero them if MV_LazyAlloc is false 228 voice->rawdataptr = nullptr; 229 voice->rawdatasiz = 0; 230 break; 231 } 232 } 233 234 void MV_StopVoice(VoiceNode *voice, bool useCallBack) 235 { 236 MV_CleanupVoice(voice, useCallBack); 237 MV_Lock(); 238 // move the voice from the play list to the free list 239 MV_FreeHandle(voice); 240 MV_Unlock(); 241 } 242 243 /*--------------------------------------------------------------------- 244 JBF: no synchronisation happens inside MV_ServiceVoc nor the 245 supporting functions it calls. This would cause a deadlock 246 between the mixer thread in the driver vs the nested 247 locking in the user-space functions of MultiVoc. The call 248 to MV_ServiceVoc is synchronised in the driver. 249 ---------------------------------------------------------------------*/ 250 static void MV_ServiceVoc(void) 251 { 252 // Toggle which buffer we'll mix next 253 ++MV_MixPage; 254 MV_MixPage &= MV_NumberOfBuffers-1; 255 256 if (MV_ReverbLevel == 0) 257 { 258 if (!MV_BufferEmpty[MV_MixPage]) 259 { 260 Bmemset(MV_MixBuffer[MV_MixPage], 0, MV_BufferSize); 261 MV_BufferEmpty[MV_MixPage] = TRUE; 262 } 263 } 264 else 265 { 266 char const *const __restrict end = MV_MixBuffer[0] + MV_BufferLength; 267 char * __restrict dest = MV_MixBuffer[MV_MixPage]; 268 char const * __restrict source = MV_MixBuffer[MV_MixPage] - MV_ReverbDelay; 269 270 if (source < MV_MixBuffer[0]) 271 source += MV_BufferLength; 272 273 int length = MV_BufferSize; 274 275 do 276 { 277 int const count = (source + length > end) ? (end - source) : length; 278 279 MV_Reverb<int16_t>(source, dest, MV_ReverbVolume, count >> 1); 280 281 // if we go through the loop again, it means that we've wrapped around the buffer 282 source = MV_MixBuffer[0]; 283 dest += count; 284 length -= count; 285 } while (length > 0); 286 } 287 288 VoiceNode *MusicVoice = nullptr; 289 290 if (VoiceList.next && VoiceList.next != &VoiceList) 291 { 292 auto voice = VoiceList.next; 293 VoiceNode *next; 294 295 do 296 { 297 next = voice->next; 298 299 if (voice->Paused.load(std::memory_order_acquire)) 300 continue; 301 302 if (voice->priority == FX_MUSIC_PRIORITY) 303 { 304 MusicVoice = voice; 305 continue; 306 } 307 308 MV_BufferEmpty[ MV_MixPage ] = FALSE; 309 310 // Is this voice done? 311 if (!MV_Mix(voice, MV_MixPage)) 312 { 313 MV_CleanupVoice(voice); 314 MV_FreeHandle(voice); 315 } 316 } 317 while ((voice = next) != &VoiceList); 318 } 319 320 Bmemcpy(MV_MixBuffer[MV_MixPage+MV_NumberOfBuffers], MV_MixBuffer[MV_MixPage], MV_BufferSize); 321 322 if (MV_MusicCallback) 323 { 324 MV_MusicCallback(); 325 int16_t * __restrict source = (int16_t*)MV_MusicBuffer; 326 int16_t * __restrict dest = (int16_t*)MV_MixBuffer[MV_MixPage+MV_NumberOfBuffers]; 327 for (int32_t i = 0; i < MV_BufferSize>>1; i++, dest++) 328 *dest = clamp(*dest + *source++,INT16_MIN, INT16_MAX); 329 } 330 331 if (MusicVoice && !MV_Mix(MusicVoice, MV_MixPage + MV_NumberOfBuffers)) 332 { 333 MV_CleanupVoice(MusicVoice); 334 MV_FreeHandle(MusicVoice); 335 } 336 } 337 338 static VoiceNode *MV_GetVoice(int handle) 339 { 340 if (handle < MV_MINVOICEHANDLE || handle > MV_MaxVoices) 341 { 342 LOG_F(WARNING, "No voice found for handle 0x%08x", handle); 343 return nullptr; 344 } 345 346 if (MV_Handles[handle - MV_MINVOICEHANDLE] != nullptr) 347 return MV_Handles[handle - MV_MINVOICEHANDLE]; 348 349 MV_SetErrorCode(MV_VoiceNotFound); 350 return nullptr; 351 } 352 353 VoiceNode *MV_BeginService(int handle) 354 { 355 if (!MV_Installed) 356 return nullptr; 357 358 auto voice = MV_GetVoice(handle); 359 360 if (voice == nullptr) 361 { 362 MV_SetErrorCode(MV_VoiceNotFound); 363 return nullptr; 364 } 365 366 if (voice->task.valid() && !voice->task.ready()) 367 voice->task.wait(); 368 369 MV_Lock(); 370 371 return voice; 372 } 373 374 static inline void MV_EndService(void) { MV_Unlock(); } 375 376 int MV_VoicePlaying(int handle) 377 { 378 Bassert(handle <= MV_MaxVoices); 379 auto voice = MV_Handles[handle - MV_MINVOICEHANDLE]; 380 return MV_Installed && voice != nullptr && !voice->Paused.load(std::memory_order_relaxed); 381 } 382 383 int MV_KillAllVoices(bool useCallBack) 384 { 385 if (!MV_Installed) 386 return MV_Error; 387 388 MV_Lock(); 389 390 if (&VoiceList == VoiceList.next) 391 { 392 MV_Unlock(); 393 return MV_Ok; 394 } 395 396 auto voice = VoiceList.prev; 397 398 // Remove all the voices from the list 399 while (voice != &VoiceList) 400 { 401 if (voice->priority == MV_MUSIC_PRIORITY) 402 { 403 voice = voice->prev; 404 continue; 405 } 406 407 MV_Kill(voice->handle, useCallBack); 408 voice = VoiceList.prev; 409 } 410 411 MV_Unlock(); 412 413 return MV_Ok; 414 } 415 416 int MV_Kill(int handle, bool useCallBack) 417 { 418 auto voice = MV_BeginService(handle); 419 420 if (voice == nullptr) 421 return MV_Error; 422 423 MV_StopVoice(voice, useCallBack); 424 MV_EndService(); 425 426 return MV_Ok; 427 } 428 429 int MV_VoicesPlaying(void) 430 { 431 if (!MV_Installed) 432 return 0; 433 434 MV_Lock(); 435 436 int NumVoices = 0; 437 438 for (auto voice = VoiceList.next; voice != &VoiceList; voice = voice->next) 439 NumVoices++; 440 441 MV_Unlock(); 442 443 return NumVoices; 444 } 445 446 static inline VoiceNode *MV_GetLowestPriorityVoice(void) 447 { 448 auto voice = VoiceList.next; 449 450 // find the voice with the lowest priority and volume 451 for (auto node = voice; node != &VoiceList; node = node->next) 452 { 453 if (node->priority < voice->priority 454 || (node->priority == voice->priority && node->PannedVolume.Left < voice->PannedVolume.Left && node->PannedVolume.Right < voice->PannedVolume.Right)) 455 voice = node; 456 } 457 458 return voice; 459 } 460 461 static inline void MV_FinishAllocation(VoiceNode* voice, uint32_t const allocsize) 462 { 463 if (voice->rawdataptr != nullptr && voice->rawdatasiz == allocsize) 464 return; 465 else if (voice->rawdataptr != nullptr && voice->wavetype >= FMT_VORBIS) 466 { 467 // this is sort of a hack... wavetypes less than FMT_VORBIS never do their own allocations, so don't bother trying to free them 468 ALIGNED_FREE_AND_NULL(voice->rawdataptr); 469 } 470 471 voice->rawdatasiz = allocsize; 472 voice->rawdataptr = Xaligned_alloc(16, allocsize); 473 Bmemset(voice->rawdataptr, 0, allocsize); 474 } 475 476 VoiceNode *MV_AllocVoice(int priority, uint32_t allocsize /* = 0 */) 477 { 478 MV_Lock(); 479 480 // Check if we have any free voices 481 if (LL::Empty(&VoicePool)) 482 { 483 auto voice = MV_GetLowestPriorityVoice(); 484 485 if (voice != &VoiceList && voice->priority <= priority && voice->handle >= MV_MINVOICEHANDLE && FX_SoundValidAndActive(voice->handle)) 486 MV_Kill(voice->handle); 487 488 if (LL::Empty(&VoicePool)) 489 { 490 // No free voices 491 MV_Unlock(); 492 return nullptr; 493 } 494 } 495 496 auto voice = VoicePool.next; 497 LL::Remove(voice); 498 499 int handle = MV_MINVOICEHANDLE; 500 501 // Find a free voice handle 502 do 503 { 504 if (++handle > MV_MaxVoices) 505 handle = MV_MINVOICEHANDLE; 506 } while (MV_Handles[handle - MV_MINVOICEHANDLE] != nullptr); 507 MV_Handles[handle - MV_MINVOICEHANDLE] = voice; 508 509 voice->length = 0; 510 voice->BlockLength = 0; 511 voice->handle = handle; 512 voice->next = voice->prev = nullptr; 513 MV_Unlock(); 514 515 if (allocsize) 516 MV_FinishAllocation(voice, allocsize); 517 518 return voice; 519 } 520 521 int MV_VoiceAvailable(int priority) 522 { 523 // Check if we have any free voices 524 if (!LL::Empty(&VoicePool)) 525 return TRUE; 526 527 MV_Lock(); 528 auto const voice = MV_GetLowestPriorityVoice(); 529 MV_Unlock(); 530 531 return (voice == &VoiceList || voice->priority > priority) ? FALSE : TRUE; 532 } 533 534 void MV_SetVoicePitch(VoiceNode *voice, uint32_t rate, int pitchoffset) 535 { 536 voice->SamplingRate = rate; 537 voice->PitchScale = PITCH_GetScale(pitchoffset); 538 voice->RateScale = divideu64((uint64_t)rate * voice->PitchScale, MV_MixRate); 539 540 // Multiply by MV_MIXBUFFERSIZE - 1 541 voice->FixedPointBufferSize = (voice->RateScale * MV_MIXBUFFERSIZE) - 542 voice->RateScale; 543 } 544 545 int MV_SetPitch(int handle, int pitchoffset) 546 { 547 auto voice = MV_BeginService(handle); 548 549 if (voice == nullptr) 550 return MV_Error; 551 552 MV_SetVoicePitch(voice, voice->SamplingRate, pitchoffset); 553 MV_EndService(); 554 555 return MV_Ok; 556 } 557 558 int MV_SetFrequency(int handle, int frequency) 559 { 560 auto voice = MV_BeginService(handle); 561 562 if (voice == nullptr) 563 return MV_Error; 564 565 MV_SetVoicePitch(voice, frequency, 0); 566 MV_EndService(); 567 568 return MV_Ok; 569 } 570 571 int MV_GetFrequency(int handle, int *frequency) 572 { 573 auto voice = MV_BeginService(handle); 574 575 if (voice == NULL || !frequency) 576 return MV_Error; 577 578 if (voice->SamplingRate == 0) 579 voice->GetSound(voice); 580 581 *frequency = voice->SamplingRate; 582 MV_EndService(); 583 584 return MV_Ok; 585 } 586 587 /*--------------------------------------------------------------------- 588 Function: MV_SetVoiceMixMode 589 590 Selects which method should be used to mix the voice. 591 592 16Bit 16Bit | 8Bit 16Bit 8Bit 16Bit | 593 Mono Ster | Mono Mono Ster Ster | Mixer 594 Out Out | In In In In | 595 ----------------------+---------------------------+------------- 596 X | X | MixMono<int16_t, int16_t> 597 X | X | MixMono<uint8_t, int16_t> 598 X | X | MixStereo<int16_t, int16_t> 599 X | X | MixStereo<uint8_t, int16_t> 600 ----------------------+---------------------------+------------- 601 X | X | MixStereoStereo<int16_t, int16_t> 602 X | X | MixStereoStereo<uint8_t, int16_t> 603 X | X | MixMonoStereo<int16_t, int16_t> 604 X | X | MixMonoStereo<uint8_t, int16_t> 605 ---------------------------------------------------------------------*/ 606 607 void MV_SetVoiceMixMode(VoiceNode *voice) 608 { 609 // stereo look-up table 610 static constexpr decltype(voice->mix) mixslut[] 611 = { MV_MixStereo<uint8_t, int16_t>, MV_MixMono<uint8_t, int16_t>, MV_MixStereo<int16_t, int16_t>, MV_MixMono<int16_t, int16_t>, 612 MV_MixStereoStereo<uint8_t, int16_t>, MV_MixMonoStereo<uint8_t, int16_t>, MV_MixStereoStereo<int16_t, int16_t>, MV_MixMonoStereo<int16_t, int16_t> }; 613 614 // corresponds to T_MONO, T_16BITSOURCE, and T_STEREOSOURCE 615 voice->mix = mixslut[(MV_Channels == 1) | ((voice->bits == 16) << 1) | ((voice->channels == 2) << 2)]; 616 } 617 618 void MV_SetVoiceVolume(VoiceNode *voice, int vol, int left, int right, fix16_t volume) 619 { 620 if (MV_Channels == 1) 621 left = right = vol; 622 #ifdef ASS_REVERSESTEREO 623 else if (MV_ReverseStereo) 624 swap(&left, &right); 625 #endif 626 627 voice->GoalVolume = { fix16_smul(fix16_from_int(left), F16(1.f/MV_MAXTOTALVOLUME)), fix16_smul(fix16_from_int(right), F16(1.f/MV_MAXTOTALVOLUME)) }; 628 voice->volume = volume; 629 630 MV_SetVoiceMixMode(voice); 631 } 632 633 int MV_PauseVoice(int handle, int pause) 634 { 635 auto voice = MV_BeginService(handle); 636 637 if (voice == nullptr) 638 return MV_Error; 639 640 voice->Paused.store(pause, std::memory_order_release); 641 MV_EndService(); 642 643 return MV_Ok; 644 } 645 646 int MV_GetPosition(int handle, int *position) 647 { 648 auto voice = MV_BeginService(handle); 649 650 if (voice == nullptr) 651 return MV_Error; 652 653 switch (voice->wavetype) 654 { 655 #ifdef HAVE_VORBIS 656 case FMT_VORBIS: *position = MV_GetVorbisPosition(voice); break; 657 #endif 658 #ifdef HAVE_FLAC 659 case FMT_FLAC: *position = MV_GetFLACPosition(voice); break; 660 #endif 661 case FMT_XA: *position = MV_GetXAPosition(voice); break; 662 #ifdef HAVE_XMP 663 case FMT_XMP: *position = MV_GetXMPPosition(voice); break; 664 #endif 665 default: *position = (int)max<intptr_t>(0, (((intptr_t)voice->NextBlock + (intptr_t)voice->position - (intptr_t)voice->rawdataptr) >> 16) * ((voice->channels * voice->bits) >> 3)); break; 666 } 667 668 MV_EndService(); 669 670 return MV_Ok; 671 } 672 673 int MV_SetPosition(int handle, int position) 674 { 675 auto voice = MV_BeginService(handle); 676 677 if (voice == nullptr) 678 return MV_Error; 679 680 switch (voice->wavetype) 681 { 682 #ifdef HAVE_VORBIS 683 case FMT_VORBIS: MV_SetVorbisPosition(voice, position); break; 684 #endif 685 #ifdef HAVE_FLAC 686 case FMT_FLAC: MV_SetFLACPosition(voice, position); break; 687 #endif 688 case FMT_XA: MV_SetXAPosition(voice, position); break; 689 #ifdef HAVE_XMP 690 case FMT_XMP: MV_SetXMPPosition(voice, position); break; 691 #endif 692 default: break; 693 } 694 695 MV_EndService(); 696 697 return MV_Ok; 698 } 699 700 int MV_EndLooping(int handle) 701 { 702 auto voice = MV_BeginService(handle); 703 704 if (voice == nullptr) 705 return MV_Error; 706 707 voice->Loop = {}; 708 709 MV_EndService(); 710 711 return MV_Ok; 712 } 713 714 int MV_SetPan(int handle, int vol, int left, int right) 715 { 716 auto voice = MV_BeginService(handle); 717 718 if (voice == nullptr) 719 return MV_Error; 720 721 MV_SetVoiceVolume(voice, vol, left, right, voice->volume); 722 MV_EndService(); 723 return MV_Ok; 724 } 725 726 int MV_Pan3D(int handle, int angle, int distance) 727 { 728 if (distance < 0) 729 { 730 distance = -distance; 731 angle += MV_NUMPANPOSITIONS / 2; 732 } 733 734 int const volume = MIX_VOLUME(distance); 735 736 angle &= MV_MAXPANPOSITION; 737 738 return MV_SetPan(handle, max(0, 255 - distance), 739 MV_PanTable[angle][volume].left, 740 MV_PanTable[angle][volume].right); 741 } 742 743 void MV_SetReverb(int reverb) 744 { 745 MV_ReverbLevel = MIX_VOLUME(reverb); 746 MV_ReverbVolume = fix16_smul(fix16_from_int(MV_ReverbLevel), F16(1.f/MV_MAXVOLUME)); 747 } 748 749 int MV_GetMaxReverbDelay(void) { return MV_MIXBUFFERSIZE * MV_NumberOfBuffers; } 750 int MV_GetReverbDelay(void) { return tabledivide32(MV_ReverbDelay, MV_SampleSize); } 751 752 void MV_SetReverbDelay(int delay) 753 { 754 MV_ReverbDelay = max(MV_MIXBUFFERSIZE, min(delay, MV_GetMaxReverbDelay())) * MV_SampleSize; 755 } 756 757 static int MV_SetMixMode(int numchannels) 758 { 759 if (!MV_Installed) 760 return MV_Error; 761 762 MV_Channels = 1 + (numchannels == 2); 763 MV_SampleSize = sizeof(int16_t) * MV_Channels; 764 765 MV_BufferSize = MV_MIXBUFFERSIZE * MV_SampleSize; 766 MV_NumberOfBuffers = tabledivide32(MV_TOTALBUFFERSIZE, MV_BufferSize); 767 Bassert(isPow2(MV_NumberOfBuffers)); 768 MV_BufferLength = MV_TOTALBUFFERSIZE; 769 770 MV_RightChannelOffset = MV_SampleSize >> 1; 771 772 return MV_Ok; 773 } 774 775 static int MV_StartPlayback(void) 776 { 777 // Initialize the buffers 778 Bmemset(MV_MixBuffer[0], 0, MV_TOTALBUFFERSIZE << 1); 779 780 for (int buffer = 0; buffer < MV_NumberOfBuffers; buffer++) 781 MV_BufferEmpty[buffer] = TRUE; 782 783 MV_MixPage = 1; 784 785 if (SoundDriver_PCM_BeginPlayback(MV_MixBuffer[MV_NumberOfBuffers], MV_BufferSize, MV_NumberOfBuffers, MV_ServiceVoc) != MV_Ok) 786 return MV_SetErrorCode(MV_DriverError); 787 788 return MV_Ok; 789 } 790 791 static void MV_StopPlayback(void) 792 { 793 SoundDriver_PCM_StopPlayback(); 794 795 // Make sure all callbacks are done. 796 MV_Lock(); 797 798 for (VoiceNode *voice = VoiceList.next, *next; voice != &VoiceList; voice = next) 799 { 800 next = voice->next; 801 MV_StopVoice(voice); 802 } 803 804 MV_Unlock(); 805 } 806 807 static void MV_CalcPanTable(void) 808 { 809 const int HalfAngle = MV_NUMPANPOSITIONS / 2; 810 const int QuarterAngle = HalfAngle / 2; 811 812 for (int distance = 0; distance <= MV_MAXVOLUME; distance++) 813 { 814 const int level = (255 * (MV_MAXVOLUME - distance)) / MV_MAXVOLUME; 815 816 for (int angle = 0; angle <= QuarterAngle; angle++) 817 { 818 const int ramp = level - (level * angle) / QuarterAngle; 819 820 MV_PanTable[angle][distance].left = ramp; 821 MV_PanTable[angle][distance].right = level; 822 823 MV_PanTable[HalfAngle - angle][distance].left = ramp; 824 MV_PanTable[HalfAngle - angle][distance].right = level; 825 826 MV_PanTable[HalfAngle + angle][distance].left = level; 827 MV_PanTable[HalfAngle + angle][distance].right = ramp; 828 829 MV_PanTable[MV_MAXPANPOSITION - angle][distance].left = level; 830 MV_PanTable[MV_MAXPANPOSITION - angle][distance].right = ramp; 831 } 832 } 833 } 834 835 void MV_SetVolume(int volume) { MV_GlobalVolume = fix16_smul(fix16_from_int(volume), F16(1.f/MV_MAXTOTALVOLUME)); } 836 837 int MV_GetVolume(void) { return Blrintf(fix16_to_float(MV_GlobalVolume) * MV_MAXTOTALVOLUME); } 838 839 void MV_SetCallBack(void (*function)(intptr_t)) { MV_CallBackFunc = function; } 840 841 #ifdef ASS_REVERSESTEREO 842 void MV_SetReverseStereo(int setting) { MV_ReverseStereo = setting; } 843 int MV_GetReverseStereo(void) { return MV_ReverseStereo; } 844 #endif 845 846 int MV_Init(int soundcard, int MixRate, int Voices, int numchannels, void *initdata) 847 { 848 if (MV_Installed) 849 MV_Shutdown(); 850 851 MV_SetErrorCode(MV_Ok); 852 853 int const totalmem = Voices * sizeof(VoiceNode) + (MV_TOTALBUFFERSIZE * sizeof(int16_t)) + (MV_MIXBUFFERSIZE * numchannels * sizeof(int16_t)); 854 855 char *ptr = (char *) Xaligned_calloc(16, 1, totalmem); 856 857 MV_Voices = (VoiceNode *)ptr; 858 ptr += Voices * sizeof(VoiceNode); 859 Bassert(Voices < MV_MAXVOICES); 860 861 MV_MaxVoices = Voices; 862 863 LL::Reset((VoiceNode*) &VoiceList); 864 LL::Reset((VoiceNode*) &VoicePool); 865 866 for (int index = 0; index < Voices; index++) 867 LL::Insert(&VoicePool, &MV_Voices[index]); 868 869 MV_Handles = (VoiceNode **)Xaligned_calloc(16, Voices, sizeof(intptr_t)); 870 #ifdef ASS_REVERSESTEREO 871 MV_SetReverseStereo(FALSE); 872 #endif 873 874 ASS_PCMSoundDriver = soundcard; 875 876 // Initialize the sound card 877 878 if (SoundDriver_PCM_Init(&MixRate, &numchannels, initdata) != MV_Ok) 879 MV_SetErrorCode(MV_DriverError); 880 881 if (MV_ErrorCode != MV_Ok) 882 { 883 ALIGNED_FREE_AND_NULL(MV_Voices); 884 885 return MV_Error; 886 } 887 888 MV_Installed = TRUE; 889 MV_InitDataPtr = initdata; 890 MV_CallBackFunc = nullptr; 891 MV_ReverbLevel = 0; 892 MV_ReverbVolume = 0.f; 893 894 // Set the sampling rate 895 MV_MixRate = MixRate; 896 897 // Set Mixer to play stereo digitized sound 898 MV_SetMixMode(numchannels); 899 MV_ReverbDelay = MV_BufferSize * 3; 900 901 // Make sure we don't cross a physical page 902 MV_MixBuffer[MV_NumberOfBuffers<<1] = ptr; 903 for (int buffer = 0; buffer < MV_NumberOfBuffers<<1; buffer++) 904 { 905 MV_MixBuffer[buffer] = ptr; 906 ptr += MV_BufferSize; 907 } 908 909 MV_MusicBuffer = ptr; 910 911 // Calculate pan table 912 MV_CalcPanTable(); 913 914 MV_VolumeSmoothFactor = fix16_from_float(1.f-powf(0.1f, 30.f/MixRate)); 915 916 // Start the playback engine 917 if (MV_StartPlayback() != MV_Ok) 918 { 919 // Preserve error code while we shutdown. 920 int status = MV_ErrorCode; 921 MV_Shutdown(); 922 return MV_SetErrorCode(status); 923 } 924 925 return MV_Ok; 926 } 927 928 int MV_Shutdown(void) 929 { 930 if (!MV_Installed) 931 return MV_Ok; 932 933 MV_KillAllVoices(); 934 935 MV_Installed = FALSE; 936 937 // Stop the sound playback engine 938 MV_StopPlayback(); 939 940 // Shutdown the sound card 941 SoundDriver_PCM_Shutdown(); 942 943 // Free any voices we allocated 944 ALIGNED_FREE_AND_NULL(MV_Voices); 945 946 LL::Reset((VoiceNode*) &VoiceList); 947 LL::Reset((VoiceNode*) &VoicePool); 948 949 ALIGNED_FREE_AND_NULL(MV_Handles); 950 951 MV_MaxVoices = 1; 952 953 // Release the descriptor from our mix buffer 954 for (int buffer = 0; buffer < MV_NUMBEROFBUFFERS<<1; buffer++) 955 MV_MixBuffer[buffer] = nullptr; 956 957 MV_SetErrorCode(MV_NotInstalled); 958 959 return MV_Ok; 960 } 961 962 void MV_HookMusicRoutine(void(*callback)(void)) 963 { 964 MV_Lock(); 965 MV_MusicCallback = callback; 966 MV_Unlock(); 967 } 968 969 void MV_UnhookMusicRoutine(void) 970 { 971 if (MV_MusicCallback) 972 { 973 MV_Lock(); 974 MV_MusicCallback = nullptr; 975 MV_Unlock(); 976 } 977 } 978 979 MV_MusicRoutineBuffer MV_GetMusicRoutineBuffer() 980 { 981 return MV_MusicRoutineBuffer{ MV_MusicBuffer, MV_BufferSize }; 982 } 983 984 const char *loopStartTags[loopStartTagCount] = { "LOOP_START", "LOOPSTART", "LOOP" }; 985 const char *loopEndTags[loopEndTagCount] = { "LOOP_END", "LOOPEND" }; 986 const char *loopLengthTags[loopLengthTagCount] = { "LOOP_LENGTH", "LOOPLENGTH" }; 987 988 const char *MV_ErrorString(int ErrorNumber) 989 { 990 switch (ErrorNumber) 991 { 992 case MV_Error: 993 return MV_ErrorString(MV_ErrorCode); 994 case MV_Ok: 995 return "Multivoc ok."; 996 case MV_NotInstalled: 997 return "Multivoc not installed."; 998 case MV_DriverError: 999 return SoundDriver_PCM_ErrorString(SoundDriver_PCM_GetError()); 1000 case MV_NoVoices: 1001 return "No free voices available to Multivoc."; 1002 case MV_VoiceNotFound: 1003 return "No voice with matching handle found."; 1004 case MV_InvalidFile: 1005 return "Invalid file passed in to Multivoc."; 1006 default: 1007 return "Unknown Multivoc error code."; 1008 } 1009 } 1010 1011 static playbackstatus MV_GetNextDemandFeedBlock(VoiceNode* voice) 1012 { 1013 if (voice->BlockLength > 0) 1014 { 1015 voice->position -= voice->length; 1016 voice->sound += voice->length >> 16; 1017 voice->length = min(voice->BlockLength, 0x8000u); 1018 voice->BlockLength -= voice->length; 1019 voice->length <<= 16; 1020 1021 return KeepPlaying; 1022 } 1023 1024 if (voice->DemandFeed == NULL) 1025 return NoMoreData; 1026 1027 voice->position = 0; 1028 (voice->DemandFeed)(&voice->sound, &voice->BlockLength, voice->rawdataptr); 1029 voice->length = min(voice->BlockLength, 0x8000u); 1030 voice->BlockLength -= voice->length; 1031 voice->length <<= 16; 1032 1033 if (voice->length > 0 && voice->sound != NULL) 1034 return KeepPlaying; 1035 1036 return NoMoreData; 1037 } 1038 1039 int MV_StartDemandFeedPlayback(void (*function)(const char** ptr, uint32_t* length, void* userdata), int bitdepth, int channels, int rate, 1040 int pitchoffset, int vol, int left, int right, int priority, fix16_t volume, intptr_t callbackval, void* userdata) 1041 { 1042 if (!MV_Installed) 1043 return MV_SetErrorCode(MV_NotInstalled); 1044 1045 // Request a voice from the voice pool 1046 auto voice = MV_AllocVoice(priority); 1047 if (voice == nullptr) 1048 return MV_SetErrorCode(MV_NoVoices); 1049 1050 // voice->wavetype = FMT_DEMANDFED; 1051 voice->bits = bitdepth; 1052 voice->channels = channels; 1053 voice->GetSound = MV_GetNextDemandFeedBlock; 1054 voice->DemandFeed = function; 1055 voice->position = 0; 1056 voice->sound = nullptr; 1057 voice->length = 0; 1058 voice->priority = priority; 1059 voice->callbackval = callbackval; 1060 voice->rawdataptr = userdata; 1061 1062 voice->Loop = {}; 1063 1064 MV_SetVoicePitch(voice, rate, pitchoffset); 1065 MV_SetVoiceMixMode(voice); 1066 MV_SetVoiceVolume(voice, vol, left, right, volume); 1067 MV_PlayVoice(voice); 1068 1069 return voice->handle; 1070 } 1071 1072 int MV_StartDemandFeedPlayback3D(void (*function)(const char** ptr, uint32_t* length, void* userdata), int bitdepth, int channels, int rate, 1073 int pitchoffset, int angle, int distance, int priority, fix16_t volume, intptr_t callbackval, void* userdata) 1074 { 1075 if (!MV_Installed) 1076 return MV_SetErrorCode(MV_NotInstalled); 1077 1078 if (distance < 0) 1079 { 1080 distance = -distance; 1081 angle += MV_NUMPANPOSITIONS / 2; 1082 } 1083 1084 int const vol = MIX_VOLUME(distance); 1085 1086 // Ensure angle is within 0 - 127 1087 angle &= MV_MAXPANPOSITION; 1088 1089 return MV_StartDemandFeedPlayback(function, bitdepth, channels, rate, pitchoffset, max(0, 255 - distance), 1090 MV_PanTable[ angle ][ vol ].left, MV_PanTable[ angle ][ vol ].right, priority, volume, callbackval, userdata); 1091 }