1 /** @file audiosystem.cpp Audio subsystem.
2 *
3 * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4 * @authors Copyright © 2005-2015 Daniel Swanson <danij@dengine.net>
5 * @authors Copyright © 2006-2007 Jamie Jones <jamie_jones_au@yahoo.com.au> *
6 *
7 * @par License
8 * GPL: http://www.gnu.org/licenses/gpl.html
9 *
10 * <small>This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2 of the License, or (at your
13 * option) any later version. This program is distributed in the hope that it
14 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
16 * Public License for more details. You should have received a copy of the GNU
17 * General Public License along with this program; if not, see:
18 * http://www.gnu.org/licenses</small>
19 */
20
21 #define DENG_NO_API_MACROS_SOUND
22
23 #include "audio/audiosystem.h"
24
25 #include "dd_share.h" // SF_* flags
26 #include "dd_main.h" // ::isDedicated
27 #include "def_main.h" // ::defs
28 #include "api_map.h"
29 #include "world/p_players.h"
30 #include "world/thinkers.h"
31 #include "audio/s_cache.h"
32 #include "Sector"
33 #include "Subsector"
34
35 #ifdef __CLIENT__
36 # include "sys_system.h" // Sys_Sleep()
37 # include "audio/m_mus2midi.h"
38 # include "audio/sfxchannel.h"
39 # include "audio/sys_audiod_dummy.h"
40 # include "world/audioenvironment.h"
41 # include "client/clientsubsector.h"
42 # include <doomsday/defs/music.h>
43 # include <doomsday/filesys/fs_main.h>
44 # include <doomsday/filesys/fs_util.h>
45 #endif
46
47 #ifdef __SERVER__
48 # include "server/sv_sound.h"
49 #endif
50
51 #include <doomsday/console/cmd.h>
52 #include <doomsday/console/var.h>
53 #include <de/App>
54 #include <de/Binder>
55 #include <de/Config>
56 #include <de/CommandLine>
57 #include <de/FileSystem>
58 #include <de/LogBuffer>
59 #include <de/NativeFile>
60 #include <de/ScriptSystem>
61 #include <de/timer.h>
62 #include <de/c_wrapper.h>
63 #include <de/concurrency.h>
64 #include <de/memory.h>
65
66 #include <QMultiHash>
67 #include <QtAlgorithms>
68
69 using namespace de;
70
71 dint soundMinDist = 256; // No distance attenuation this close.
72 dint soundMaxDist = 2025;
73
74 // Setting these variables is enough to adjust the volumes.
75 // S_StartFrame() will call the actual routines to change the volume
76 // when there are changes.
77 dint sfxVolume = 255 * 2/3;
78 dint musVolume = 255 * 2/3;
79
80 dint sfxBits = 8;
81 dint sfxRate = 11025;
82
83 #ifdef __CLIENT__
84 # if defined(MACOSX) && defined(MACOS_HAVE_QTKIT)
85 /// Built-in QuickTime audio interface implemented by MusicPlayer.m
86 DENG_EXTERN_C audiointerface_music_t audiodQuickTimeMusic;
87 # endif
88 #endif
89
90 static AudioSystem *theAudioSystem;
91
92 static duint const SOUND_LOGICAL_PURGEINTERVAL = 2000; ///< 2 seconds
93
94 static byte sfxOneSoundPerEmitter; //< @c false= Traditional Doomsday behavior: allow sounds to overlap.
95
96 #ifdef __CLIENT__
97 static dint const SOUND_CHANNEL_COUNT_DEFAULT = 16;
98 static dint const SOUND_CHANNEL_COUNT_MAX = 256;
99 static dint const SOUND_CHANNEL_2DCOUNT = 4;
100 static char const *MUSIC_BUFFEREDFILE = "/tmp/dd-buffered-song";
101
102 static thread_t refreshHandle;
103 static volatile bool allowRefresh, refreshing;
104
105 static bool sfxNoRndPitch; ///< @todo should be a cvar.
106
107 // Console variables:
108 //static dint sfx16Bit;
109 //static dint sfxSampleRate = 11025;
110 static dint sfx3D;
111 static dfloat sfxReverbStrength = 0.5f;
112 static char *musMidiFontPath = (char *) "";
113 // When multiple sources are available this setting determines which to use (mus < ext < cd).
114 static AudioSystem::MusicSource musSourcePreference = AudioSystem::MUSP_EXT;
115
116 static String const driverIdentifier[AUDIODRIVER_COUNT] = {
117 "dummy",
118 "sdlmixer",
119 "openal",
120 "fmod",
121 "fluidsynth",
122 "dsound",
123 "winmm"
124 };
125
identifierToDriverId(String name)126 static audiodriverid_t identifierToDriverId(String name)
127 {
128 for (dint i = 0; i < AUDIODRIVER_COUNT; ++i)
129 {
130 if (!driverIdentifier[i].compareWithoutCase(name))
131 return audiodriverid_t(i);
132 }
133 LOG_AUDIO_ERROR("'%s' is not a valid audio driver name") << name;
134 return AUDIOD_INVALID;
135 }
136
137 /**
138 * This is a high-priority thread that periodically checks if the channels need
139 * to be updated with more data. The thread terminates when it notices that the
140 * channels have been destroyed. The Sfx audio driver maintains a 250ms buffer
141 * for each channel, which means the refresh must be done often enough to keep
142 * them filled.
143 *
144 * @todo Use a real mutex, will you?
145 */
sfxChannelRefreshThread(void *)146 static dint C_DECL sfxChannelRefreshThread(void *)
147 {
148 // We'll continue looping until the Sfx module is shut down.
149 while (App_AudioSystem().sfxIsAvailable() && App_AudioSystem().hasSfxChannels())
150 {
151 // The bit is swapped on each refresh (debug info).
152 ::refMonitor ^= 1;
153
154 if (allowRefresh)
155 {
156 // Do the refresh.
157 refreshing = true;
158 App_AudioSystem().sfxChannels().refreshAll();
159 refreshing = false;
160
161 // Let's take a nap.
162 Sys_Sleep(200);
163 }
164 else
165 {
166 // Refreshing is not allowed, so take a shorter nap while
167 // waiting for allowRefresh.
168 Sys_Sleep(150);
169 }
170 }
171
172 // Time to end this thread.
173 return 0;
174 }
175
176 /**
177 * Returns @c true if the given @a file appears to contain MUS format music.
178 */
recognizeMus(de::File1 & file)179 static bool recognizeMus(de::File1 &file)
180 {
181 char buf[4];
182 file.read((uint8_t *)buf, 0, 4);
183
184 // ASCII "MUS" and CTRL-Z (hex 4d 55 53 1a)
185 return !qstrncmp(buf, "MUS\x01a", 4);
186 }
187
188 static Value *Function_Audio_LocalSound(Context &, const Function::ArgumentValues &args);
189
190 #endif // __CLIENT__
191
DENG2_PIMPL(AudioSystem)192 DENG2_PIMPL(AudioSystem)
193 , DENG2_OBSERVES(DoomsdayApp, GameUnload)
194 #ifdef __CLIENT__
195 , DENG2_OBSERVES(audio::SfxSampleCache, SampleRemove)
196 #endif
197 {
198 Record module;
199 Binder binder;
200
201 #ifdef __CLIENT__
202 AudioDriver drivers[AUDIODRIVER_COUNT];
203
204 AudioDriver &driverById(audiodriverid_t id)
205 {
206 DENG2_ASSERT(VALID_AUDIODRIVER_IDENTIFIER(id));
207 return drivers[id];
208 }
209
210 /**
211 * Chooses the default audio driver based on configuration options.
212 */
213 audiodriverid_t chooseAudioDriver()
214 {
215 CommandLine &cmdLine = CommandLine::get();
216
217 // No audio output?
218 if (::isDedicated)
219 return AUDIOD_DUMMY;
220
221 if (cmdLine.has("-dummy"))
222 return AUDIOD_DUMMY;
223
224 if (cmdLine.has("-fmod"))
225 return AUDIOD_FMOD;
226
227 if (cmdLine.has("-oal") || cmdLine.has("-openal"))
228 return AUDIOD_OPENAL;
229
230 #ifdef WIN32
231 if (cmdLine.has("-dsound"))
232 return AUDIOD_DSOUND;
233
234 if (cmdLine.has("-winmm"))
235 return AUDIOD_WINMM;
236 #endif
237
238 #ifndef DENG_DISABLE_SDLMIXER
239 if (cmdLine.has("-sdlmixer"))
240 return AUDIOD_SDL_MIXER;
241
242 // FMOD is preferred, but SDL_mixer is also a fallback.
243 if (!AudioDriver::isAvailable(driverIdentifier[AUDIOD_FMOD]))
244 return AUDIOD_SDL_MIXER;
245 #endif
246 // The default audio driver.
247 return AUDIOD_FMOD;
248 }
249
250 /**
251 * Initializes the audio driver interfaces.
252 *
253 * @return @c true iff successful.
254 */
255 bool initDriver(audiodriverid_t driverId)
256 {
257 LOG_AS("AudioSystem");
258 try
259 {
260 String const idStr = driverIdentifier[driverId];
261 if (!AudioDriver::isAvailable(idStr))
262 {
263 return false;
264 }
265
266 AudioDriver &driver = driverById(driverId);
267 switch (driverId)
268 {
269 case AUDIOD_DUMMY:
270 case AUDIOD_OPENAL:
271 case AUDIOD_FMOD:
272 case AUDIOD_FLUIDSYNTH:
273 driver.load(idStr);
274 break;
275 #ifndef DENG_DISABLE_SDLMIXER
276 case AUDIOD_SDL_MIXER:
277 driver.load(idStr);
278 break;
279 #endif
280 #ifdef WIN32
281 case AUDIOD_DSOUND:
282 case AUDIOD_WINMM:
283 driver.load(idStr);
284 break;
285 #endif
286 default:
287 return false;
288 }
289
290 // All loaded drivers are automatically initialized so they are ready for use.
291 driver.initialize();
292 return driver.isInitialized();
293 }
294 catch (AudioDriver::LoadError const &er)
295 {
296 LOG_AUDIO_WARNING("Failed initializing driver \"%s\":\n")
297 << AudioDriver_GetName(driverId)
298 << er.asText();
299 }
300 return false;
301 }
302
303 audiodriverid_t initDriverIfNeeded(String const &identifier)
304 {
305 audiodriverid_t id = identifierToDriverId(identifier);
306 AudioDriver &driver = driverById(id);
307 if (!driver.isInitialized())
308 {
309 if (!initDriver(id))
310 {
311 return AUDIOD_DUMMY;
312 }
313 }
314 return id;
315 }
316
317 bool loadDrivers()
318 {
319 activeInterfaces.clear();
320
321 // The audio drivers may use Audio.outputs to declare which outputs are available.
322 module.set("outputs", new DictionaryValue);
323
324 if (CommandLine::get().has("-nosound"))
325 return false;
326
327 audiodriverid_t defaultDriverId = chooseAudioDriver();
328 initDriver(defaultDriverId);
329
330 /*#ifndef DENG_DISABLE_SDLMIXER
331 // Fallback option for the default driver.
332 if (!ok)
333 {
334 defaultDriverId = AUDIOD_SDL_MIXER;
335 ok = initDriver(defaultDriverId);
336 }
337 #endif*/
338
339 // Choose the interfaces to use.
340 selectInterfaces(defaultDriverId);
341
342 return !activeInterfaces.isEmpty();
343 }
344
345 void unloadDrivers()
346 {
347 // Deinitialize all loaded drivers. (Note: reverse order)
348 for (dint i = AUDIODRIVER_COUNT; i--> 0; )
349 {
350 drivers[i].deinitialize();
351 }
352
353 module.set("outputs", new DictionaryValue);
354
355 // Unload the plugins after everything has been shut down.
356 for (AudioDriver &driver : drivers)
357 {
358 driver.unload();
359 }
360
361 // No more interfaces available.
362 activeInterfaces.clear();
363 }
364
365 /**
366 * The active/loaded interfaces.
367 *
368 * @todo The audio interface could also declare which audio formats it is capable
369 * of playing (e.g., MIDI only, CD tracks only).
370 */
371 struct AudioInterface
372 {
373 audiointerfacetype_t type;
374 union {
375 void *any;
376 audiointerface_sfx_t *sfx;
377 audiointerface_music_t *music;
378 audiointerface_cd_t *cd;
379 } i;
380
381 bool isDummy() const
382 {
383 switch (type)
384 {
385 case AUDIO_ISFX:
386 return !std::memcmp(i.sfx, &audiod_dummy_sfx, sizeof(*i.sfx));
387
388 case AUDIO_IMUSIC:
389 return !std::memcmp(i.music, &audiod_dummy_music, sizeof(*i.music));
390
391 case AUDIO_ICD:
392 return !std::memcmp(i.cd, &audiod_dummy_cd, sizeof(*i.cd));
393
394 default:
395 break;
396 }
397 return false;
398 }
399 };
400 QList<AudioInterface> activeInterfaces;
401
402 bool isPrimaryInterface(audiointerfacetype_t type, void *ptr)
403 {
404 for (int i = activeInterfaces.size() - 1; i >= 0; --i)
405 {
406 auto const &intf = activeInterfaces.at(i);
407 if (intf.type != type) continue;
408 return intf.i.any == ptr;
409 }
410 return false;
411 }
412
413 void addPrimaryInterface(audiointerfacetype_t type, void *ptr)
414 {
415 if (!isPrimaryInterface(type, ptr)) // check if already there
416 {
417 AudioInterface ifs; zap(ifs);
418 ifs.type = type;
419 ifs.i.any = ptr;
420
421 if (ifs.isDummy())
422 {
423 // A dummy interface as the primary one removes the need to have any
424 // other interfaces of the same type.
425 for (int i = activeInterfaces.size() - 1; i >= 0; --i)
426 {
427 if (activeInterfaces[i].type == type)
428 {
429 activeInterfaces.removeAt(i);
430 }
431 }
432 }
433
434 activeInterfaces << ifs; // a copy is made
435 }
436 }
437
438 /**
439 * Choose the SFX, Music, and CD audio interfaces to use.
440 *
441 * @param defaultDriverId Default audio driver to use unless overridden.
442 */
443 void selectInterfaces(audiodriverid_t defaultDriverId)
444 {
445 AudioDriver &defaultDriver = driverById(defaultDriverId);
446
447 // The default driver goes on the bottom of the stack.
448 if (defaultDriver.hasSfx())
449 {
450 AudioInterface ifs; zap(ifs);
451 ifs.type = AUDIO_ISFX;
452 ifs.i.any = &defaultDriver.iSfx();
453 activeInterfaces << ifs; // a copy is made
454 }
455
456 if (defaultDriver.hasMusic())
457 {
458 AudioInterface ifs; zap(ifs);
459 ifs.type = AUDIO_IMUSIC;
460 ifs.i.any = &defaultDriver.iMusic();
461 activeInterfaces << ifs; // a copy is made
462 }
463 #if defined(MACOSX) && defined(MACOS_HAVE_QTKIT)
464 else if (defaultDriverId != AUDIOD_DUMMY)
465 {
466 // On the Mac, use the built-in QuickTime interface as the fallback for music.
467 AudioInterface ifs; zap(ifs);
468 ifs.type = AUDIO_IMUSIC;
469 ifs.i.any = &audiodQuickTimeMusic;
470 activeInterfaces << ifs; // a copy is made
471 }
472 #endif
473
474 if (defaultDriver.hasCd())
475 {
476 AudioInterface ifs; zap(ifs);
477 ifs.type = AUDIO_ICD;
478 ifs.i.any = &defaultDriver.iCd();
479 activeInterfaces << ifs; // a copy is made
480 }
481
482 String userSfx = Config::get("audio.soundPlugin");
483 String userMusic = Config::get("audio.musicPlugin");
484 String userCD = Config::get("audio.cdPlugin");
485
486 // Command line options may also be used to specify which plugin to use.
487 CommandLine &cmdLine = CommandLine::get();
488 if (auto arg = cmdLine.check("-isfx", 1))
489 {
490 userSfx = arg.params.at(0);
491 }
492 if (auto arg = cmdLine.check("-imusic", 1))
493 {
494 userMusic = arg.params.at(0);
495 }
496 if (auto arg = cmdLine.check("-icd", 1))
497 {
498 userCD = arg.params.at(0);
499 }
500
501 // Activate the user's preferred interfaces.
502 {
503 AudioDriver &driver = driverById(initDriverIfNeeded(userSfx));
504 if (driver.hasSfx())
505 {
506 addPrimaryInterface(AUDIO_ISFX, &driver.iSfx());
507 }
508 }
509 {
510 AudioDriver &driver = driverById(initDriverIfNeeded(userMusic));
511 if (driver.hasMusic())
512 {
513 addPrimaryInterface(AUDIO_IMUSIC, &driver.iMusic());
514 }
515 }
516 {
517 AudioDriver &driver = driverById(initDriverIfNeeded(userCD));
518 if (driver.hasCd())
519 {
520 addPrimaryInterface(AUDIO_ICD, &driver.iCd());
521 }
522 }
523
524 // Let the music driver(s) know of the primary sfx interface, in case they
525 // want to play audio through it.
526 setMusicProperty(AUDIOP_SFX_INTERFACE, self().sfx());
527 }
528
529 /**
530 * Iterate through the active interfaces of a given type, in descending priority
531 * order: the most important interface is visited first.
532 *
533 * @param type Type of interface to process.
534 * @param func Callback to make for each interface.
535 */
536 LoopResult forAllInterfaces(audiointerfacetype_t type, std::function<LoopResult (void *)> func) const
537 {
538 if (type != AUDIO_INONE)
539 {
540 for (dint i = activeInterfaces.count(); i--> 0; )
541 {
542 AudioInterface const &ifs = activeInterfaces[i];
543 if (ifs.type == type ||
544 (type == AUDIO_IMUSIC_OR_ICD && (ifs.type == AUDIO_IMUSIC ||
545 ifs.type == AUDIO_ICD)))
546 {
547 if (auto result = func(ifs.i.any))
548 return result;
549 }
550 }
551 }
552 return LoopContinue;
553 }
554
555 /**
556 * Find the Base interface of the audio driver to which @a anyAudioInterface
557 * belongs.
558 *
559 * @param anyAudioInterface Pointer to a SFX, Music, or CD interface.
560 *
561 * @return Audio interface, or @c nullptr if the none of the loaded drivers match.
562 */
563 audiodriver_t &getBaseInterface(void *anyAudioInterface) const
564 {
565 if (anyAudioInterface)
566 {
567 for (AudioDriver const &driver : drivers)
568 {
569 if ((void *)&driver.iSfx() == anyAudioInterface ||
570 (void *)&driver.iMusic() == anyAudioInterface ||
571 (void *)&driver.iCd() == anyAudioInterface)
572 {
573 return driver.iBase();
574 }
575 }
576 }
577 throw Error("audio::System::getBaseInterface", "Unknown audio interface");
578 }
579
580 audiointerfacetype_t interfaceType(void *anyAudioInterface) const
581 {
582 if (anyAudioInterface)
583 {
584 for (AudioDriver const &driver : drivers)
585 {
586 if ((void *)&driver.iSfx() == anyAudioInterface) return AUDIO_ISFX;
587 if ((void *)&driver.iMusic() == anyAudioInterface) return AUDIO_IMUSIC;
588 if ((void *)&driver.iCd() == anyAudioInterface) return AUDIO_ICD;
589 }
590 }
591 return AUDIO_INONE;
592 }
593
594 String interfaceName(void *anyAudioInterface) const
595 {
596 if (anyAudioInterface)
597 {
598 for (AudioDriver const &driver : drivers)
599 {
600 String const name = driver.interfaceName(anyAudioInterface);
601 if (!name.isEmpty()) return name;
602 }
603 }
604 return "(invalid)";
605 }
606 #endif // __CLIENT__
607
608 /**
609 * LogicSounds are used to track currently playing sounds on a logical
610 * level (irrespective of whether playback is available, or if the sounds
611 * are actually audible to anyone).
612 *
613 * @todo The premise behind this functionality is fundamentally flawed in
614 * that it assumes that the same samples are used by both the Client and
615 * the Server, and that the later schedules remote playback of the former
616 * (determined by examining sample lengths on Server side).
617 *
618 * Furthermore, the Server should not be dictating 'oneSoundPerEmitter'
619 * policy so that Clients can be configured independently.
620 */
621 struct LogicSound
622 {
623 //dint soundId = 0;
624 mobj_t const *emitter = nullptr;
625 duint endTime = 0;
626 bool isRepeating = false;
627
628 bool inline isPlaying(duint nowTime) const {
629 return (isRepeating || endTime > nowTime);
630 }
631 };
632 typedef QMultiHash<dint /*key: soundId*/, LogicSound *> LogicSoundHash;
633 typedef QMutableHashIterator<dint /*key: soundId*/, LogicSound *> MutableLogicSoundHashIterator;
634
635 #if __CLIENT__
636 bool musAvail = false; ///< @c true if at least one driver is initialized for music playback.
637 bool musNeedBufFileSwitch = false; ///< @c true= choose a new file name for the buffered playback file when asked. */
638 String musCurrentSong;
639 bool musPaused = false;
640
641 bool sfxAvail = false; ///< @c true if a sound driver is initialized for sound effect playback.
642 mobj_t *sfxListener = nullptr;
643 world::Subsector *sfxListenerSubsector = nullptr;
644 std::unique_ptr<audio::SfxChannels> sfxChannels;
645 #endif
646
647 audio::SfxSampleCache sfxSampleCache; ///< @todo should be __CLIENT__ only.
648 LogicSoundHash sfxLogicHash;
649 duint sfxLogicLastPurge = 0;
650 bool sfxLogicOneSoundPerEmitter = false; ///< set at the start of the frame
651
652 Impl(Public *i) : Base(i)
653 {
654 theAudioSystem = thisPublic;
655
656 // Script bindings.
657 {
658 ScriptSystem::get().addNativeModule("Audio", module);
659 #if defined(__CLIENT__)
660 binder.init(module)
661 << DENG2_FUNC(Audio_LocalSound, "localSound", "id" << "volume");
662 #endif
663 }
664
665 #ifdef __CLIENT__
666 DoomsdayApp::app().audienceForGameUnload() += this;
667 sfxSampleCache.audienceForSampleRemove() += this;
668 #endif
669 }
670
671 ~Impl()
672 {
673 sfxClearLogical();
674 //#ifdef __CLIENT__
675 // sfxSampleCache.audienceForSampleRemove() -= this;
676 // DoomsdayApp::app().audienceForGameUnload() -= this;
677 //#endif
678
679 theAudioSystem = nullptr;
680 }
681
682 #ifdef __CLIENT__
683 String composeMusicBufferFilename(String const &ext = "")
684 {
685 // Switch the name of the buffered song file?
686 static dint currentBufFile = 0;
687 if (musNeedBufFileSwitch)
688 {
689 currentBufFile ^= 1;
690 musNeedBufFileSwitch = false;
691 }
692 // Compose the name.
693 return MUSIC_BUFFEREDFILE + String::number(currentBufFile) + ext;
694 }
695
696 void setMusicProperty(dint prop, void const *ptr)
697 {
698 forAllInterfaces(AUDIO_IMUSIC, [this, &prop, &ptr] (void *ifs)
699 {
700 audiodriver_t &iBase = getBaseInterface(ifs);
701 if (iBase.Set) iBase.Set(prop, ptr);
702 return LoopContinue;
703 });
704
705 if (prop == AUDIOP_SOUNDFONT_FILENAME)
706 {
707 auto *fn = (char const *) ptr;
708 if (!fn || !fn[0]) return; // No path.
709
710 if (F_FileExists(fn))
711 {
712 LOG_AUDIO_MSG("Current soundfont set to: \"%s\"") << fn;
713 }
714 else
715 {
716 LOG_AUDIO_WARNING("Soundfont \"%s\" not found") << fn;
717 }
718 }
719 }
720
721 dint playMusicFile(String const &virtualOrNativePath, bool looped = false)
722 {
723 DENG2_ASSERT(musAvail);
724
725 if (virtualOrNativePath.isEmpty())
726 return 0;
727
728 // Relative paths are relative to the native working directory.
729 String const path = (NativePath::workPath() / NativePath(virtualOrNativePath).expand()).withSeparators('/');
730 LOG_AUDIO_VERBOSE("Attempting to play music file \"%s\"")
731 << NativePath(virtualOrNativePath).pretty();
732
733 try
734 {
735 std::unique_ptr<FileHandle> hndl(&App_FileSystem().openFile(path, "rb"));
736
737 auto didPlay = forAllInterfaces(AUDIO_IMUSIC, [this, &path, &hndl, &looped] (void *ifs)
738 {
739 auto *iMusic = (audiointerface_music_t *) ifs;
740
741 // Does this interface offer buffered playback?
742 if (iMusic->PlayFile)
743 {
744 // Write the data to disk and play from there.
745 File &file = FS::rootFolder().replaceFile(
746 composeMusicBufferFilename(path.fileNameExtension()));
747 Block buf(hndl->length());
748 hndl->read(buf.data(), buf.size());
749 file << buf;
750 file.flush();
751 return iMusic->PlayFile(file.as<NativeFile>().nativePath().toUtf8(), looped);
752 }
753 else if (iMusic->Play && iMusic->SongBuffer)
754 {
755 // Buffer the data using the driver's own facility.
756 dsize const len = hndl->length();
757 hndl->read((duint8 *) iMusic->SongBuffer(len), len);
758
759 return iMusic->Play(looped);
760 }
761 // Does this interface offer playback from a native file?
762 else
763 return 0; // Continue.
764 });
765
766 App_FileSystem().releaseFile(hndl->file());
767 return didPlay;
768 }
769 catch (FS1::NotFoundError const &)
770 {} // Ignore this error.
771 return 0; // Continue.
772 }
773
774 /**
775 * @return @c 1= if music was started. 0, if attempted to start but failed.
776 * @c -1= if it was MUS data and @a canPlayMUS says we can't play it.
777 */
778 dint playMusicLump(lumpnum_t lumpNum, bool looped = false, bool canPlayMUS = true)
779 {
780 DENG2_ASSERT(musAvail);
781
782 if (!App_FileSystem().nameIndex().hasLump(lumpNum))
783 return 0;
784
785 File1 &lump = App_FileSystem().lump(lumpNum);
786 if (recognizeMus(lump))
787 {
788 // Lump is in DOOM's MUS format. We must first convert it to MIDI.
789 if (!canPlayMUS) return -1;
790
791 File &midi = FS::rootFolder().replaceFile(composeMusicBufferFilename(".mid"));
792
793 // Read the lump, convert to MIDI and output to a temp file in the working directory.
794 // Use a filename with the .mid extension so that any player which relies on the it
795 // for format recognition works as expected.
796 //duint8 *buf = (duint8 *) M_Malloc(lump.size());
797 Block buf(lump.size());
798 lump.read(buf.data(), 0, lump.size());
799 midi << M_Mus2Midi(buf);
800 midi.flush();
801 //M_Free(buf); buf = nullptr;
802
803 return forAllInterfaces(AUDIO_IMUSIC, [&midi, &looped] (void *ifs)
804 {
805 auto *iMusic = (audiointerface_music_t *) ifs;
806 if (iMusic->PlayFile)
807 {
808 return iMusic->PlayFile(midi.as<NativeFile>().nativePath().toUtf8(), looped);
809 }
810 return 0; // Continue.
811 });
812 }
813
814 return forAllInterfaces(AUDIO_IMUSIC, [this, &lump, &looped] (void *ifs)
815 {
816 auto *iMusic = (audiointerface_music_t *) ifs;
817
818 // Does this interface offer buffered playback?
819 if (iMusic->Play && iMusic->SongBuffer)
820 {
821 // Buffer the data using the driver's own facility.
822 std::unique_ptr<FileHandle> hndl(&App_FileSystem().openLump(lump));
823 dsize const length = hndl->length();
824 hndl->read((duint8 *) iMusic->SongBuffer(length), length);
825 App_FileSystem().releaseFile(hndl->file());
826
827 return iMusic->Play(looped);
828 }
829 // Does this interface offer playback from a native file?
830 else if (iMusic->PlayFile)
831 {
832 String bufName = composeMusicBufferFilename();
833 if (!F_DumpFile(lump, bufName.toUtf8()))
834 {
835 // Failed to write the lump...
836 return 0;
837 }
838 return iMusic->PlayFile(FS::rootFolder().locate<File const>(bufName)
839 .as<NativeFile>().nativePath().toUtf8(), looped);
840 }
841
842 return 0; // Continue.
843 });
844 }
845
846 dint playMusicCDTrack(dint track, bool looped)
847 {
848 // Assume track 0 is not valid.
849 if (track == 0) return 0;
850
851 return forAllInterfaces(AUDIO_ICD, [&track, &looped] (void *ifs)
852 {
853 auto *iCD = (audiointerface_cd_t *) ifs;
854 if (iCD->Play)
855 {
856 return iCD->Play(track, looped);
857 }
858 return 0; // Continue.
859 });
860 }
861
862 /**
863 * Perform initialization for music playback.
864 */
865 void initMusic()
866 {
867 // Already been here?
868 if (musAvail) return;
869
870 LOG_AUDIO_VERBOSE("Initializing Music subsystem...");
871
872 musAvail = false;
873 musCurrentSong = "";
874 musPaused = false;
875
876 CommandLine &cmdLine = CommandLine::get();
877 if (::isDedicated || cmdLine.has("-nomusic"))
878 {
879 LOG_AUDIO_NOTE("Music disabled");
880 return;
881 }
882
883 // Initialize interfaces for music playback.
884 dint initialized = 0;
885 forAllInterfaces(AUDIO_IMUSIC_OR_ICD, [this, &initialized] (void *ifs)
886 {
887 auto *iMusic = (audiointerface_music_generic_t *) ifs;
888 if (iMusic->Init())
889 {
890 initialized += 1;
891 }
892 else
893 {
894 LOG_AUDIO_WARNING("Failed to initialize \"%s\" for music playback")
895 << interfaceName(iMusic);
896 }
897 return LoopContinue;
898 });
899
900 // Remember whether an interface for music playback initialized successfully.
901 musAvail = initialized >= 1;
902 if (musAvail)
903 {
904 // Tell audio drivers about our soundfont config.
905 self().updateMusicMidiFont();
906 }
907 }
908
909 /**
910 * Perform deinitialize for music playback.
911 */
912 void deinitMusic()
913 {
914 // Already been here?
915 if (!musAvail) return;
916 musAvail = false;
917
918 // Shutdown interfaces.
919 forAllInterfaces(AUDIO_IMUSIC_OR_ICD, [] (void *ifs)
920 {
921 auto *iMusic = (audiointerface_music_generic_t *) ifs;
922 if (iMusic->Shutdown) iMusic->Shutdown();
923 return LoopContinue;
924 });
925 }
926
927 void updateMusicVolumeIfChanged()
928 {
929 if (!musAvail) return;
930
931 static dint oldMusVolume = -1;
932 if (::musVolume != oldMusVolume)
933 {
934 oldMusVolume = ::musVolume;
935
936 // Set volume of all available interfaces.
937 dfloat newVolume = ::musVolume / 255.0f;
938 forAllInterfaces(AUDIO_IMUSIC_OR_ICD, [&newVolume] (void *ifs)
939 {
940 auto *iMusic = (audiointerface_music_generic_t *) ifs;
941 iMusic->Set(MUSIP_VOLUME, newVolume);
942 return LoopContinue;
943 });
944 }
945 }
946
947 /**
948 * Perform initialization for sound effect playback.
949 */
950 void initSfx()
951 {
952 // Already initialized?
953 if (sfxAvail) return;
954
955 // Check if sound has been disabled with a command line option.
956 if (CommandLine::get().has("-nosfx"))
957 {
958 LOG_AUDIO_NOTE("Sound effects disabled");
959 return;
960 }
961
962 LOG_AUDIO_VERBOSE("Initializing sound effect playback...");
963 // No available interface?
964 if (!self().sfx()) return;
965
966 // This is based on the scientific calculations that if the DOOM marine
967 // is 56 units tall, 60 is about two meters.
968 //// @todo Derive from the viewheight.
969 self().sfx()->Listener(SFXLP_UNITS_PER_METER, 30);
970 self().sfx()->Listener(SFXLP_DOPPLER, 1.5f);
971
972 // The audio driver is working, let's create the channels.
973 initSfxChannels();
974
975 // (Re)Init the sample cache.
976 sfxSampleCache.clear();
977
978 // Initialize reverb effects to off.
979 sfxListenerNoReverb();
980
981 // Finally, start the sound channel refresh thread. It will stop on its
982 // own when it notices that the rest of the sound system is going down.
983
984 refreshing = false;
985 allowRefresh = true;
986
987 dint disableRefresh = false;
988
989 // Nothing to refresh?
990 if (!self().sfx()) goto noRefresh;
991
992 if (self().sfx()->Getv)
993 {
994 self().sfx()->Getv(SFXIP_DISABLE_CHANNEL_REFRESH, &disableRefresh);
995 }
996
997 if (!disableRefresh)
998 {
999 // Start the refresh thread. It will run until the Sfx module is shut down.
1000 refreshHandle = Sys_StartThread(sfxChannelRefreshThread, nullptr, nullptr);
1001 if (!refreshHandle)
1002 {
1003 throw Error("audio::System::initSfx", "Failed to start refresh thread");
1004 }
1005 }
1006 else
1007 {
1008 noRefresh:
1009 LOGDEV_AUDIO_NOTE("Audio driver does not require a refresh thread");
1010 }
1011
1012 // The Sfx module is now available.
1013 sfxAvail = true;
1014 }
1015
1016 /**
1017 * Perform deinitialization for sound effect playback.
1018 */
1019 void deinitSfx()
1020 {
1021 // Not initialized?
1022 if (!sfxAvail) return;
1023
1024 // These will stop further refreshing.
1025 sfxAvail = false;
1026 allowRefresh = false;
1027
1028 if (refreshHandle)
1029 {
1030 // Wait for the sfx refresh thread to stop.
1031 Sys_WaitThread(refreshHandle, 2000, nullptr);
1032 refreshHandle = nullptr;
1033 }
1034
1035 // Clear the sample cache.
1036 sfxSampleCache.clear();
1037
1038 // Destroy channels.
1039 shutdownSfxChannels();
1040 }
1041
1042 /**
1043 * The specified sample will soon no longer exist. All channel buffers
1044 * loaded with the sample will be reset.
1045 */
1046 void unloadSoundID(dint id)
1047 {
1048 if (!sfxAvail) return;
1049
1050 self().allowSfxRefresh(false);
1051 sfxChannels->forAll([this, &id] (audio::SfxChannel &ch)
1052 {
1053 if (ch.hasBuffer())
1054 {
1055 sfxbuffer_t &sbuf = ch.buffer();
1056 if (sbuf.sample && sbuf.sample->id == id)
1057 {
1058 // Stop and unload.
1059 self().sfx()->Reset(&sbuf);
1060 }
1061 }
1062 return LoopContinue;
1063 });
1064 self().allowSfxRefresh(true);
1065 }
1066
1067 /**
1068 * Stop all channels and destroy their buffers.
1069 */
1070 void destroySfxChannels()
1071 {
1072 self().allowSfxRefresh(false);
1073 sfxChannels->forAll([this] (audio::SfxChannel &ch)
1074 {
1075 ch.stop();
1076 if (ch.hasBuffer())
1077 {
1078 self().sfx()->Destroy(&ch.buffer());
1079 ch.setBuffer(nullptr);
1080 }
1081 return LoopContinue;
1082 });
1083 self().allowSfxRefresh(true);
1084 }
1085
1086 void createSfxChannels()
1087 {
1088 if (!bool( sfxChannels )) return; // Huh?
1089
1090 dint num2D = sfx3D ? SOUND_CHANNEL_2DCOUNT: sfxChannels->count(); // The rest will be 3D.
1091 dint bits = sfxBits;
1092 dint rate = sfxRate;
1093
1094 // Change the primary buffer format to match the channel format.
1095 dfloat parm[2] = { dfloat(bits), dfloat(rate) };
1096 self().sfx()->Listenerv(SFXLP_PRIMARY_FORMAT, parm);
1097
1098 // Create sample buffers for the channels.
1099 dint idx = 0;
1100 sfxChannels->forAll([this, &num2D, &bits, &rate, &idx] (audio::SfxChannel &ch)
1101 {
1102 ch.setBuffer(self().sfx()->Create(num2D-- > 0 ? 0 : SFXBF_3D, bits, rate));
1103 if (!ch.hasBuffer())
1104 {
1105 LOG_AUDIO_WARNING("Failed to create sample buffer for #%i") << idx;
1106 }
1107 idx += 1;
1108 return LoopContinue;
1109 });
1110 }
1111
1112 // Create channels according to the current mode.
1113 void initSfxChannels()
1114 {
1115 // The -sfxchan option can be used to change the number of channels.
1116 if (CommandLine_CheckWith("-sfxchan", 1))
1117 {
1118 Config::get().set("audio.channels", String(CommandLine_Next()).toInt());
1119
1120 //numChannels = de::clamp(1, String(CommandLine_Next()).toInt(), SOUND_CHANNEL_COUNT_MAX);
1121 }
1122
1123 dint numChannels = Config::get().geti("audio.channels", SOUND_CHANNEL_COUNT_DEFAULT);
1124 numChannels = Rangei(1, SOUND_CHANNEL_COUNT_MAX).clamp(numChannels);
1125
1126 LOG_AUDIO_NOTE("Initializing %i sound effect channels") << numChannels;
1127
1128 // Allocate and init the channels.
1129 sfxChannels.reset(new audio::SfxChannels(numChannels));
1130 createSfxChannels();
1131 }
1132
1133 /**
1134 * Frees all memory allocated for the channels.
1135 */
1136 void shutdownSfxChannels()
1137 {
1138 destroySfxChannels();
1139 sfxChannels.reset();
1140 }
1141
1142 /**
1143 * Destroys all channels and creates them again.
1144 */
1145 void recreateSfxChannels()
1146 {
1147 destroySfxChannels();
1148 createSfxChannels();
1149 }
1150
1151 void getSfxChannelPriorities(dfloat *prios) const
1152 {
1153 if (!prios) return;
1154
1155 dint idx = 0;
1156 sfxChannels->forAll([&prios, &idx] (audio::SfxChannel &ch)
1157 {
1158 prios[idx++] = ch.priority();
1159 return LoopContinue;
1160 });
1161 }
1162
1163 #endif // __CLIENT__
1164
1165 void sfxClearLogical()
1166 {
1167 qDeleteAll(sfxLogicHash);
1168 sfxLogicHash.clear();
1169 }
1170
1171 /**
1172 * Maybe remove stopped sounds from the LSM.
1173 */
1174 void sfxPurgeLogical()
1175 {
1176 // Too soon?
1177 duint const nowTime = Timer_RealMilliseconds();
1178 if (nowTime - sfxLogicLastPurge < SOUND_LOGICAL_PURGEINTERVAL) return;
1179
1180 // Peform the purge now.
1181 // LOGDEV_AUDIO_XVERBOSE("purging logic sound hash...", "");
1182 sfxLogicLastPurge = nowTime;
1183
1184 // Check all sounds in the hash.
1185 MutableLogicSoundHashIterator it(sfxLogicHash);
1186 while (it.hasNext())
1187 {
1188 it.next();
1189 LogicSound &lsound = *it.value();
1190 if (!lsound.isRepeating && lsound.endTime < nowTime)
1191 {
1192 // This has stopped.
1193 delete &lsound;
1194 it.remove();
1195 }
1196 }
1197 }
1198
1199 /**
1200 * The sound is removed from the list of playing sounds. Called whenever
1201 * a sound is stopped, regardless of whether it was actually playing on
1202 * the local system.
1203 *
1204 * @note If @a soundId == 0 and @a emitter == nullptr then stop everything.
1205 *
1206 * @return Number of sounds stopped.
1207 */
1208 dint sfxStopLogical(dint soundId, mobj_t const *emitter)
1209 {
1210 dint stopCount = 0;
1211 MutableLogicSoundHashIterator it(sfxLogicHash);
1212 while (it.hasNext())
1213 {
1214 it.next();
1215
1216 LogicSound const &lsound = *it.value();
1217 if (soundId)
1218 {
1219 if (it.key() != soundId) continue;
1220 }
1221 else if (emitter)
1222 {
1223 if (lsound.emitter != emitter) continue;
1224 }
1225
1226 delete &lsound;
1227 it.remove();
1228 stopCount++;
1229 }
1230 return stopCount;
1231 }
1232
1233 /**
1234 * The sound is entered into the list of playing sounds. Called when a
1235 * 'world class' sound is started, regardless of whether it's actually
1236 * started on the local system.
1237 *
1238 * @todo Why does the Server cache sound samples and/or care to know the
1239 * length of the samples? It is entirely possible that the Client is using
1240 * a different set of samples so using this information on server side (for
1241 * scheduling of remote playback events?) is not logical. -ds
1242 */
1243 void sfxStartLogical(dint soundIdAndFlags, mobj_t const *emitter)
1244 {
1245 if (soundIdAndFlags <= 0) return;
1246
1247 dint const soundId = (soundIdAndFlags & ~DDSF_FLAG_MASK);
1248
1249 // Cache the sound sample associated with @a soundId (if necessary)
1250 // so that we can determine it's length.
1251 if (sfxsample_t *sample = sfxSampleCache.cache(soundId))
1252 {
1253 bool const isRepeating = (soundIdAndFlags & DDSF_REPEAT) ||
1254 Def_SoundIsRepeating(soundId);
1255
1256 duint length = (1000 * sample->numSamples) / sample->rate;
1257 if (isRepeating && length > 1)
1258 {
1259 length = 1;
1260 }
1261
1262 // Ignore zero length sounds.
1263 /// @todo Shouldn't we still stop others though? -ds
1264 if (!length) return;
1265
1266 // Only one sound per emitter?
1267 if (emitter && sfxLogicOneSoundPerEmitter)
1268 {
1269 // Stop all other sounds.
1270 sfxStopLogical(0, emitter);
1271 }
1272
1273 auto *ls = new Impl::LogicSound;
1274 //ls->soundId = soundId;
1275 ls->emitter = emitter;
1276 ls->isRepeating = isRepeating;
1277 ls->endTime = Timer_RealMilliseconds() + length;
1278 sfxLogicHash.insert(soundId, ls);
1279 }
1280 }
1281
1282 /**
1283 * @param sectorEmitter Sector in which to stop sounds.
1284 * @param soundId Unique identifier of the sound to be stopped.
1285 * If @c 0, ID not checked.
1286 * @param flags @ref soundStopFlags
1287 */
1288 void stopSectorSounds(ddmobj_base_t *sectorEmitter, dint soundId, dint flags)
1289 {
1290 if (!sectorEmitter || !flags) return;
1291
1292 // Are we stopping with this sector's emitter?
1293 if (flags & SSF_SECTOR)
1294 {
1295 self().stopSound(soundId, (mobj_t *)sectorEmitter);
1296 }
1297
1298 // Are we stopping with linked emitters?
1299 if (!(flags & SSF_SECTOR_LINKED_SURFACES)) return;
1300
1301 // Process the rest of the emitter chain.
1302 ddmobj_base_t *base = sectorEmitter;
1303 while ((base = (ddmobj_base_t *)base->thinker.next))
1304 {
1305 // Stop sounds from this emitter.
1306 self().stopSound(soundId, (mobj_t *)base);
1307 }
1308 }
1309
1310 #ifdef __CLIENT__
1311
1312 /**
1313 * Returns the 3D position of the sound effect listener, in map space.
1314 */
1315 Vector3d getSfxListenerOrigin() const
1316 {
1317 if (sfxListener)
1318 {
1319 auto origin = Vector3d(sfxListener->origin);
1320 origin.z += sfxListener->height - 5; /// @todo Make it exactly eye-level! (viewheight).
1321 return origin;
1322 }
1323 return Vector3d();
1324 }
1325
1326 void sfxListenerNoReverb()
1327 {
1328 if (!sfxAvail) return;
1329
1330 sfxListenerSubsector = nullptr;
1331
1332 dfloat rev[4] = { 0, 0, 0, 0 };
1333 self().sfx()->Listenerv(SFXLP_REVERB, rev);
1334 self().sfx()->Listener(SFXLP_UPDATE, 0);
1335 }
1336
1337 void updateSfxListener()
1338 {
1339 if (!sfxAvail || !sfx3D) return;
1340
1341 // No volume means no sound.
1342 if (!::sfxVolume) return;
1343
1344 // Update the listener mobj.
1345 self().setSfxListener(S_GetListenerMobj());
1346 if (sfxListener)
1347 {
1348 {
1349 // Origin. At eye-level.
1350 auto const origin = Vector4f(getSfxListenerOrigin().toVector3f(), 0);
1351 dfloat vec[4];
1352 origin.decompose(vec);
1353 self().sfx()->Listenerv(SFXLP_POSITION, vec);
1354 }
1355 {
1356 // Orientation. (0,0) will produce front=(1,0,0) and up=(0,0,1).
1357 dfloat vec[2] = {
1358 sfxListener->angle / (dfloat) ANGLE_MAX * 360,
1359 (sfxListener->dPlayer ? LOOKDIR2DEG(sfxListener->dPlayer->lookDir) : 0)
1360 };
1361 self().sfx()->Listenerv(SFXLP_ORIENTATION, vec);
1362 }
1363 {
1364 // Velocity. The unit is world distance units per second
1365 auto const velocity = Vector4f(Vector3d(sfxListener->mom).toVector3f(), 0) * TICSPERSEC;
1366 dfloat vec[4];
1367 velocity.decompose(vec);
1368 self().sfx()->Listenerv(SFXLP_VELOCITY, vec);
1369 }
1370
1371 // Reverb effects. Has the current subsector changed?
1372 world::Subsector *newSubsector = Mobj_SubsectorPtr(*sfxListener);
1373 if (newSubsector && (!sfxListenerSubsector || sfxListenerSubsector != newSubsector))
1374 {
1375 sfxListenerSubsector = newSubsector;
1376
1377 // It may be necessary to recalculate the reverb properties...
1378 world::ClientSubsector::AudioEnvironment const &aenv = sfxListenerSubsector->as<world::ClientSubsector>().reverb();
1379
1380 dfloat args[NUM_REVERB_DATA];
1381 args[SFXLP_REVERB_VOLUME ] = aenv.volume * sfxReverbStrength;
1382 args[SFXLP_REVERB_SPACE ] = aenv.space;
1383 args[SFXLP_REVERB_DECAY ] = aenv.decay;
1384 args[SFXLP_REVERB_DAMPING] = aenv.damping;
1385
1386 self().sfx()->Listenerv(SFXLP_REVERB, args);
1387 }
1388 }
1389
1390 // Update all listener properties.
1391 self().sfx()->Listener(SFXLP_UPDATE, 0);
1392 }
1393
1394 void updateSfx3DModeIfChanged()
1395 {
1396 static dint old3DMode = false;
1397
1398 if (old3DMode == sfx3D) return; // No change.
1399
1400 LOG_AUDIO_VERBOSE("Switching to %s mode...") << (old3DMode ? "2D" : "3D");
1401
1402 // To make the change effective, re-create all channels.
1403 recreateSfxChannels();
1404
1405 if (old3DMode)
1406 {
1407 // Going 2D - ensure reverb is disabled.
1408 sfxListenerNoReverb();
1409 }
1410 old3DMode = sfx3D;
1411 }
1412
1413 #if 0
1414 void updateSfxSampleRateIfChanged()
1415 {
1416 static dint old16Bit = false;
1417 static dint oldRate = 11025;
1418
1419 // Ensure the rate is valid.
1420 if (sfxSampleRate != 11025 && sfxSampleRate != 22050 && sfxSampleRate != 44100)
1421 {
1422 LOG_AUDIO_WARNING("\"sound-rate\" corrected to 11025 from invalid value (%i)") << sfxSampleRate;
1423 sfxSampleRate = 11025;
1424 }
1425
1426 // Do we need to change the sample format?
1427 if (old16Bit != sfx16Bit || oldRate != sfxSampleRate)
1428 {
1429 dint const newBits = sfx16Bit ? 16 : 8;
1430 dint const newRate = sfxSampleRate;
1431 if (::sfxBits != newBits || ::sfxRate != newRate)
1432 {
1433 LOG_AUDIO_VERBOSE("Switching sound rate to %iHz (%i-bit)..") << newRate << newBits;
1434
1435 // Set the new buffer format.
1436 ::sfxBits = newBits;
1437 ::sfxRate = newRate;
1438 recreateSfxChannels();
1439
1440 // The cache just became useless, clear it.
1441 sfxSampleCache.clear();
1442 }
1443 old16Bit = sfx16Bit;
1444 oldRate = sfxSampleRate;
1445 }
1446 }
1447 #endif
1448
1449 void sfxSampleCacheAboutToRemove(sfxsample_t const &sample)
1450 {
1451 // Reset all channels loaded with the sample data and stop all sounds using
1452 // this sample (the sample data will be gone soon).
1453 unloadSoundID(sample.id);
1454 }
1455 #endif // __CLIENT__
1456
1457 void reset()
1458 {
1459 #ifdef __CLIENT__
1460 self().reset();
1461 #endif
1462 sfxClearLogical();
1463 }
1464
1465 void aboutToUnloadGame(Game const &)
1466 {
1467 reset();
1468 }
1469 };
1470
AudioSystem()1471 AudioSystem::AudioSystem() : d(new Impl(this))
1472 {}
1473
get()1474 AudioSystem &AudioSystem::get()
1475 {
1476 DENG2_ASSERT(theAudioSystem);
1477 return *theAudioSystem;
1478 }
1479
timeChanged(Clock const &)1480 void AudioSystem::timeChanged(Clock const &)
1481 {
1482 // Nothing to do.
1483 }
1484
reinitialize()1485 void AudioSystem::reinitialize()
1486 {
1487 LOG_AS("AudioSystem");
1488 LOG_AUDIO_NOTE("Reinitializing all audio interfaces...");
1489
1490 d->reset();
1491 #ifdef __CLIENT__
1492 deinitPlayback();
1493 initPlayback();
1494 #endif
1495 }
1496
description() const1497 String AudioSystem::description() const
1498 {
1499 #define TABBED(A, B) _E(Ta) " " _E(l) A _E(.) " " _E(Tb) << (B) << "\n"
1500
1501 String str;
1502 QTextStream os(&str);
1503
1504 os << _E(b) "Audio configuration:\n" _E(.);
1505
1506 #if 0
1507 /// @todo When a game is loaded, these could be included under an additional
1508 /// "Music preferences" heading. -jk
1509 os << TABBED("Music volume:", musVolume);
1510 #ifdef __CLIENT__
1511 String const midiFontPath(musMidiFontPath);
1512 os << TABBED("Music sound font:", midiFontPath.isEmpty() ? "None" : midiFontPath);
1513 os << TABBED("Music source preference:", musicSourceAsText(musSourcePreference));
1514 #endif
1515 #endif
1516
1517 #ifdef __CLIENT__
1518 int ifCounts[AUDIO_INTERFACE_COUNT] {};
1519
1520 // Include an active playback interface itemization.
1521 for (dint i = d->activeInterfaces.count(); i-- > 0; )
1522 {
1523 Impl::AudioInterface const &ifs = d->activeInterfaces[i];
1524
1525 String ifName = (ifs.type == AUDIO_IMUSIC? "Music" :
1526 ifs.type == AUDIO_ISFX? "SFX" : "CD");
1527 if (++ifCounts[ifs.type] > 1)
1528 {
1529 ifName += String(" %1").arg(ifCounts[ifs.type]);
1530 }
1531
1532 os << _E(Ta) _E(l) " " << ifName << ": " << _E(.) _E(Tb)
1533 << d->interfaceName(ifs.i.any) << "\n";
1534
1535 /*}
1536 else if (ifs.type == AUDIO_ISFX)
1537 {
1538 os << _E(Ta) _E(l) << " SFX: " << _E(.) _E(Tb) << d->interfaceName(ifs.i.sfx) << "\n";
1539 }*/
1540 }
1541 #endif
1542
1543 return str.rightStrip();
1544
1545 #undef TABBED
1546 }
1547
1548 #ifdef __CLIENT__
reset()1549 void AudioSystem::reset()
1550 {
1551 LOG_AS("AudioSystem");
1552 LOG_AUDIO_VERBOSE("Reseting...");
1553
1554 if (d->sfxAvail)
1555 {
1556 d->sfxListenerSubsector = nullptr;
1557
1558 // Stop all channels.
1559 d->sfxChannels->forAll([] (audio::SfxChannel &ch)
1560 {
1561 ch.stop();
1562 return LoopContinue;
1563 });
1564
1565 // Clear the sample cache.
1566 d->sfxSampleCache.clear();
1567 }
1568
1569 stopMusic();
1570 }
1571 #endif
1572
1573 /**
1574 * @todo Do this in timeChanged()
1575 */
startFrame()1576 void AudioSystem::startFrame()
1577 {
1578 LOG_AS("AudioSystem");
1579
1580 #ifdef __CLIENT__
1581 d->updateMusicVolumeIfChanged();
1582
1583 if (sfxIsAvailable())
1584 {
1585 // Update all channels (freq, 2D:pan,volume, 3D:position,velocity).
1586
1587 // Update the active interface.
1588 d->getBaseInterface(sfx()).Event(SFXEV_BEGIN);
1589
1590 // Have there been changes to the cvar settings?
1591 d->updateSfx3DModeIfChanged();
1592 //d->updateSfxSampleRateIfChanged();
1593
1594 // Should we purge the cache (to conserve memory)?
1595 d->sfxSampleCache.maybeRunPurge();
1596 }
1597
1598 if (d->musAvail)
1599 {
1600 // Update all interfaces.
1601 d->forAllInterfaces(AUDIO_IMUSIC_OR_ICD, [] (void *ifs)
1602 {
1603 auto *iMusic = (audiointerface_music_generic_t *) ifs;
1604 iMusic->Update();
1605 return LoopContinue;
1606 });
1607 }
1608 #endif
1609
1610 d->sfxLogicOneSoundPerEmitter = sfxOneSoundPerEmitter;
1611 d->sfxPurgeLogical();
1612 }
1613
1614 #ifdef __CLIENT__
endFrame()1615 void AudioSystem::endFrame()
1616 {
1617 LOG_AS("AudioSystem");
1618
1619 if (sfxIsAvailable())
1620 {
1621 if (!BusyMode_Active())
1622 {
1623 // Update channel and listener properties.
1624
1625 // If no listener is available - no 3D positioning is done.
1626 d->sfxListener = S_GetListenerMobj();
1627
1628 // Update channels.
1629 d->sfxChannels->forAll([] (audio::SfxChannel &ch)
1630 {
1631 if (ch.hasBuffer() && (ch.buffer().flags & SFXBF_PLAYING))
1632 {
1633 ch.updatePriority();
1634 }
1635 return LoopContinue;
1636 });
1637
1638 // Update listener.
1639 d->updateSfxListener();
1640 }
1641
1642 // Update the active interface.
1643 d->getBaseInterface(sfx()).Event(SFXEV_END);
1644 }
1645 }
1646 #endif
1647
initPlayback()1648 void AudioSystem::initPlayback()
1649 {
1650 LOG_AS("AudioSystem");
1651
1652 CommandLine &cmdLine = CommandLine::get();
1653 if (cmdLine.has("-nosound") || cmdLine.has("-noaudio"))
1654 return;
1655
1656 #ifdef __CLIENT__
1657 LOG_AUDIO_VERBOSE("Initializing for playback...");
1658
1659 // Disable random pitch changes?
1660 sfxNoRndPitch = cmdLine.has("-norndpitch");
1661
1662 // Try to load the audio driver plugin(s).
1663 if (d->loadDrivers())
1664 {
1665 // Init for sound effects.
1666 try
1667 {
1668 d->initSfx();
1669 }
1670 catch (Error const &er)
1671 {
1672 LOG_AUDIO_NOTE("Failed initializing playback for sound effects:\n")
1673 << er.asText();
1674 }
1675
1676 // Init for music.
1677 try
1678 {
1679 d->initMusic();
1680 }
1681 catch (Error const &er)
1682 {
1683 LOG_AUDIO_NOTE("Failed initializing playback for music:\n")
1684 << er.asText();
1685 }
1686 }
1687 else
1688 {
1689 LOG_AUDIO_NOTE("Music and sound effects are disabled");
1690 }
1691
1692 // Print a summary of the active configuration to the log.
1693 LOG_AUDIO_MSG("%s") << description();
1694 #endif
1695 }
1696
1697 #ifdef __CLIENT__
1698
deinitPlayback()1699 void AudioSystem::deinitPlayback()
1700 {
1701 LOG_AS("AudioSystem");
1702
1703 d->deinitSfx();
1704 d->deinitMusic();
1705
1706 d->unloadDrivers();
1707 }
1708
musicSourceAsText(MusicSource source)1709 String AudioSystem::musicSourceAsText(MusicSource source) // static
1710 {
1711 static char const *sourceNames[3] = {
1712 /* MUSP_MUS */ "MUS lumps",
1713 /* MUSP_EXT */ "External files",
1714 /* MUSP_CD */ "CD",
1715 };
1716 if (source >= MUSP_MUS && source <= MUSP_CD)
1717 return sourceNames[dint( source )];
1718 return "(invalid)";
1719 }
1720
musicIsAvailable() const1721 bool AudioSystem::musicIsAvailable() const
1722 {
1723 return d->musAvail;
1724 }
1725
musicIsPlaying() const1726 bool AudioSystem::musicIsPlaying() const
1727 {
1728 //LOG_AS("AudioSystem");
1729 return d->forAllInterfaces(AUDIO_IMUSIC_OR_ICD, [] (void *ifs)
1730 {
1731 auto *iMusic = (audiointerface_music_t *) ifs;
1732 return iMusic->gen.Get(MUSIP_PLAYING, nullptr);
1733 });
1734 }
1735
stopMusic()1736 void AudioSystem::stopMusic()
1737 {
1738 if (!d->musAvail) return;
1739
1740 LOG_AS("AudioSystem");
1741 d->musCurrentSong = "";
1742
1743 // Stop all interfaces.
1744 d->forAllInterfaces(AUDIO_IMUSIC_OR_ICD, [] (void *ifs)
1745 {
1746 auto *iMusic = (audiointerface_music_generic_t *) ifs;
1747 iMusic->Stop();
1748 return LoopContinue;
1749 });
1750 }
1751
pauseMusic(bool doPause)1752 void AudioSystem::pauseMusic(bool doPause)
1753 {
1754 if (!d->musAvail) return;
1755
1756 LOG_AS("AudioSystem");
1757 d->musPaused = !d->musPaused;
1758
1759 // Pause playback on all interfaces.
1760 d->forAllInterfaces(AUDIO_IMUSIC_OR_ICD, [&doPause] (void *ifs)
1761 {
1762 auto *iMusic = (audiointerface_music_generic_t *) ifs;
1763 iMusic->Pause(doPause);
1764 return LoopContinue;
1765 });
1766 }
1767
musicIsPaused() const1768 bool AudioSystem::musicIsPaused() const
1769 {
1770 return d->musPaused;
1771 }
1772
playMusic(Record const & definition,bool looped)1773 dint AudioSystem::playMusic(Record const &definition, bool looped)
1774 {
1775 if (!d->musAvail) return false;
1776
1777 LOG_AS("AudioSystem");
1778 LOG_AUDIO_MSG("Starting music \"%s\"%s") << definition.gets("id") << (looped ? " looped" : "");
1779 //LOG_AUDIO_VERBOSE("Current song '%s'") << d->musCurrentSong;
1780
1781 // We will not restart the currently playing song.
1782 if (definition.gets("id") == d->musCurrentSong && musicIsPlaying())
1783 {
1784 // This is not a failure, though, since the right music is played.
1785 return true;
1786 }
1787
1788 // Stop the currently playing song.
1789 stopMusic();
1790
1791 // Switch to an unused file buffer if asked.
1792 d->musNeedBufFileSwitch = true;
1793
1794 // This is the song we're playing now.
1795 d->musCurrentSong = definition.gets("id");
1796
1797 // Determine the music source, order preferences.
1798 dint source[3];
1799 source[0] = musSourcePreference;
1800 switch (musSourcePreference)
1801 {
1802 case MUSP_CD:
1803 source[1] = MUSP_EXT;
1804 source[2] = MUSP_MUS;
1805 break;
1806
1807 case MUSP_EXT:
1808 source[1] = MUSP_MUS;
1809 source[2] = MUSP_CD;
1810 break;
1811
1812 default: // MUSP_MUS
1813 source[1] = MUSP_EXT;
1814 source[2] = MUSP_CD;
1815 break;
1816 }
1817
1818 // Try to start the song.
1819 for (dint i = 0; i < 3; ++i)
1820 {
1821 bool canPlayMUS = true;
1822
1823 switch (source[i])
1824 {
1825 case MUSP_CD:
1826 if (cd())
1827 {
1828 const int cdTrack = defn::Music(definition).cdTrack();
1829 if (d->playMusicCDTrack(cdTrack, looped))
1830 {
1831 LOG_AUDIO_VERBOSE("Playing CD track %d") << cdTrack;
1832 return true;
1833 }
1834 }
1835 break;
1836
1837 case MUSP_EXT:
1838 if (d->playMusicFile(App_Resources().tryFindMusicFile(definition), looped))
1839 {
1840 LOG_AUDIO_VERBOSE("Playing external music file \"%s\"")
1841 << definition.gets("path");
1842 return true;
1843 }
1844
1845 // Next, try non-MUS lumps.
1846 canPlayMUS = false;
1847
1848 // fall through
1849
1850 case MUSP_MUS:
1851 {
1852 const String lump = definition.gets("lumpName");
1853 if (d->playMusicLump(App_FileSystem().lumpNumForName(lump), looped, canPlayMUS) == 1)
1854 {
1855 LOG_AUDIO_VERBOSE("Playing music lump \"%s\"") << lump;
1856 return true;
1857 }
1858 break;
1859 }
1860
1861 default: DENG2_ASSERT(!"Mus_Start: Invalid value for order[i]"); break;
1862 }
1863 }
1864
1865 // No song was started.
1866 return false;
1867 }
1868
playMusicLump(lumpnum_t lumpNum,bool looped)1869 dint AudioSystem::playMusicLump(lumpnum_t lumpNum, bool looped)
1870 {
1871 stopMusic();
1872 LOG_AS("AudioSystem");
1873 return d->playMusicLump(lumpNum, looped);
1874 }
1875
playMusicFile(String const & filePath,bool looped)1876 dint AudioSystem::playMusicFile(String const &filePath, bool looped)
1877 {
1878 stopMusic();
1879 LOG_AS("AudioSystem");
1880 return d->playMusicFile(filePath, looped);
1881 }
1882
playMusicCDTrack(dint cdTrack,bool looped)1883 dint AudioSystem::playMusicCDTrack(dint cdTrack, bool looped)
1884 {
1885 stopMusic();
1886 LOG_AS("AudioSystem");
1887 return d->playMusicCDTrack(cdTrack, looped);
1888 }
1889
updateMusicMidiFont()1890 void AudioSystem::updateMusicMidiFont()
1891 {
1892 LOG_AS("AudioSystem");
1893
1894 NativePath path(musMidiFontPath);
1895 if (path.isEmpty())
1896 {
1897 // The bootstrap script copies the default GeneralUser GS soundfont from the
1898 // client's package so it can be loaded by FluidSynth.
1899 path = App::app().nativeHomePath()/"cache/default.sf2";
1900 }
1901 d->setMusicProperty(AUDIOP_SOUNDFONT_FILENAME, path.expand().toString().toUtf8().constData());
1902 }
1903
sfxIsAvailable() const1904 bool AudioSystem::sfxIsAvailable() const
1905 {
1906 return d->sfxAvail;
1907 }
1908
mustUpsampleToSfxRate() const1909 bool AudioSystem::mustUpsampleToSfxRate() const
1910 {
1911 dint anyRateAccepted = 0;
1912 if (sfx()->Getv)
1913 {
1914 sfx()->Getv(SFXIP_ANY_SAMPLE_RATE_ACCEPTED, &anyRateAccepted);
1915 }
1916 return (anyRateAccepted ? false : true);
1917 }
1918
sfxListener()1919 mobj_t *AudioSystem::sfxListener()
1920 {
1921 return d->sfxListener;
1922 }
1923
setSfxListener(mobj_t * newListener)1924 void AudioSystem::setSfxListener(mobj_t *newListener)
1925 {
1926 d->sfxListener = newListener;
1927 }
1928
1929 #endif // __CLIENT__
1930
soundIsPlaying(dint soundId,mobj_t * emitter) const1931 bool AudioSystem::soundIsPlaying(dint soundId, mobj_t *emitter) const
1932 {
1933 //LOG_AS("AudioSystem");
1934
1935 // Use the logic sound hash to determine whether the referenced sound is being
1936 // played currently. We don't care whether its audible or not.
1937 duint const nowTime = Timer_RealMilliseconds();
1938 if (soundId)
1939 {
1940 auto it = d->sfxLogicHash.constFind(soundId);
1941 while (it != d->sfxLogicHash.constEnd() && it.key() == soundId)
1942 {
1943 Impl::LogicSound const &lsound = *it.value();
1944 if (lsound.emitter == emitter && lsound.isPlaying(nowTime))
1945 return true;
1946
1947 ++it;
1948 }
1949 }
1950 else if (emitter)
1951 {
1952 // Check if the emitter is playing any sound.
1953 auto it = d->sfxLogicHash.constBegin();
1954 while (it != d->sfxLogicHash.constEnd())
1955 {
1956 Impl::LogicSound const &lsound = *it.value();
1957 if (lsound.emitter == emitter && lsound.isPlaying(nowTime))
1958 return true;
1959
1960 ++it;
1961 }
1962 }
1963 return false;
1964
1965 #if 0
1966 // Use the sound channels to determine whether the referenced sound is actually
1967 // being played currently.
1968 if (!d->sfxAvail) return false;
1969
1970 return d->sfxChannels->forAll([&id, &emitter] (audio::SfxChannel &ch)
1971 {
1972 if (ch.hasSample())
1973 {
1974 sfxbuffer_t &sbuf = ch.sample();
1975
1976 if (!(sbuf.flags & SFXBF_PLAYING) ||
1977 ch.emitter() != emitter ||
1978 id && sbuf.sample->id != id)
1979 {
1980 return LoopContinue;
1981 }
1982
1983 // Once playing, repeating sounds don't stop.
1984 if (sbuf.flags & SFXBF_REPEAT)
1985 return LoopAbort;
1986
1987 // Check time. The flag is updated after a slight delay (only at refresh).
1988 if (Sys_GetTime() - ch->startTime() < sbuf.sample->numsamples / (dfloat) sbuf.freq * TICSPERSEC)
1989 {
1990 return LoopAbort;
1991 }
1992 }
1993 return LoopContinue;
1994 });
1995 #endif
1996 }
1997
1998 #ifdef __CLIENT__
1999
stopSoundGroup(dint group,mobj_t const * emitter)2000 void AudioSystem::stopSoundGroup(dint group, mobj_t const *emitter)
2001 {
2002 if (!d->sfxAvail) return;
2003 LOG_AS("AudioSystem");
2004 d->sfxChannels->forAll([this, &group, &emitter] (audio::SfxChannel &ch)
2005 {
2006 if (ch.hasBuffer())
2007 {
2008 sfxbuffer_t &sbuf = ch.buffer();
2009 if ((sbuf.flags & SFXBF_PLAYING) &&
2010 (sbuf.sample->group == group && (!emitter || ch.emitter() == emitter)))
2011 {
2012 // This channel must stop.
2013 sfx()->Stop(&sbuf);
2014 }
2015 }
2016 return LoopContinue;
2017 });
2018 }
2019
stopSoundWithLowerPriority(dint id,mobj_t const * emitter,dint defPriority)2020 dint AudioSystem::stopSoundWithLowerPriority(dint id, mobj_t const *emitter, dint defPriority)
2021 {
2022 if (!d->sfxAvail) return false;
2023
2024 LOG_AS("AudioSystem");
2025 dint stopCount = 0;
2026 d->sfxChannels->forAll([this, &id, &emitter, &defPriority, &stopCount] (audio::SfxChannel &ch)
2027 {
2028 if (!ch.hasBuffer()) return LoopContinue;
2029 sfxbuffer_t &sbuf = ch.buffer();
2030
2031 if (!(sbuf.flags & SFXBF_PLAYING) || (id && sbuf.sample->id != id) ||
2032 (emitter && ch.emitter() != emitter))
2033 {
2034 return LoopContinue;
2035 }
2036
2037 // Can it be stopped?
2038 if (sbuf.flags & SFXBF_DONT_STOP)
2039 {
2040 // The emitter might get destroyed...
2041 ch.setEmitter(nullptr);
2042 ch.setFlags(ch.flags() | (SFXCF_NO_UPDATE | SFXCF_NO_ORIGIN));
2043 return LoopContinue;
2044 }
2045
2046 // Check the priority.
2047 if (defPriority >= 0)
2048 {
2049 dint oldPrio = DED_Definitions()->sounds[sbuf.sample->id].priority;
2050 if (oldPrio < defPriority) // Old is more important.
2051 {
2052 stopCount = -1;
2053 return LoopAbort;
2054 }
2055 }
2056
2057 // This channel must be stopped!
2058 /// @todo should observe. -ds
2059 sfx()->Stop(&sbuf);
2060 ++stopCount;
2061 return LoopContinue;
2062 });
2063
2064 return stopCount;
2065 }
2066
2067 #endif // __CLIENT__
2068
stopSound(dint soundId,mobj_t const * emitter,dint flags)2069 void AudioSystem::stopSound(dint soundId, mobj_t const *emitter, dint flags)
2070 {
2071 LOG_AS("AudioSystem");
2072
2073 // Are we performing any special stop behaviors?
2074 if (emitter && flags)
2075 {
2076 if (emitter->thinker.id)
2077 {
2078 // Emitter is a real Mobj.
2079 d->stopSectorSounds(&Mobj_Sector(emitter)->soundEmitter(), soundId, flags);
2080 return;
2081 }
2082
2083 // The head of the chain is the sector. Find it.
2084 while (emitter->thinker.prev)
2085 {
2086 emitter = (mobj_t *)emitter->thinker.prev;
2087 }
2088 d->stopSectorSounds((ddmobj_base_t *)emitter, soundId, flags);
2089 return;
2090 }
2091
2092 // No special stop behavior.
2093
2094 #ifdef __CLIENT__
2095 stopSoundWithLowerPriority(soundId, emitter, -1);
2096 #endif
2097
2098 // Notify the LSM.
2099 if (d->sfxStopLogical(soundId, emitter))
2100 {
2101 #ifdef __SERVER__
2102 // In netgames, the server is responsible for telling clients
2103 // when to stop sounds. The LSM will tell us if a sound was
2104 // stopped somewhere in the world.
2105 Sv_StopSound(soundId, emitter);
2106 #endif
2107 }
2108 }
2109
2110 #ifdef __CLIENT__
playSound(sfxsample_t * sample,dfloat volume,dfloat freq,mobj_t const * emitter,coord_t * fixedOrigin,dint flags)2111 dint AudioSystem::playSound(sfxsample_t *sample, dfloat volume, dfloat freq, mobj_t const *emitter,
2112 coord_t *fixedOrigin, dint flags)
2113 {
2114 DENG2_ASSERT(sample);
2115 if (!d->sfxAvail) return false;
2116
2117 bool const play3D = sfx3D && (emitter || fixedOrigin);
2118
2119 LOG_AS("AudioSystem");
2120 if (sample->id < 1 || sample->id >= DED_Definitions()->sounds.size()) return false;
2121 if (volume <= 0 || !sample->size) return false;
2122
2123 if (emitter && sfxOneSoundPerEmitter)
2124 {
2125 // Stop any other sounds from the same emitter.
2126 if (stopSoundWithLowerPriority(0, emitter, DED_Definitions()->sounds[sample->id].priority) < 0)
2127 {
2128 // Something with a higher priority is playing, can't start now.
2129 LOG_AUDIO_MSG("Not playing soundId:%i (prio:%i) because overridden (emitter id:%i)")
2130 << sample->id
2131 << DED_Definitions()->sounds[sample->id].priority
2132 << emitter->thinker.id;
2133 return false;
2134 }
2135 }
2136
2137 // Calculate the new sound's priority.
2138 dint const nowTime = Timer_Ticks();
2139 dfloat const myPrio = rateSoundPriority(emitter, fixedOrigin, volume, nowTime);
2140
2141 bool haveChannelPrios = false;
2142 dfloat channelPrios[256/*MAX_CHANNEL_COUNT*/];
2143 dfloat lowPrio = 0;
2144
2145 // Ensure there aren't already too many channels playing this sample.
2146 sfxinfo_t *info = &::runtimeDefs.sounds[sample->id];
2147 if (info->channels > 0)
2148 {
2149 // The decision to stop channels is based on priorities.
2150 d->getSfxChannelPriorities(channelPrios);
2151 haveChannelPrios = true;
2152
2153 dint count = d->sfxChannels->countPlaying(sample->id);
2154 while (count >= info->channels)
2155 {
2156 // Stop the lowest priority sound of the playing instances, again
2157 // noting sounds that are more important than us.
2158 dint idx = 0;
2159 audio::SfxChannel *selCh = nullptr;
2160 d->sfxChannels->forAll([&sample, &myPrio, &channelPrios,
2161 &selCh, &lowPrio, &idx] (audio::SfxChannel &ch)
2162 {
2163 dfloat const chPriority = channelPrios[idx++];
2164
2165 if (ch.hasBuffer())
2166 {
2167 sfxbuffer_t &sbuf = ch.buffer();
2168 if ((sbuf.flags & SFXBF_PLAYING))
2169 {
2170 DENG2_ASSERT(sbuf.sample != nullptr);
2171
2172 if (sbuf.sample->id == sample->id &&
2173 (myPrio >= chPriority && (!selCh || chPriority <= lowPrio)))
2174 {
2175 selCh = &ch;
2176 lowPrio = chPriority;
2177 }
2178 }
2179 }
2180
2181 return LoopContinue;
2182 });
2183
2184 if (!selCh)
2185 {
2186 // The new sound can't be played because we were unable to stop
2187 // enough channels to accommodate the limitation.
2188 LOG_AUDIO_XVERBOSE("Not playing soundId:%i because all channels are busy",
2189 sample->id);
2190 return false;
2191 }
2192
2193 // Stop this one.
2194 count--;
2195 selCh->stop();
2196 }
2197 }
2198
2199 // Hit count tells how many times the cached sound has been used.
2200 d->sfxSampleCache.hit(sample->id);
2201
2202 /*
2203 * Pick a channel for the sound. We will do our best to play the sound,
2204 * cancelling existing ones if need be. The ideal choice is a free channel
2205 * that is already loaded with the sample, in the correct format and mode.
2206 */
2207 allowSfxRefresh(false);
2208
2209 // First look through the stopped channels. At this stage we're very picky:
2210 // only the perfect choice will be good enough.
2211 audio::SfxChannel *selCh = d->sfxChannels->tryFindVacant(play3D, sample->bytesPer,
2212 sample->rate, sample->id);
2213
2214 if (!selCh)
2215 {
2216 // Perhaps there is a vacant channel (with any sample, but preferably one
2217 // with no sample already loaded).
2218 selCh = d->sfxChannels->tryFindVacant(play3D, sample->bytesPer, sample->rate, 0);
2219 }
2220
2221 if (!selCh)
2222 {
2223 // Try any non-playing channel in the correct format.
2224 selCh = d->sfxChannels->tryFindVacant(play3D, sample->bytesPer, sample->rate, -1);
2225 }
2226
2227 if (!selCh)
2228 {
2229 // A perfect channel could not be found.
2230 // We must use a channel with the wrong format or decide which one of the
2231 // playing ones gets stopped.
2232
2233 if (!haveChannelPrios)
2234 {
2235 d->getSfxChannelPriorities(channelPrios);
2236 }
2237
2238 // All channels with a priority less than or equal to ours can be stopped.
2239 audio::SfxChannel *prioCh = nullptr;
2240 dint idx = 0;
2241 d->sfxChannels->forAll([&play3D, &myPrio, &channelPrios,
2242 &selCh, &prioCh, &lowPrio, &idx] (audio::SfxChannel &ch)
2243 {
2244 dfloat const chPriority = channelPrios[idx++];
2245
2246 if (ch.hasBuffer())
2247 {
2248 // Sample buffer must be configured for the right mode.
2249 sfxbuffer_t &sbuf = ch.buffer();
2250 if (play3D == ((sbuf.flags & SFXBF_3D) != 0))
2251 {
2252 if (!(sbuf.flags & SFXBF_PLAYING))
2253 {
2254 // This channel is not playing, we'll take it!
2255 selCh = &ch;
2256 return LoopAbort;
2257 }
2258
2259 // Are we more important than this sound?
2260 // We want to choose the lowest priority sound.
2261 if (myPrio >= chPriority && (!prioCh || chPriority <= lowPrio))
2262 {
2263 prioCh = &ch;
2264 lowPrio = chPriority;
2265 }
2266 }
2267 }
2268
2269 return LoopContinue;
2270 });
2271
2272 // If a good low-priority channel was found, use it.
2273 if (prioCh)
2274 {
2275 selCh = prioCh;
2276 selCh->stop();
2277 }
2278 }
2279
2280 if (!selCh)
2281 {
2282 // A suitable channel was not found.
2283 allowSfxRefresh(true);
2284 LOG_AUDIO_XVERBOSE("Failed to find suitable channel for sample id:%i", sample->id);
2285 return false;
2286 }
2287
2288 DENG2_ASSERT(selCh->hasBuffer());
2289 // The sample buffer may need to be reformatted.
2290
2291 if (selCh->buffer().rate != sample->rate ||
2292 selCh->buffer().bytes != sample->bytesPer)
2293 {
2294 // Create a new sample buffer with the correct format.
2295 sfx()->Destroy(&selCh->buffer());
2296 selCh->setBuffer(sfx()->Create(play3D ? SFXBF_3D : 0, sample->bytesPer * 8, sample->rate));
2297 }
2298 sfxbuffer_t &sbuf = selCh->buffer();
2299
2300 // Configure buffer flags.
2301 sbuf.flags &= ~(SFXBF_REPEAT | SFXBF_DONT_STOP);
2302 if (flags & SF_REPEAT) sbuf.flags |= SFXBF_REPEAT;
2303 if (flags & SF_DONT_STOP) sbuf.flags |= SFXBF_DONT_STOP;
2304
2305 // Init the channel information.
2306 selCh->setFlags(selCh->flags() & ~(SFXCF_NO_ORIGIN | SFXCF_NO_ATTENUATION | SFXCF_NO_UPDATE));
2307 selCh->setVolume(volume);
2308 selCh->setFrequency(freq);
2309
2310 if (!emitter && !fixedOrigin)
2311 {
2312 selCh->setFlags(selCh->flags() | SFXCF_NO_ORIGIN);
2313 selCh->setEmitter(nullptr);
2314 }
2315 else
2316 {
2317 selCh->setEmitter(emitter);
2318 if (fixedOrigin)
2319 {
2320 selCh->setFixedOrigin(Vector3d(fixedOrigin));
2321 }
2322 }
2323
2324 if (flags & SF_NO_ATTENUATION)
2325 {
2326 // The sound can be heard from any distance.
2327 selCh->setFlags(selCh->flags() | SFXCF_NO_ATTENUATION);
2328 }
2329
2330 /**
2331 * Load in the sample. Must load prior to setting properties, because the audio driver
2332 * might actually create the real buffer only upon loading.
2333 *
2334 * @note The sample is not reloaded if a sample with the same ID is already loaded on
2335 * the channel.
2336 */
2337 if (!sbuf.sample || sbuf.sample->id != sample->id)
2338 {
2339 sfx()->Load(&sbuf, sample);
2340 }
2341
2342 // Update channel properties.
2343 selCh->updatePriority();
2344
2345 // 3D sounds need a few extra properties set up.
2346 if (play3D)
2347 {
2348 // Init the buffer's min/max distances.
2349 // This is only done once, when the sound is started (i.e., here).
2350 dfloat const minDist = (selCh->flags() & SFXCF_NO_ATTENUATION) ? 10000 : ::soundMinDist;
2351 dfloat const maxDist = (selCh->flags() & SFXCF_NO_ATTENUATION) ? 20000 : ::soundMaxDist;
2352
2353 sfx()->Set(&sbuf, SFXBP_MIN_DISTANCE, minDist);
2354 sfx()->Set(&sbuf, SFXBP_MAX_DISTANCE, maxDist);
2355 }
2356
2357 // This'll commit all the deferred properties.
2358 sfx()->Listener(SFXLP_UPDATE, 0);
2359
2360 // Start playing.
2361 sfx()->Play(&sbuf);
2362
2363 allowSfxRefresh();
2364
2365 // Take note of the start time.
2366 selCh->setStartTime(nowTime);
2367
2368 // Sound successfully started.
2369 return true;
2370 }
2371
rateSoundPriority(mobj_t const * emitter,coord_t const * point,dfloat volume,dint startTic)2372 dfloat AudioSystem::rateSoundPriority(mobj_t const *emitter, coord_t const *point, dfloat volume,
2373 dint startTic)
2374 {
2375 // In five seconds all priority of a sound is gone.
2376 dfloat const timeoff = 1000 * (Timer_Ticks() - startTic) / (5.0f * TICSPERSEC);
2377
2378 if (!d->sfxListener || (!emitter && !point))
2379 {
2380 // The sound does not have an origin.
2381 return 1000 * volume - timeoff;
2382 }
2383
2384 // The sound has an origin, base the points on distance.
2385 coord_t const *origin;
2386 if (emitter)
2387 {
2388 origin = emitter->origin;
2389 }
2390 else
2391 {
2392 // No emitter mobj, use the fixed source position.
2393 origin = point;
2394 }
2395
2396 return 1000 * volume - Mobj_ApproxPointDistance(d->sfxListener, origin) / 2 - timeoff;
2397 }
2398
sfx() const2399 audiointerface_sfx_generic_t *AudioSystem::sfx() const
2400 {
2401 // The primary interface is the first one.
2402 audiointerface_sfx_generic_t *found = nullptr;
2403 d->forAllInterfaces(AUDIO_ISFX, [&found] (void *ifs)
2404 {
2405 found = (audiointerface_sfx_generic_t *)ifs;
2406 return LoopAbort;
2407 });
2408 return found;
2409 }
2410
cd() const2411 audiointerface_cd_t *AudioSystem::cd() const
2412 {
2413 // The primary interface is the first one.
2414 audiointerface_cd_t *found = nullptr;
2415 d->forAllInterfaces(AUDIO_ICD, [&found] (void *ifs)
2416 {
2417 found = (audiointerface_cd_t *)ifs;
2418 return LoopAbort;
2419 });
2420 return found;
2421 }
2422
toDriverId(AudioDriver const * driver) const2423 audiodriverid_t AudioSystem::toDriverId(AudioDriver const *driver) const
2424 {
2425 if (driver && driver >= &d->drivers[0] && driver <= &d->drivers[AUDIODRIVER_COUNT])
2426 {
2427 return audiodriverid_t( driver - d->drivers );
2428 }
2429 return AUDIOD_INVALID;
2430 }
2431 #endif
2432
sfxSampleCache() const2433 audio::SfxSampleCache /*const*/ &AudioSystem::sfxSampleCache() const
2434 {
2435 return d->sfxSampleCache;
2436 }
2437
2438 #ifdef __CLIENT__
hasSfxChannels()2439 bool AudioSystem::hasSfxChannels()
2440 {
2441 return bool( d->sfxChannels );
2442 }
2443
sfxChannels() const2444 audio::SfxChannels &AudioSystem::sfxChannels() const
2445 {
2446 DENG2_ASSERT(d->sfxChannels.get() != nullptr);
2447 return *d->sfxChannels;
2448 }
2449
allowSfxRefresh(bool allow)2450 void AudioSystem::allowSfxRefresh(bool allow)
2451 {
2452 if (!d->sfxAvail) return;
2453 if (allowRefresh == allow) return; // No change.
2454
2455 allowRefresh = allow;
2456
2457 // If we're denying refresh, let's make sure that if it's currently
2458 // running, we don't continue until it has stopped.
2459 if (!allow)
2460 {
2461 while (refreshing)
2462 {
2463 Sys_Sleep(0);
2464 }
2465 }
2466
2467 // Sys_SuspendThread(::refreshHandle, !allow);
2468 }
2469
requestSfxListenerUpdate()2470 void AudioSystem::requestSfxListenerUpdate()
2471 {
2472 // Request a listener reverb update at the end of the frame.
2473 d->sfxListenerSubsector = nullptr;
2474 }
2475
2476 #endif // __CLIENT__
2477
startLogical(dint soundIdAndFlags,mobj_t const * emitter)2478 void AudioSystem::startLogical(dint soundIdAndFlags, mobj_t const *emitter)
2479 {
2480 d->sfxStartLogical(soundIdAndFlags, emitter);
2481 }
2482
aboutToUnloadMap()2483 void AudioSystem::aboutToUnloadMap()
2484 {
2485 LOG_AS("AudioSystem");
2486 LOG_AUDIO_VERBOSE("Cleaning for map unload...");
2487
2488 d->sfxClearLogical();
2489
2490 #ifdef __CLIENT__
2491 // Mobjs are about to be destroyed so stop all sound channels using one as an emitter.
2492 d->sfxChannels->forAll([] (audio::SfxChannel &ch)
2493 {
2494 if (ch.emitter())
2495 {
2496 ch.setEmitter(nullptr);
2497 ch.stop();
2498 }
2499 return LoopContinue;
2500 });
2501
2502 // Sectors, too, for that matter.
2503 d->sfxListenerSubsector = nullptr;
2504 #endif
2505 }
2506
2507 #ifdef __CLIENT__
worldMapChanged()2508 void AudioSystem::worldMapChanged()
2509 {
2510 // Update who is listening now.
2511 setSfxListener(S_GetListenerMobj());
2512 }
2513 #endif
2514
2515 /**
2516 * Console command for playing a (local) sound effect.
2517 */
D_CMD(PlaySound)2518 D_CMD(PlaySound)
2519 {
2520 DENG2_UNUSED(src);
2521
2522 if (argc < 2)
2523 {
2524 LOG_SCR_NOTE("Usage: %s (id) (volume) at (x) (y) (z)") << argv[0];
2525 LOG_SCR_MSG("(volume) must be in 0..1, but may be omitted");
2526 LOG_SCR_MSG("'at (x) (y) (z)' may also be omitted");
2527 LOG_SCR_MSG("The sound is always played locally");
2528 return true;
2529 }
2530 dint p = 0;
2531
2532 // The sound ID is always first.
2533 dint const id = DED_Definitions()->getSoundNum(argv[1]);
2534
2535 // The second argument may be a volume.
2536 dfloat volume = 1;
2537 if (argc >= 3 && String(argv[2]).compareWithoutCase("at"))
2538 {
2539 volume = String(argv[2]).toFloat();
2540 p = 3;
2541 }
2542 else
2543 {
2544 p = 2;
2545 }
2546
2547 bool useFixedPos = false;
2548 coord_t fixedPos[3];
2549 if (argc >= p + 4 && !String(argv[p]).compareWithoutCase("at"))
2550 {
2551 useFixedPos = true;
2552 fixedPos[0] = String(argv[p + 1]).toDouble();
2553 fixedPos[1] = String(argv[p + 2]).toDouble();
2554 fixedPos[2] = String(argv[p + 3]).toDouble();
2555 }
2556
2557 // Check that the volume is valid.
2558 volume = de::clamp(0.f, volume, 1.f);
2559 if (de::fequal(volume, 0)) return true;
2560
2561 if (useFixedPos)
2562 {
2563 _api_S.LocalSoundAtVolumeFrom(id, nullptr, fixedPos, volume);
2564 }
2565 else
2566 {
2567 _api_S.LocalSoundAtVolume(id, nullptr, volume);
2568 }
2569
2570 return true;
2571 }
2572
2573 #ifdef __CLIENT__
2574
2575 /**
2576 * CCmd: Play a music track.
2577 */
D_CMD(PlayMusic)2578 D_CMD(PlayMusic)
2579 {
2580 DENG2_UNUSED(src);
2581
2582 LOG_AS("playmusic (Cmd)");
2583
2584 if (!App_AudioSystem().musicIsAvailable())
2585 {
2586 LOGDEV_SCR_ERROR("Music subsystem is not available");
2587 return false;
2588 }
2589
2590 bool const looped = true;
2591
2592 if (argc == 2)
2593 {
2594 // Play a file associated with the referenced music definition.
2595 if (Record const *definition = DED_Definitions()->musics.tryFind("id", argv[1]))
2596 {
2597 return Mus_Start(*definition, looped);
2598 }
2599 LOG_RES_WARNING("Music '%s' not defined") << argv[1];
2600 return false;
2601 }
2602
2603 if (argc == 3)
2604 {
2605 // Play a file referenced directly.
2606 if (!qstricmp(argv[1], "lump"))
2607 {
2608 return Mus_StartLump(App_FileSystem().lumpNumForName(argv[2]), looped);
2609 }
2610 else if (!qstricmp(argv[1], "file"))
2611 {
2612 return Mus_StartFile(argv[2], looped);
2613 }
2614 else if (!qstricmp(argv[1], "cd"))
2615 {
2616 if (!App_AudioSystem().cd())
2617 {
2618 LOG_AUDIO_WARNING("No CD audio interface available");
2619 return false;
2620 }
2621 return Mus_StartCDTrack(String(argv[2]).toInt(), looped);
2622 }
2623 return false;
2624 }
2625
2626 LOG_SCR_NOTE("Usage:\n %s (music-def)") << argv[0];
2627 LOG_SCR_MSG(" %s lump (lumpname)") << argv[0];
2628 LOG_SCR_MSG(" %s file (filename)") << argv[0];
2629 LOG_SCR_MSG(" %s cd (track)") << argv[0];
2630 return true;
2631 }
2632
D_CMD(StopMusic)2633 D_CMD(StopMusic)
2634 {
2635 DENG2_UNUSED3(src, argc, argv);
2636
2637 App_AudioSystem().stopMusic();
2638 return true;
2639 }
2640
D_CMD(PauseMusic)2641 D_CMD(PauseMusic)
2642 {
2643 DENG2_UNUSED3(src, argc, argv);
2644
2645 App_AudioSystem().pauseMusic(!App_AudioSystem().musicIsPaused());
2646 return true;
2647 }
2648
sfxReverbStrengthChanged()2649 static void sfxReverbStrengthChanged()
2650 {
2651 App_AudioSystem().requestSfxListenerUpdate();
2652 }
2653
musicMidiFontChanged()2654 static void musicMidiFontChanged()
2655 {
2656 App_AudioSystem().updateMusicMidiFont();
2657 }
2658
D_CMD(ReverbParameters)2659 D_CMD(ReverbParameters)
2660 {
2661 DENG2_UNUSED2(src, argc);
2662
2663 dfloat args[NUM_REVERB_DATA];
2664
2665 args[SFXLP_REVERB_VOLUME ] = String(argv[1]).toFloat();
2666 args[SFXLP_REVERB_SPACE ] = String(argv[2]).toFloat();
2667 args[SFXLP_REVERB_DECAY ] = String(argv[3]).toFloat();
2668 args[SFXLP_REVERB_DAMPING] = String(argv[4]).toFloat();
2669
2670 LOG_SCR_MSG("Setting reverb parameters:\n"
2671 "- volume: %f\n"
2672 "- space: %f\n"
2673 "- decay: %f\n"
2674 "- damping: %f")
2675 << args[SFXLP_REVERB_VOLUME ]
2676 << args[SFXLP_REVERB_SPACE ]
2677 << args[SFXLP_REVERB_DECAY ]
2678 << args[SFXLP_REVERB_DAMPING];
2679
2680 App_AudioSystem().sfx()->Listenerv(SFXLP_REVERB, args);
2681
2682 return true;
2683 }
2684 #endif
2685
consoleRegister()2686 void AudioSystem::consoleRegister() // static
2687 {
2688 // Sound effects:
2689 #ifdef __CLIENT__
2690 //C_VAR_INT ("sound-16bit", &sfx16Bit, 0, 0, 1);
2691 C_VAR_INT ("sound-3d", &sfx3D, 0, 0, 1);
2692 #endif
2693 C_VAR_BYTE ("sound-overlap-stop", &sfxOneSoundPerEmitter, 0, 0, 1);
2694 #ifdef __CLIENT__
2695 //C_VAR_INT ("sound-rate", &sfxSampleRate, 0, 11025, 44100);
2696 C_VAR_FLOAT2 ("sound-reverb-volume", &sfxReverbStrength, 0, 0, 1.5f, sfxReverbStrengthChanged);
2697 C_VAR_INT ("sound-volume", &sfxVolume, 0, 0, 255);
2698
2699 C_CMD_FLAGS("playsound", nullptr, PlaySound, CMDF_NO_DEDICATED);
2700
2701 // Music:
2702 C_VAR_CHARPTR2("music-soundfont", &musMidiFontPath, 0, 0, 0, musicMidiFontChanged);
2703 C_VAR_INT ("music-source", &musSourcePreference, 0, 0, 2);
2704 C_VAR_INT ("music-volume", &musVolume, 0, 0, 255);
2705
2706 C_CMD_FLAGS("pausemusic", nullptr, PauseMusic, CMDF_NO_DEDICATED);
2707 C_CMD_FLAGS("playmusic", nullptr, PlayMusic, CMDF_NO_DEDICATED);
2708 C_CMD_FLAGS("stopmusic", "", StopMusic, CMDF_NO_DEDICATED);
2709
2710 C_CMD("reverbparams", "ffff", ReverbParameters);
2711
2712 // Debug:
2713 C_VAR_INT ("sound-info", &showSoundInfo, 0, 0, 1);
2714 #endif
2715 }
2716
2717 // Music: ---------------------------------------------------------------------------
2718
Mus_IsPlaying()2719 bool Mus_IsPlaying()
2720 {
2721 #ifdef __CLIENT__
2722 return App_AudioSystem().musicIsPlaying();
2723 #else
2724 return false;
2725 #endif
2726 }
2727
2728 #undef S_StopMusic
S_StopMusic()2729 void S_StopMusic()
2730 {
2731 #ifdef __CLIENT__
2732 App_AudioSystem().stopMusic();
2733 #endif
2734 }
2735
2736 #undef S_PauseMusic
S_PauseMusic(dd_bool paused)2737 void S_PauseMusic(dd_bool paused)
2738 {
2739 #ifdef __CLIENT__
2740 App_AudioSystem().pauseMusic(paused);
2741 #else
2742 DENG2_UNUSED(paused);
2743 #endif
2744 }
2745
Mus_Start(Record const & definition,bool looped)2746 dint Mus_Start(Record const &definition, bool looped)
2747 {
2748 #ifdef __CLIENT__
2749 return App_AudioSystem().playMusic(definition, looped);
2750 #else
2751 DENG2_UNUSED2(definition, looped);
2752 return 0;
2753 #endif
2754 }
2755
Mus_StartLump(lumpnum_t lumpNum,bool looped)2756 dint Mus_StartLump(lumpnum_t lumpNum, bool looped)
2757 {
2758 #ifdef __CLIENT__
2759 return App_AudioSystem().playMusicLump(lumpNum, looped);
2760 #else
2761 DENG2_UNUSED2(lumpNum, looped);
2762 return 0;
2763 #endif
2764 }
2765
Mus_StartFile(char const * filePath,bool looped)2766 dint Mus_StartFile(char const *filePath, bool looped)
2767 {
2768 #ifdef __CLIENT__
2769 return App_AudioSystem().playMusicFile(filePath, looped);
2770 #else
2771 DENG2_UNUSED2(filePath, looped);
2772 return 0;
2773 #endif
2774 }
2775
Mus_StartCDTrack(dint cdTrack,bool looped)2776 dint Mus_StartCDTrack(dint cdTrack, bool looped)
2777 {
2778 #ifdef __CLIENT__
2779 return App_AudioSystem().playMusicCDTrack(cdTrack, looped);
2780 #else
2781 DENG2_UNUSED2(cdTrack, looped);
2782 return 0;
2783 #endif
2784 }
2785
2786 #undef S_StartMusicNum
S_StartMusicNum(dint musicId,dd_bool looped)2787 dint S_StartMusicNum(dint musicId, dd_bool looped)
2788 {
2789 #ifdef __CLIENT__
2790 if (::isDedicated) return true;
2791
2792 if (musicId >= 0 && musicId < DED_Definitions()->musics.size())
2793 {
2794 const Record &def = DED_Definitions()->musics[musicId];
2795 return Mus_Start(def, looped);
2796 }
2797 return false;
2798 #else
2799 DENG2_UNUSED2(musicId, looped);
2800 return false;
2801 #endif
2802 }
2803
2804 #undef S_StartMusic
S_StartMusic(char const * musicId,dd_bool looped)2805 dint S_StartMusic(char const *musicId, dd_bool looped)
2806 {
2807 LOG_AS("S_StartMusic");
2808 dint idx = DED_Definitions()->getMusicNum(musicId);
2809 if (idx < 0)
2810 {
2811 if (musicId && !String(musicId).isEmpty())
2812 {
2813 #if defined __CLIENT__
2814 // Fallback: maybe there's a lump with this name instead.
2815 const String musicLumpName = String::format("d_%s.lmp", musicId);
2816 const lumpnum_t lumpNum = App_FileSystem().lumpNumForName(musicLumpName);
2817 if (lumpNum >= 0)
2818 {
2819 LOG_AUDIO_MSG("No Music definition for \"%s\", but found lump \"%s\" (%d) instead")
2820 << musicId << musicLumpName << lumpNum;
2821 if (const auto result = App_AudioSystem().playMusicLump(lumpNum, looped))
2822 {
2823 return result;
2824 }
2825 }
2826 #endif // __CLIENT__
2827 LOG_AUDIO_WARNING("Music \"%s\" not defined, cannot start playback") << musicId;
2828 }
2829 return false;
2830 }
2831 return S_StartMusicNum(idx, looped);
2832 }
2833
2834 // Sound Effects: -------------------------------------------------------------------
2835
S_GetListenerMobj()2836 mobj_t *S_GetListenerMobj()
2837 {
2838 return DD_Player(::displayPlayer)->publicData().mo;
2839 }
2840
2841 #undef S_LocalSoundAtVolumeFrom
S_LocalSoundAtVolumeFrom(dint soundIdAndFlags,mobj_t const * origin,coord_t * point,dfloat volume)2842 dint S_LocalSoundAtVolumeFrom(dint soundIdAndFlags, mobj_t const *origin, coord_t *point, dfloat volume)
2843 {
2844 #ifdef __CLIENT__
2845 LOG_AS("S_LocalSoundAtVolumeFrom");
2846
2847 // A dedicated server never starts any local sounds (only logical sounds in the LSM).
2848 if (::isDedicated) return false;
2849
2850 // Sounds cannot be started while in busy mode...
2851 if (DoomsdayApp::app().busyMode().isActive())
2852 return false;
2853
2854 dint const soundId = (soundIdAndFlags & ~DDSF_FLAG_MASK);
2855 if (soundId <= 0 || soundId >= DED_Definitions()->sounds.size())
2856 return false;
2857
2858 // Skip if sounds won't be heard.
2859 if (::sfxVolume <= 0 || volume <= 0)
2860 return false;
2861
2862 if (volume > 1)
2863 {
2864 LOGDEV_AUDIO_WARNING("Volume is too high (%f > 1)") << volume;
2865 }
2866
2867 dfloat freq = 1;
2868 // This is the sound we're going to play.
2869 sfxinfo_t *info = Def_GetSoundInfo(soundId, &freq, &volume);
2870 if (!info) return false; // Hmm? This ID is not defined.
2871
2872 bool const isRepeating = (soundIdAndFlags & DDSF_REPEAT) || Def_SoundIsRepeating(soundId);
2873
2874 // Check the distance (if applicable).
2875 if (!(info->flags & SF_NO_ATTENUATION) && !(soundIdAndFlags & DDSF_NO_ATTENUATION))
2876 {
2877 // If origin is too far, don't even think about playing the sound.
2878 coord_t const *fixPoint = (origin ? origin->origin : point);
2879
2880 if (Mobj_ApproxPointDistance(S_GetListenerMobj(), fixPoint) > soundMaxDist)
2881 return false;
2882 }
2883
2884 // Load the sample.
2885 sfxsample_t *sample = App_AudioSystem().sfxSampleCache().cache(soundId);
2886 if (!sample)
2887 {
2888 if (App_AudioSystem().sfxIsAvailable())
2889 {
2890 LOG_AUDIO_VERBOSE("Caching of sound %i failed") << soundId;
2891 }
2892 return false;
2893 }
2894
2895 // Random frequency alteration? (Multipliers chosen to match original
2896 // sound code.)
2897 if (!sfxNoRndPitch)
2898 {
2899 if (info->flags & SF_RANDOM_SHIFT)
2900 {
2901 freq += (RNG_RandFloat() - RNG_RandFloat()) * (7.0f / 255);
2902 }
2903 if (info->flags & SF_RANDOM_SHIFT2)
2904 {
2905 freq += (RNG_RandFloat() - RNG_RandFloat()) * (15.0f / 255);
2906 }
2907 }
2908
2909 // If the sound has an exclusion group, either all or the same emitter's
2910 // iterations of this sound will stop.
2911 if (info->group)
2912 {
2913 mobj_t const *emitter = ((info->flags & SF_GLOBAL_EXCLUDE) ? nullptr : origin);
2914 S_StopSoundGroup(info->group, emitter);
2915 }
2916
2917 // Let's play it.
2918 dint flags = 0;
2919 flags |= (((info->flags & SF_NO_ATTENUATION) || (soundIdAndFlags & DDSF_NO_ATTENUATION)) ? SF_NO_ATTENUATION : 0);
2920 flags |= (isRepeating ? SF_REPEAT : 0);
2921 flags |= ((info->flags & SF_DONT_STOP) ? SF_DONT_STOP : 0);
2922 return App_AudioSystem().playSound(sample, volume, freq, origin, point, flags);
2923
2924 #else
2925 DENG2_UNUSED4(soundIdAndFlags, origin, point, volume);
2926 return false;
2927 #endif
2928 }
2929
2930 #undef S_LocalSoundAtVolume
S_LocalSoundAtVolume(dint soundIdAndFlags,mobj_t const * emitter,dfloat volume)2931 dint S_LocalSoundAtVolume(dint soundIdAndFlags, mobj_t const *emitter, dfloat volume)
2932 {
2933 return S_LocalSoundAtVolumeFrom(soundIdAndFlags, emitter, nullptr, volume);
2934 }
2935
2936 #undef S_LocalSound
S_LocalSound(dint soundIdAndFlags,mobj_t const * emitter)2937 dint S_LocalSound(dint soundIdAndFlags, mobj_t const *emitter)
2938 {
2939 // Play local sound at max volume.
2940 return S_LocalSoundAtVolumeFrom(soundIdAndFlags, emitter, nullptr, 1);
2941 }
2942
2943 #undef S_LocalSoundFrom
S_LocalSoundFrom(dint soundIdAndFlags,coord_t * origin)2944 dint S_LocalSoundFrom(dint soundIdAndFlags, coord_t *origin)
2945 {
2946 return S_LocalSoundAtVolumeFrom(soundIdAndFlags, nullptr, origin, 1);
2947 }
2948
2949 #undef S_StartSound
S_StartSound(dint soundIdAndFlags,mobj_t const * emitter)2950 dint S_StartSound(dint soundIdAndFlags, mobj_t const *emitter)
2951 {
2952 #ifdef __SERVER__
2953 // The sound is audible to everybody.
2954 Sv_Sound(soundIdAndFlags, emitter, SVSF_TO_ALL);
2955 #endif
2956 App_AudioSystem().startLogical(soundIdAndFlags, emitter);
2957
2958 return S_LocalSound(soundIdAndFlags, emitter);
2959 }
2960
2961 #undef S_StartSoundEx
S_StartSoundEx(dint soundIdAndFlags,mobj_t const * emitter)2962 dint S_StartSoundEx(dint soundIdAndFlags, mobj_t const *emitter)
2963 {
2964 #ifdef __SERVER__
2965 Sv_Sound(soundIdAndFlags, emitter, SVSF_TO_ALL | SVSF_EXCLUDE_ORIGIN);
2966 #endif
2967 App_AudioSystem().startLogical(soundIdAndFlags, emitter);
2968
2969 return S_LocalSound(soundIdAndFlags, emitter);
2970 }
2971
2972 #undef S_StartSoundAtVolume
S_StartSoundAtVolume(dint soundIdAndFlags,mobj_t const * emitter,dfloat volume)2973 dint S_StartSoundAtVolume(dint soundIdAndFlags, mobj_t const *emitter, dfloat volume)
2974 {
2975 #ifdef __SERVER__
2976 Sv_SoundAtVolume(soundIdAndFlags, emitter, volume, SVSF_TO_ALL);
2977 #endif
2978 App_AudioSystem().startLogical(soundIdAndFlags, emitter);
2979
2980 // The sound is audible to everybody.
2981 return S_LocalSoundAtVolume(soundIdAndFlags, emitter, volume);
2982 }
2983
2984 #undef S_ConsoleSound
S_ConsoleSound(dint soundId,mobj_t * emitter,dint targetConsole)2985 dint S_ConsoleSound(dint soundId, mobj_t *emitter, dint targetConsole)
2986 {
2987 #ifdef __SERVER__
2988 Sv_Sound(soundId, emitter, targetConsole);
2989 #endif
2990
2991 // If it's for us, we can hear it.
2992 if (targetConsole == consolePlayer)
2993 {
2994 S_LocalSound(soundId, emitter);
2995 }
2996
2997 return true;
2998 }
2999
3000 #undef S_StopSound
S_StopSound(dint soundId,mobj_t const * emitter)3001 void S_StopSound(dint soundId, mobj_t const *emitter)
3002 {
3003 App_AudioSystem().stopSound(soundId, emitter);
3004 }
3005
3006 #undef S_StopSound2
S_StopSound2(dint soundId,mobj_t const * emitter,dint flags)3007 void S_StopSound2(dint soundId, mobj_t const *emitter, dint flags)
3008 {
3009 App_AudioSystem().stopSound(soundId, emitter, flags);
3010 }
3011
3012 #undef S_IsPlaying
S_IsPlaying(dint soundId,mobj_t * emitter)3013 dint S_IsPlaying(dint soundId, mobj_t *emitter)
3014 {
3015 return (dint) App_AudioSystem().soundIsPlaying(soundId, emitter);
3016 }
3017
3018 #ifdef __CLIENT__
3019
S_StopSoundGroup(dint group,mobj_t const * emitter)3020 void S_StopSoundGroup(dint group, mobj_t const *emitter)
3021 {
3022 App_AudioSystem().stopSoundGroup(group, emitter);
3023 }
3024
S_StopSoundWithLowerPriority(dint soundId,mobj_t * emitter,dint defPriority)3025 dint S_StopSoundWithLowerPriority(dint soundId, mobj_t *emitter, dint defPriority)
3026 {
3027 return App_AudioSystem().stopSoundWithLowerPriority(soundId, emitter, defPriority);
3028 }
3029
Function_Audio_LocalSound(Context &,const Function::ArgumentValues & args)3030 static Value *Function_Audio_LocalSound(Context &, const Function::ArgumentValues &args)
3031 {
3032 const int sound = DED_Definitions()->getSoundNum(args.at(0)->asText());
3033 const float volume = float(args.at(1)->asNumber());
3034 if (sound >= 0)
3035 {
3036 S_LocalSoundAtVolume(sound, nullptr, volume);
3037 }
3038 else
3039 {
3040 throw Error("Function_Thing_StartSound", "Undefined sound: " + args.at(0)->asText());
3041 }
3042 return nullptr;
3043 }
3044
3045 #endif // __CLIENT__
3046
3047 DENG_DECLARE_API(S) =
3048 {
3049 { DE_API_SOUND },
3050 S_LocalSoundAtVolumeFrom,
3051 S_LocalSoundAtVolume,
3052 S_LocalSound,
3053 S_LocalSoundFrom,
3054 S_StartSound,
3055 S_StartSoundEx,
3056 S_StartSoundAtVolume,
3057 S_ConsoleSound,
3058 S_StopSound,
3059 S_StopSound2,
3060 S_IsPlaying,
3061 S_StartMusic,
3062 S_StartMusicNum,
3063 S_StopMusic,
3064 S_PauseMusic
3065 };
3066