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