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