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