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