1 //-------------------------------------------------------------------------
2 /*
3 Copyright (C) 2010-2019 EDuke32 developers and contributors
4 Copyright (C) 2019 Nuke.YKT
5 
6 This file is part of NBlood.
7 
8 NBlood is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License version 2
10 as published by the Free Software Foundation.
11 
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 
16 See the GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 */
22 //-------------------------------------------------------------------------
23 #include "build.h"
24 #include "compat.h"
25 #include "music.h"
26 #include "fx_man.h"
27 #include "al_midi.h"
28 #include "common_game.h"
29 #include "config.h"
30 #include "levels.h"
31 #include "resource.h"
32 #include "sound.h"
33 #include "renderlayer.h"
34 
35 
36 int32_t SoundToggle;
37 int32_t MusicToggle;
38 int32_t CDAudioToggle;
39 int32_t FXVolume;
40 int32_t MusicVolume;
41 int32_t CDVolume;
42 int32_t NumVoices;
43 int32_t NumChannels;
44 int32_t NumBits;
45 int32_t MixRate;
46 #ifdef ASS_REVERSESTEREO
47 int32_t ReverseStereo;
48 #endif
49 int32_t MusicDevice;
50 
51 Resource gSoundRes;
52 
53 int soundRates[13] = {
54     11025,
55     11025,
56     11025,
57     11025,
58     11025,
59     22050,
60     22050,
61     22050,
62     22050,
63     44100,
64     44100,
65     44100,
66     44100,
67 };
68 #define kChannelMax 32
69 
sndGetRate(int format)70 int sndGetRate(int format)
71 {
72     if (format < 13)
73         return soundRates[format];
74     return 11025;
75 }
76 
77 SAMPLE2D Channel[kChannelMax];
78 
FindChannel(void)79 SAMPLE2D * FindChannel(void)
80 {
81     for (int i = kChannelMax - 1; i >= 0; i--)
82         if (Channel[i].at5 == 0) return &Channel[i];
83     consoleSysMsg("No free channel available for sample");
84     //ThrowError("No free channel available for sample");
85     return NULL;
86 }
87 
88 DICTNODE *hSong;
89 char *pSongPtr;
90 int nSongSize;
91 bool bWaveMusic;
92 int nWaveMusicHandle;
93 
sndPlaySong(const char * songName,bool bLoop)94 int sndPlaySong(const char *songName, bool bLoop)
95 {
96     if (!MusicToggle)
97         return 0;
98     if (!songName || strlen(songName) == 0)
99         return 1;
100 
101     int32_t fp = S_OpenAudio(songName, 0, 1);
102     if (EDUKE32_PREDICT_FALSE(fp < 0))
103     {
104         hSong = gSoundRes.Lookup(songName, "MID");
105         if (!hSong)
106         {
107             OSD_Printf(OSD_ERROR "sndPlaySong(): error: can't open \"%s\" for playback!\n", songName);
108             return 2;
109         }
110         int nNewSongSize = hSong->size;
111         char *pNewSongPtr = (char *)Xaligned_alloc(16, nNewSongSize);
112         gSoundRes.Load(hSong, pNewSongPtr);
113         MUSIC_SetVolume(MusicVolume);
114         int32_t retval = MUSIC_PlaySong(pNewSongPtr, nNewSongSize, bLoop);
115 
116         if (retval != MUSIC_Ok)
117         {
118             ALIGNED_FREE_AND_NULL(pNewSongPtr);
119             return 5;
120         }
121 
122         if (bWaveMusic && nWaveMusicHandle >= 0)
123         {
124             FX_StopSound(nWaveMusicHandle);
125             nWaveMusicHandle = -1;
126         }
127 
128         bWaveMusic = false;
129         ALIGNED_FREE_AND_NULL(pSongPtr);
130         pSongPtr = pNewSongPtr;
131         nSongSize = nNewSongSize;
132         return 0;
133     }
134 
135     int32_t nSongLen = kfilelength(fp);
136 
137     if (EDUKE32_PREDICT_FALSE(nSongLen < 4))
138     {
139         OSD_Printf(OSD_ERROR "sndPlaySong(): error: empty music file \"%s\"\n", songName);
140         kclose(fp);
141         return 3;
142     }
143 
144     char * pNewSongPtr = (char *)Xaligned_alloc(16, nSongLen);
145     int nNewSongSize = kread(fp, pNewSongPtr, nSongLen);
146 
147     if (EDUKE32_PREDICT_FALSE(nNewSongSize != nSongLen))
148     {
149         OSD_Printf(OSD_ERROR "sndPlaySong(): error: read %d bytes from \"%s\", expected %d\n",
150             nNewSongSize, songName, nSongLen);
151         kclose(fp);
152         ALIGNED_FREE_AND_NULL(pNewSongPtr);
153         return 4;
154     }
155 
156     kclose(fp);
157 
158     if (!Bmemcmp(pNewSongPtr, "MThd", 4))
159     {
160         int32_t retval = MUSIC_PlaySong(pNewSongPtr, nNewSongSize, bLoop);
161 
162         if (retval != MUSIC_Ok)
163         {
164             ALIGNED_FREE_AND_NULL(pNewSongPtr);
165             return 5;
166         }
167 
168         if (bWaveMusic && nWaveMusicHandle >= 0)
169         {
170             FX_StopSound(nWaveMusicHandle);
171             nWaveMusicHandle = -1;
172         }
173 
174         bWaveMusic = false;
175         ALIGNED_FREE_AND_NULL(pSongPtr);
176         pSongPtr = pNewSongPtr;
177         nSongSize = nNewSongSize;
178     }
179     else
180     {
181         int nNewWaveMusicHandle = FX_Play(pNewSongPtr, bLoop ? nNewSongSize : -1, 0, 0, 0, MusicVolume, MusicVolume, MusicVolume,
182                                    FX_MUSIC_PRIORITY, fix16_one, (intptr_t)&nWaveMusicHandle);
183 
184         if (nNewWaveMusicHandle <= FX_Ok)
185         {
186             ALIGNED_FREE_AND_NULL(pNewSongPtr);
187             return 5;
188         }
189 
190         if (bWaveMusic && nWaveMusicHandle >= 0)
191             FX_StopSound(nWaveMusicHandle);
192 
193         MUSIC_StopSong();
194 
195         nWaveMusicHandle = nNewWaveMusicHandle;
196         bWaveMusic = true;
197         ALIGNED_FREE_AND_NULL(pSongPtr);
198         pSongPtr = pNewSongPtr;
199         nSongSize = nNewSongSize;
200     }
201 
202     return 0;
203 }
204 
sndIsSongPlaying(void)205 bool sndIsSongPlaying(void)
206 {
207     //return MUSIC_SongPlaying();
208     return false;
209 }
210 
sndFadeSong(int nTime)211 void sndFadeSong(int nTime)
212 {
213     UNREFERENCED_PARAMETER(nTime);
214     // NUKE-TODO:
215     //if (MusicDevice == -1)
216     //    return;
217     //if (gEightyTwoFifty && sndMultiPlayer)
218     //    return;
219     //MUSIC_FadeVolume(0, nTime);
220     if (bWaveMusic && nWaveMusicHandle >= 0)
221     {
222         FX_StopSound(nWaveMusicHandle);
223         nWaveMusicHandle = -1;
224         bWaveMusic = false;
225     }
226     // MUSIC_SetVolume(0);
227     MUSIC_StopSong();
228 }
229 
sndSetMusicVolume(int nVolume)230 void sndSetMusicVolume(int nVolume)
231 {
232     MusicVolume = nVolume;
233     if (bWaveMusic && nWaveMusicHandle >= 0)
234         FX_SetPan(nWaveMusicHandle, nVolume, nVolume, nVolume);
235     MUSIC_SetVolume(nVolume);
236 }
237 
sndSetFXVolume(int nVolume)238 void sndSetFXVolume(int nVolume)
239 {
240     FXVolume = nVolume;
241     FX_SetVolume(nVolume);
242 }
243 
sndStopSong(void)244 void sndStopSong(void)
245 {
246     if (bWaveMusic && nWaveMusicHandle >= 0)
247     {
248         FX_StopSound(nWaveMusicHandle);
249         nWaveMusicHandle = -1;
250         bWaveMusic = false;
251     }
252 
253     MUSIC_StopSong();
254 
255     ALIGNED_FREE_AND_NULL(pSongPtr);
256     nSongSize = 0;
257 }
258 
SoundCallback(intptr_t val)259 void SoundCallback(intptr_t val)
260 {
261     int *phVoice = (int*)val;
262     *phVoice = 0;
263 }
264 
265 void sndKillSound(SAMPLE2D *pChannel);
266 
sndStartSample(const char * pzSound,int nVolume,int nChannel)267 void sndStartSample(const char *pzSound, int nVolume, int nChannel)
268 {
269     if (!SoundToggle)
270         return;
271     if (!strlen(pzSound))
272         return;
273     dassert(nChannel >= -1 && nChannel < kChannelMax);
274     SAMPLE2D *pChannel;
275     if (nChannel == -1)
276         pChannel = FindChannel();
277     else
278         pChannel = &Channel[nChannel];
279     if (pChannel->hVoice > 0)
280         sndKillSound(pChannel);
281     pChannel->at5 = gSoundRes.Lookup(pzSound, "RAW");
282     if (!pChannel->at5)
283         return;
284     int nSize = pChannel->at5->size;
285     char *pData = (char*)gSoundRes.Lock(pChannel->at5);
286     pChannel->hVoice = FX_PlayRaw(pData, nSize, sndGetRate(1), 0, nVolume, nVolume, nVolume, nVolume, fix16_one, (intptr_t)&pChannel->hVoice);
287 }
288 
sndStartSample(unsigned int nSound,int nVolume,int nChannel,bool bLoop)289 void sndStartSample(unsigned int nSound, int nVolume, int nChannel, bool bLoop)
290 {
291     if (!SoundToggle)
292         return;
293     dassert(nChannel >= -1 && nChannel < kChannelMax);
294     DICTNODE *hSfx = gSoundRes.Lookup(nSound, "SFX");
295     if (!hSfx)
296         return;
297     SFX *pEffect = (SFX*)gSoundRes.Lock(hSfx);
298     dassert(pEffect != NULL);
299     SAMPLE2D *pChannel;
300     if (nChannel == -1)
301         pChannel = FindChannel();
302     else
303         pChannel = &Channel[nChannel];
304     if (pChannel->hVoice > 0)
305         sndKillSound(pChannel);
306     pChannel->at5 = gSoundRes.Lookup(pEffect->rawName, "RAW");
307     if (!pChannel->at5)
308         return;
309     if (nVolume < 0)
310         nVolume = pEffect->relVol;
311     nVolume *= 80;
312     nVolume = clamp(nVolume, 0, 255); // clamp to range that audiolib accepts
313     int nSize = pChannel->at5->size;
314     int nLoopEnd = nSize - 1;
315     if (nLoopEnd < 0)
316         nLoopEnd = 0;
317     if (nSize <= 0)
318         return;
319     char *pData = (char*)gSoundRes.Lock(pChannel->at5);
320     if (nChannel < 0)
321         bLoop = false;
322     if (bLoop)
323     {
324         pChannel->hVoice = FX_PlayLoopedRaw(pData, nSize, pData + pEffect->loopStart, pData + nLoopEnd, sndGetRate(pEffect->format),
325             0, nVolume, nVolume, nVolume, nVolume, fix16_one, (intptr_t)&pChannel->hVoice);
326         pChannel->at4 |= 1;
327     }
328     else
329     {
330         pChannel->hVoice = FX_PlayRaw(pData, nSize, sndGetRate(pEffect->format), 0, nVolume, nVolume, nVolume, nVolume, fix16_one, (intptr_t)&pChannel->hVoice);
331         pChannel->at4 &= ~1;
332     }
333 }
334 
sndStartWavID(unsigned int nSound,int nVolume,int nChannel)335 void sndStartWavID(unsigned int nSound, int nVolume, int nChannel)
336 {
337     if (!SoundToggle)
338         return;
339     dassert(nChannel >= -1 && nChannel < kChannelMax);
340     SAMPLE2D *pChannel;
341     if (nChannel == -1)
342         pChannel = FindChannel();
343     else
344         pChannel = &Channel[nChannel];
345     if (pChannel->hVoice > 0)
346         sndKillSound(pChannel);
347     pChannel->at5 = gSoundRes.Lookup(nSound, "WAV");
348     if (!pChannel->at5)
349         return;
350     char *pData = (char*)gSoundRes.Lock(pChannel->at5);
351     pChannel->hVoice = FX_Play(pData, pChannel->at5->size, 0, -1, 0, nVolume, nVolume, nVolume, nVolume, fix16_one, (intptr_t)&pChannel->hVoice);
352 }
353 
sndKillSound(SAMPLE2D * pChannel)354 void sndKillSound(SAMPLE2D *pChannel)
355 {
356     if (pChannel->at4 & 1)
357     {
358         FX_EndLooping(pChannel->hVoice);
359         pChannel->at4 &= ~1;
360     }
361     FX_StopSound(pChannel->hVoice);
362 }
363 
sndStartWavDisk(const char * pzFile,int nVolume,int nChannel)364 void sndStartWavDisk(const char *pzFile, int nVolume, int nChannel)
365 {
366     dassert(nChannel >= -1 && nChannel < kChannelMax);
367     SAMPLE2D *pChannel;
368     if (nChannel == -1)
369         pChannel = FindChannel();
370     else
371         pChannel = &Channel[nChannel];
372     if (pChannel->hVoice > 0)
373         sndKillSound(pChannel);
374     int hFile = kopen4loadfrommod(pzFile, 0);
375     if (hFile == -1)
376         return;
377     int nLength = kfilelength(hFile);
378     char *pData = (char*)gSoundRes.Alloc(nLength);
379     if (!pData)
380     {
381         kclose(hFile);
382         return;
383     }
384     kread(hFile, pData, kfilelength(hFile));
385     kclose(hFile);
386     pChannel->at5 = (DICTNODE*)pData;
387     pChannel->at4 |= 2;
388     pChannel->hVoice = FX_Play(pData, nLength, 0, -1, 0, nVolume, nVolume, nVolume, nVolume, fix16_one, (intptr_t)&pChannel->hVoice);
389 }
390 
sndKillAllSounds(void)391 void sndKillAllSounds(void)
392 {
393     for (int i = 0; i < kChannelMax; i++)
394     {
395         SAMPLE2D *pChannel = &Channel[i];
396         if (pChannel->hVoice > 0)
397             sndKillSound(pChannel);
398         if (pChannel->at5)
399         {
400             if (pChannel->at4 & 2)
401             {
402 #if 0
403                 free(pChannel->at5);
404 #else
405                 gSoundRes.Free(pChannel->at5);
406 #endif
407                 pChannel->at4 &= ~2;
408             }
409             else
410             {
411                 gSoundRes.Unlock(pChannel->at5);
412             }
413             pChannel->at5 = 0;
414         }
415     }
416 }
417 
sndProcess(void)418 void sndProcess(void)
419 {
420     for (int i = 0; i < kChannelMax; i++)
421     {
422         if (Channel[i].hVoice <= 0 && Channel[i].at5)
423         {
424             if (Channel[i].at4 & 2)
425             {
426                 gSoundRes.Free(Channel[i].at5);
427                 Channel[i].at4 &= ~2;
428             }
429             else
430             {
431                 gSoundRes.Unlock(Channel[i].at5);
432             }
433             Channel[i].at5 = 0;
434         }
435     }
436 }
437 
InitSoundDevice(void)438 void InitSoundDevice(void)
439 {
440 #ifdef MIXERTYPEWIN
441     void *initdata = (void *)win_gethwnd(); // used for DirectSound
442 #else
443     void *initdata = NULL;
444 #endif
445     int nStatus;
446     nStatus = FX_Init(NumVoices, NumChannels, MixRate, initdata);
447     if (nStatus != 0)
448     {
449         initprintf("InitSoundDevice: %s\n", FX_ErrorString(nStatus));
450         return;
451     }
452 #ifdef ASS_REVERSESTEREO
453     if (ReverseStereo == 1)
454         FX_SetReverseStereo(!FX_GetReverseStereo());
455 #endif
456     FX_SetVolume(FXVolume);
457     FX_SetCallBack(SoundCallback);
458 }
459 
DeinitSoundDevice(void)460 void DeinitSoundDevice(void)
461 {
462     int nStatus = FX_Shutdown();
463     if (nStatus != 0)
464         ThrowError(FX_ErrorString(nStatus));
465 }
466 
InitMusicDevice(void)467 void InitMusicDevice(void)
468 {
469     int nStatus;
470     if ((nStatus = MUSIC_Init(MusicDevice)) == MUSIC_Ok)
471     {
472         if (MusicDevice == ASS_AutoDetect)
473             MusicDevice = MIDI_GetDevice();
474     }
475     else if ((nStatus = MUSIC_Init(ASS_AutoDetect)) == MUSIC_Ok)
476     {
477         initprintf("InitMusicDevice: %s\n", MUSIC_ErrorString(nStatus));
478         return;
479     }
480     DICTNODE *hTmb = gSoundRes.Lookup("GMTIMBRE", "TMB");
481     if (hTmb)
482         AL_RegisterTimbreBank((unsigned char*)gSoundRes.Load(hTmb));
483     MUSIC_SetVolume(MusicVolume);
484 }
485 
DeinitMusicDevice(void)486 void DeinitMusicDevice(void)
487 {
488     FX_StopAllSounds();
489     int nStatus = MUSIC_Shutdown();
490     if (nStatus != 0)
491         ThrowError(MUSIC_ErrorString(nStatus));
492 }
493 
494 bool sndActive = false;
495 
sndTerm(void)496 void sndTerm(void)
497 {
498     if (!sndActive)
499         return;
500     sndActive = false;
501     sndStopSong();
502     DeinitSoundDevice();
503     DeinitMusicDevice();
504 }
505 extern char *pUserSoundRFF;
sndInit(void)506 void sndInit(void)
507 {
508     memset(Channel, 0, sizeof(Channel));
509     pSongPtr = NULL;
510     nSongSize = 0;
511     bWaveMusic = false;
512     nWaveMusicHandle = -1;
513     InitSoundDevice();
514     InitMusicDevice();
515     sndActive = true;
516 }
517