LCOV - code coverage report
Current view: top level - engine/interface - sound.cpp (source / functions) Hit Total Coverage
Test: Libprimis Test Coverage Lines: 0 613 0.0 %
Date: 2024-11-22 05:07:59 Functions: 0 77 0.0 %

          Line data    Source code
       1             : // sound.cpp: basic positional sound using sdl_mixer
       2             : 
       3             : #include "../libprimis-headers/cube.h"
       4             : #include "../libprimis-headers/sound.h"
       5             : #include "SDL_mixer.h"
       6             : 
       7             : #include "console.h"
       8             : #include "control.h"
       9             : #include "cs.h"
      10             : #include "input.h"
      11             : #include "menus.h"
      12             : 
      13             : #include "render/rendergl.h" //needed to get camera position
      14             : 
      15             : #include "world/entities.h"
      16             : #include "world/world.h"
      17             : 
      18           0 : SoundEngine::SoundEngine() : gamesounds("game/", *this), mapsounds("mapsound/", *this)
      19             : {
      20           0 : }
      21             : 
      22           0 : SoundEngine::SoundSample::SoundSample(SoundEngine& p) : parent(&p), name(""), chunk(nullptr)
      23             : {
      24           0 : }
      25             : 
      26           0 : SoundEngine::SoundSample::~SoundSample()
      27             : {
      28           0 : }
      29             : 
      30           0 : void SoundEngine::SoundSample::cleanup()
      31             : {
      32           0 :     if(chunk)
      33             :     {
      34           0 :         Mix_FreeChunk(chunk);
      35           0 :         chunk = nullptr;
      36             :     }
      37           0 : }
      38             : 
      39           0 : bool SoundEngine::SoundConfig::hasslot(const soundslot *p, const std::vector<soundslot> &v) const
      40             : {
      41           0 :     return p >= v.data() + slots && p < v.data() + slots+numslots && slots+numslots < static_cast<long>(v.size());
      42             : }
      43             : 
      44           0 : int SoundEngine::SoundConfig::chooseslot(int flags) const
      45             : {
      46           0 :     if(flags&Music_NoAlt || numslots <= 1)
      47             :     {
      48           0 :         return slots;
      49             :     }
      50           0 :     if(flags&Music_UseAlt)
      51             :     {
      52           0 :         return slots + 1 + randomint(numslots - 1);
      53             :     }
      54           0 :     return slots + randomint(numslots);
      55             : }
      56             : 
      57           0 : SoundEngine::SoundChannel::SoundChannel(int id, SoundEngine& p) : parent(&p), id(id)
      58             : {
      59           0 :     reset();
      60           0 : }
      61             : 
      62           0 : bool SoundEngine::SoundChannel::hasloc() const
      63             : {
      64           0 :     return loc.x >= -1e15f;
      65             : }
      66             : 
      67           0 : void SoundEngine::SoundChannel::clearloc()
      68             : {
      69           0 :     loc = vec(-1e16f, -1e16f, -1e16f);
      70           0 : }
      71             : 
      72           0 : void SoundEngine::SoundChannel::reset()
      73             : {
      74           0 :     inuse  = false;
      75           0 :     clearloc();
      76           0 :     slot   = nullptr;
      77           0 :     ent    = nullptr;
      78           0 :     radius = 0;
      79           0 :     volume = -1;
      80           0 :     pan    = -1;
      81           0 :     flags  = 0;
      82           0 :     dirty  = false;
      83           0 : }
      84             : 
      85           0 : void SoundEngine::SoundChannel::setloc(const vec& newloc)
      86             : {
      87           0 :     loc = newloc;
      88           0 : }
      89             : 
      90           0 : void SoundEngine::SoundChannel::setupchannel(int newn, soundslot *newslot, const vec *newloc, extentity *newent, int newflags, int newradius)
      91             : {
      92           0 :     reset();
      93           0 :     inuse = true;
      94           0 :     if(newloc)
      95             :     {
      96           0 :         loc = *newloc;
      97             :     }
      98           0 :     slot = newslot;
      99           0 :     ent = newent;
     100           0 :     flags = 0;
     101           0 :     radius = newradius;
     102           0 : }
     103             : 
     104             : //creates a new SoundChannel object with passed properties
     105           0 : SoundEngine::SoundChannel& SoundEngine::newchannel(int n, soundslot *slot, const vec *loc, extentity *ent, int flags, int radius)
     106             : {
     107           0 :     if(ent)
     108             :     {
     109           0 :         loc = &ent->o;
     110           0 :         ent->flags |= EntFlag_Sound;
     111             :     }
     112           0 :     while(!(static_cast<long>(channels.size()) > n))
     113             :     {
     114           0 :         channels.emplace_back(channels.size(), *this);
     115             :     }
     116           0 :     SoundChannel &chan = channels[n];
     117           0 :     chan.setupchannel(n, slot, loc, ent, flags, radius);
     118           0 :     return chan;
     119             : }
     120             : 
     121             : //sets a channel as not being in use
     122           0 : void SoundEngine::freechannel(int n)
     123             : {
     124           0 :     if(!(static_cast<long>(channels.size()) > n) || !channels[n].inuse)
     125             :     {
     126           0 :         return;
     127             :     }
     128           0 :     SoundChannel &chan = channels[n];
     129           0 :     chan.inuse = false;
     130           0 :     if(chan.ent)
     131             :     {
     132           0 :         chan.ent->flags &= ~EntFlag_Sound;
     133             :     }
     134             : }
     135             : 
     136           0 : void SoundEngine::SoundChannel::syncchannel()
     137             : {
     138           0 :     if(!dirty)
     139             :     {
     140           0 :         return;
     141             :     }
     142           0 :     if(!Mix_FadingChannel(id))
     143             :     {
     144           0 :         Mix_Volume(id, volume);
     145             :     }
     146           0 :     Mix_SetPanning(id, 255-pan, pan);
     147           0 :     dirty = false;
     148             : }
     149             : 
     150           0 : void SoundEngine::stopchannels()
     151             : {
     152           0 :     for(uint i = 0; i < channels.size(); i++)
     153             :     {
     154           0 :         SoundChannel &chan = channels[i];
     155           0 :         if(!chan.inuse) //don't clear channels that are already flagged as unused
     156             :         {
     157           0 :             continue;
     158             :         }
     159           0 :         Mix_HaltChannel(i);
     160           0 :         freechannel(i);
     161             :     }
     162           0 : }
     163             : 
     164           0 : void SoundEngine::setsoundvol(const int * const vol)
     165             : {
     166           0 :     soundvol = std::clamp(*vol, 0, 255);
     167           0 :     if(!vol)
     168             :     {
     169           0 :         stopchannels();
     170           0 :         setmusicvol(0);
     171             :     }
     172           0 : }
     173             : 
     174           0 : int SoundEngine::getsoundvol()
     175             : {
     176           0 :     return soundvol;
     177             : }
     178             : 
     179           0 : void SoundEngine::setmusicvol(const int * const vol)
     180             : {
     181           0 :     soundvol = std::clamp(*vol, 0, 255);
     182           0 :     setmusicvol(soundvol ? musicvol : 0);
     183           0 : }
     184             : 
     185           0 : int SoundEngine::getmusicvol()
     186             : {
     187           0 :     return musicvol;
     188             : }
     189             : 
     190           0 : void SoundEngine::setmusicvol(int musicvol)
     191             : {
     192           0 :     if(nosound) //don't modulate music that isn't there
     193             :     {
     194           0 :         return;
     195             :     }
     196           0 :     if(music)
     197             :     {
     198           0 :         Mix_VolumeMusic((musicvol*MIX_MAX_VOLUME)/255);
     199             :     }
     200             : }
     201             : 
     202           0 : void SoundEngine::stopmusic()
     203             : {
     204           0 :     if(nosound) //don't stop music that isn't there
     205             :     {
     206           0 :         return;
     207             :     }
     208           0 :     delete[] musicfile;
     209           0 :     delete[] musicdonecmd;
     210           0 :     musicfile = musicdonecmd = nullptr;
     211             : 
     212           0 :     if(music)
     213             :     {
     214           0 :         Mix_HaltMusic();
     215           0 :         Mix_FreeMusic(music);
     216           0 :         music = nullptr;
     217             :     }
     218           0 :     if(musicrw)
     219             :     {
     220           0 :         SDL_FreeRW(musicrw);
     221           0 :         musicrw = nullptr;
     222             :     }
     223           0 :     if(musicstream)
     224             :     {
     225           0 :         delete musicstream;
     226           0 :         musicstream = nullptr;
     227             :     }
     228             : }
     229             : 
     230             : //SVARF(audiodriver, AUDIODRIVER, { shouldinitaudio = true; initwarning("sound configuration", Init_Reset, Change_Sound); });
     231           0 : void SoundEngine::setaudiodriver(char * f)
     232             : {
     233           0 :     audiodriver = std::string(f);
     234           0 :     shouldinitaudio = true;
     235           0 :     initwarning("sound configuration", Init_Reset, Change_Sound);
     236           0 : }
     237             : 
     238           0 : void SoundEngine::setsound(const int * const on)
     239             : {
     240           0 :     sound = *on;
     241           0 :     shouldinitaudio = true;
     242           0 :     initwarning("sound configuration", Init_Reset, Change_Sound);
     243           0 : }
     244           0 : int SoundEngine::getsound()
     245             : {
     246           0 :     if(sound)
     247             :     {
     248           0 :         return 1;
     249             :     }
     250           0 :     return 0;
     251             : }
     252             : 
     253             : //VARF(soundchans, 1, 32, 128, initwarning("sound configuration", Init_Reset, Change_Sound));
     254           0 : void SoundEngine::setsoundchans(const int * const val)
     255             : {
     256           0 :     soundchans = std::clamp(*val, 1, 128);
     257           0 :     initwarning("sound configuration", Init_Reset, Change_Sound);
     258           0 : }
     259           0 : int SoundEngine::getsoundchans()
     260             : {
     261           0 :     return soundchans;
     262             : }
     263             : 
     264           0 : bool SoundEngine::initaudio()
     265             : {
     266           0 :     static std::string fallback = "";
     267             :     static bool initfallback = true;
     268           0 :     if(initfallback)
     269             :     {
     270           0 :         initfallback = false;
     271           0 :         if(const char *env = SDL_getenv("SDL_AUDIODRIVER"))
     272             :         {
     273           0 :             fallback = std::string(env);
     274             :         }
     275             :     }
     276           0 :     if(!fallback[0] && audiodriver[0])
     277             :     {
     278           0 :         std::vector<std::string> drivers;
     279           0 :         explodelist(audiodriver.c_str(), drivers);
     280           0 :         for(uint i = 0; i < drivers.size(); i++)
     281             :         {
     282           0 :             SDL_setenv("SDL_AUDIODRIVER", drivers[i].c_str(), 1);
     283           0 :             if(SDL_InitSubSystem(SDL_INIT_AUDIO) >= 0)
     284             :             {
     285           0 :                 return true;
     286             :             }
     287             :         }
     288           0 :     }
     289           0 :     SDL_setenv("SDL_AUDIODRIVER", fallback.c_str(), 1);
     290           0 :     if(SDL_InitSubSystem(SDL_INIT_AUDIO) >= 0)
     291             :     {
     292           0 :         return true;
     293             :     }
     294           0 :     conoutf(Console_Error, "sound init failed: %s", SDL_GetError());
     295           0 :     return false;
     296             : }
     297             : 
     298             : //used in iengine
     299           0 : void SoundEngine::initsound()
     300             : {
     301             :     //get sdl version info
     302             :     SDL_version version;
     303           0 :     SDL_GetVersion(&version);
     304             :     //SDL 2.0.6 error check: 2.0.6 audio doesn't work
     305           0 :     if(version.major == 2 && version.minor == 0 && version.patch == 6)
     306             :     {
     307           0 :         nosound = true;
     308           0 :         if(sound)
     309             :         {
     310           0 :             conoutf(Console_Error, "audio is broken in SDL 2.0.6");
     311             :         }
     312           0 :         return;
     313             :     }
     314             : 
     315           0 :     if(shouldinitaudio)
     316             :     {
     317           0 :         shouldinitaudio = false; //don't init more than once
     318           0 :         if(SDL_WasInit(SDL_INIT_AUDIO))
     319             :         {
     320           0 :             SDL_QuitSubSystem(SDL_INIT_AUDIO);
     321             :         }
     322           0 :         if(!sound || !initaudio())
     323             :         {
     324           0 :             nosound = true;
     325           0 :             return;
     326             :         }
     327             :     }
     328             : 
     329           0 :     if(Mix_OpenAudio(soundfreq, MIX_DEFAULT_FORMAT, 2, soundbufferlen)<0)
     330             :     {
     331           0 :         nosound = true;
     332           0 :         conoutf(Console_Error, "sound init failed (SDL_mixer): %s", Mix_GetError());
     333           0 :         return;
     334             :     }
     335           0 :     Mix_AllocateChannels(soundchans);
     336           0 :     maxchannels = soundchans;
     337           0 :     nosound = false;
     338             : }
     339             : 
     340             : //clears and deletes cached music for playback
     341           0 : void SoundEngine::musicdone()
     342             : {
     343           0 :     if(music)
     344             :     {
     345           0 :         Mix_HaltMusic();
     346           0 :         Mix_FreeMusic(music);
     347           0 :         music = nullptr;
     348             :     }
     349           0 :     if(musicrw)
     350             :     {
     351           0 :         SDL_FreeRW(musicrw);
     352           0 :         musicrw = nullptr;
     353             :     }
     354           0 :     if(musicstream)
     355             :     {
     356           0 :         delete musicstream;
     357           0 :         musicstream = nullptr;
     358             :     }
     359           0 :     delete[] musicfile;
     360           0 :     musicfile = nullptr;
     361           0 :     if(!musicdonecmd)
     362             :     {
     363           0 :         return;
     364             :     }
     365           0 :     char *cmd = musicdonecmd;
     366           0 :     musicdonecmd = nullptr;
     367           0 :     execute(cmd);
     368           0 :     delete[] cmd;
     369           0 :     cmd = nullptr;
     370             : }
     371             : 
     372             : //uses Mix_Music object from libSDL
     373           0 : Mix_Music *SoundEngine::loadmusic(const char *name)
     374             : {
     375           0 :     if(!musicstream)
     376             :     {
     377           0 :         musicstream = openzipfile(name, "rb");
     378             :     }
     379           0 :     if(musicstream)
     380             :     {
     381           0 :         if(!musicrw)
     382             :         {
     383           0 :             musicrw = musicstream->rwops();
     384             :         }
     385           0 :         if(!musicrw)
     386             :         {
     387           0 :             if(musicstream)
     388             :             {
     389           0 :                 delete musicstream;
     390           0 :                 musicstream = nullptr;
     391             :             }
     392             :         }
     393             :     }
     394           0 :     if(musicrw)
     395             :     {
     396           0 :         music = Mix_LoadMUSType_RW(musicrw, MUS_NONE, 0);
     397             :     }
     398             :     else
     399             :     {
     400           0 :         music = Mix_LoadMUS(findfile(name, "rb"));
     401             :     }
     402           0 :     if(!music)
     403             :     {
     404           0 :         if(musicrw)
     405             :         {
     406           0 :             SDL_FreeRW(musicrw);
     407           0 :             musicrw = nullptr;
     408             :         }
     409           0 :         if(musicstream)
     410             :         {
     411           0 :             delete musicstream;
     412           0 :             musicstream = nullptr;
     413             :         }
     414             :     }
     415           0 :     return music;
     416             : }
     417             : 
     418             : //cmd
     419           0 : void SoundEngine::startmusic(char *name, char *cmd)
     420             : {
     421           0 :     if(nosound)
     422             :     {
     423           0 :         return;
     424             :     }
     425           0 :     stopmusic();
     426           0 :     if(soundvol && musicvol && *name) //if volume > 0 and music name passed
     427             :     {
     428           0 :         std::string file = "media/";
     429           0 :         file.append(name);
     430           0 :         path(file);
     431           0 :         if(loadmusic(file.c_str()))
     432             :         {
     433           0 :             delete[] musicfile;
     434           0 :             delete[] musicdonecmd;
     435             : 
     436           0 :             musicfile = newstring(file.c_str());
     437           0 :             if(cmd[0])
     438             :             {
     439           0 :                 musicdonecmd = newstring(cmd);
     440             :             }
     441             :             else
     442             :             {
     443           0 :                 musicdonecmd = nullptr;
     444             :             }
     445           0 :             Mix_PlayMusic(music, cmd[0] ? 0 : -1);
     446           0 :             Mix_VolumeMusic((musicvol*MIX_MAX_VOLUME)/255);
     447           0 :             intret(1);
     448             :         }
     449             :         else //note that there is no error message for  soundvol/musicvol/name null
     450             :         {
     451           0 :             conoutf(Console_Error, "could not play music: %s", file.c_str());
     452           0 :             intret(0);
     453             :         }
     454           0 :     }
     455             : }
     456             : 
     457           0 : Mix_Chunk *SoundEngine::loadwav(const char *name)
     458             : {
     459           0 :     Mix_Chunk *c = nullptr;
     460           0 :     stream *z = openzipfile(name, "rb");
     461           0 :     if(z)
     462             :     {
     463           0 :         SDL_RWops *rw = z->rwops();
     464           0 :         if(rw)
     465             :         {
     466           0 :             c = Mix_LoadWAV_RW(rw, 0);
     467           0 :             SDL_FreeRW(rw);
     468             :         }
     469           0 :         delete z;
     470             :     }
     471           0 :     if(!c)
     472             :     {
     473           0 :         c = Mix_LoadWAV(findfile(name, "rb"));
     474             :     }
     475           0 :     return c;
     476             : }
     477             : 
     478           0 : bool SoundEngine::SoundSample::load(const char *dir)
     479             : {
     480           0 :     if(chunk)
     481             :     {
     482           0 :         return true;
     483             :     }
     484           0 :     if(!name.size())
     485             :     {
     486           0 :         return false;
     487             :     }
     488             :     static const char * const exts[] = { "", ".ogg" };
     489             :     string filename;
     490           0 :     for(int i = 0; i < static_cast<int>(sizeof(exts)/sizeof(exts[0])); ++i)
     491             :     {
     492           0 :         formatstring(filename, "media/sound/%s%s%s", dir, name.c_str(), exts[i]);
     493           0 :         path(filename);
     494           0 :         chunk = parent->loadwav(filename);
     495           0 :         if(chunk)
     496             :         {
     497           0 :             return true;
     498             :         }
     499             :     }
     500           0 :     conoutf(Console_Error, "failed to load sample: media/sound/%s%s", dir, name.c_str());
     501           0 :     return false;
     502             : }
     503             : 
     504             : //SoundType
     505           0 : SoundEngine::SoundType::SoundType(const char *dir, SoundEngine& p) : parent(&p), dir(dir) {}
     506             : 
     507           0 : int SoundEngine::SoundType::findsound(const char *name, int vol)
     508             : {
     509           0 :     for(uint i = 0; i < configs.size(); i++)
     510             :     {
     511           0 :         SoundConfig &s = configs[i];
     512           0 :         for(int j = 0; j < s.numslots; ++j)
     513             :         {
     514           0 :             soundslot &c = slots[s.slots+j];
     515           0 :             if(!std::strcmp(c.sample->name.c_str(), name) && (!vol || c.volume==vol))
     516             :             {
     517           0 :                 return i;
     518             :             }
     519             :         }
     520             :     }
     521           0 :     return -1;
     522             : }
     523           0 : int SoundEngine::SoundType::addslot(const char *name, int vol)
     524             : {
     525           0 :     SoundSample * sample = nullptr;
     526           0 :     auto itr = samples.find(std::string(name));
     527           0 :     if(itr == samples.end())
     528             :     {
     529           0 :         SoundSample s(*parent);
     530           0 :         s.name = std::string(name);
     531           0 :         s.chunk = nullptr;
     532           0 :         samples.insert(std::pair<std::string, SoundSample>(std::string(name), s));
     533           0 :         itr = samples.find(std::string(name));
     534           0 :     }
     535             :     else
     536             :     {
     537           0 :         sample = &(*(itr)).second;
     538             :     }
     539           0 :     soundslot *oldslots = slots.data();
     540           0 :     int oldlen = slots.size();
     541           0 :     slots.emplace_back();
     542           0 :     soundslot &slot = slots.back();
     543             :     // soundslots.add() may relocate slot pointers
     544           0 :     if(slots.data() != oldslots)
     545             :     {
     546           0 :         for(uint i = 0; i < parent->channels.size(); i++)
     547             :         {
     548           0 :             SoundChannel &chan = parent->channels[i];
     549           0 :             if(chan.inuse && chan.slot >= oldslots && chan.slot < &oldslots[oldlen])
     550             :             {
     551           0 :                 chan.slot = &slots[chan.slot - oldslots];
     552             :             }
     553             :         }
     554             :     }
     555           0 :     slot.sample = sample;
     556           0 :     slot.volume = vol ? vol : 100;
     557           0 :     return oldlen;
     558             : }
     559           0 : int SoundEngine::SoundType::addsound(const char *name, int vol, int maxuses)
     560             : {
     561             :     SoundConfig s;
     562           0 :     s.slots = addslot(name, vol);
     563           0 :     s.numslots = 1;
     564           0 :     s.maxuses = maxuses;
     565           0 :     configs.push_back(s);
     566           0 :     return configs.size()-1;
     567             : }
     568             : 
     569           0 : void SoundEngine::SoundType::addalt(const char *name, int vol)
     570             : {
     571           0 :     if(configs.empty())
     572             :     {
     573           0 :         return;
     574             :     }
     575           0 :     addslot(name, vol);
     576           0 :     configs.back().numslots++;
     577             : }
     578           0 : void SoundEngine::SoundType::clear()
     579             : {
     580           0 :     slots.clear();
     581           0 :     configs.clear();
     582           0 : }
     583           0 : void SoundEngine::SoundType::reset() //cleanup each channel
     584             : {
     585           0 :     for(uint i = 0; i < parent->channels.size(); i++)
     586             :     {
     587           0 :         SoundChannel &chan = parent->channels[i];
     588           0 :         soundslot * array = slots.data();
     589           0 :         uint size = slots.size();
     590           0 :         bool inbuf = chan.slot >= array + size && chan.slot < array; //within bounds of utilized vector spaces
     591           0 :         if(chan.inuse && inbuf)
     592             :         {
     593           0 :             Mix_HaltChannel(i);
     594           0 :             parent->freechannel(i);
     595             :         }
     596             :     }
     597           0 :     clear();
     598           0 : }
     599           0 : void SoundEngine::SoundType::cleanupsamples()
     600             : {
     601           0 :     for (auto& [k, v]: samples)
     602             :     {
     603           0 :         v.cleanup();
     604             :     }
     605           0 : }
     606           0 : void SoundEngine::SoundType::cleanup()
     607             : {
     608           0 :     cleanupsamples();
     609           0 :     slots.clear();
     610           0 :     configs.clear();
     611           0 :     samples.clear();
     612           0 : }
     613           0 : void SoundEngine::SoundType::preloadsound(int n)
     614             : {
     615           0 :     if(parent->nosound || !(static_cast<long>(configs.size()) > n))
     616             :     {
     617           0 :         return;
     618             :     }
     619           0 :     SoundConfig &config = configs[n];
     620           0 :     for(int k = 0; k < config.numslots; ++k)
     621             :     {
     622           0 :         slots[config.slots+k].sample->load(dir);
     623             :     }
     624             : }
     625           0 : bool SoundEngine::SoundType::playing(const SoundChannel &chan, const SoundConfig &config) const
     626             : {
     627           0 :     return chan.inuse && config.hasslot(chan.slot, slots);
     628             : }
     629             : 
     630             : //free all channels
     631           0 : void SoundEngine::resetchannels()
     632             : {
     633           0 :     for(uint i = 0; i < channels.size(); i++)
     634             :     {
     635           0 :         if(channels[i].inuse)
     636             :         {
     637           0 :             freechannel(i);
     638             :         }
     639             :     }
     640           0 :     channels.clear();
     641           0 : }
     642             : 
     643             : //used externally in iengine
     644           0 : void SoundEngine::clear_sound()
     645             : {
     646           0 :     if(nosound) //don't bother closing stuff that isn't there
     647             :     {
     648           0 :         return;
     649             :     }
     650           0 :     stopmusic();
     651             : 
     652           0 :     gamesounds.cleanup();
     653           0 :     mapsounds.cleanup();
     654           0 :     Mix_CloseAudio();
     655           0 :     resetchannels();
     656             : }
     657             : 
     658           0 : void SoundEngine::stopmapsound(extentity *e)
     659             : {
     660           0 :     for(uint i = 0; i < channels.size(); i++)
     661             :     {
     662           0 :         SoundChannel &chan = channels[i];
     663           0 :         if(chan.inuse && chan.ent == e)
     664             :         {
     665           0 :             Mix_HaltChannel(i);
     666           0 :             freechannel(i);
     667             :         }
     668             :     }
     669           0 : }
     670             : 
     671           0 : void SoundEngine::setstereo(const int * const on)
     672             : {
     673           0 :     stereo = on;
     674           0 : }
     675           0 : int SoundEngine::getstereo()
     676             : {
     677           0 :     if(stereo)
     678             :     {
     679           0 :         return 1;
     680             :     }
     681           0 :     return 0;
     682             : }
     683             : //VAR(stereo, 0, 1, 1); //toggles mixing of sounds by direction
     684             : 
     685             : //distance in cubits: how far away sound entities can be heard at(340 = 42.5m)
     686           0 : void SoundEngine::setmaxradius(const int * const dist)
     687             : {
     688           0 :     maxsoundradius = *dist;
     689           0 : }
     690           0 : int SoundEngine::getmaxradius()
     691             : {
     692           0 :     return maxsoundradius;
     693             : }
     694             : 
     695             : //recalculates stereo mix & volume for a soundchannel (sound ent, or player generated sound)
     696             : //(unless stereo is disabled, in which case the mix is only by distance)
     697           0 : bool SoundEngine::SoundChannel::updatechannel()
     698             : {
     699           0 :     if(!slot)
     700             :     {
     701           0 :         return false;
     702             :     }
     703           0 :     int vol = parent->soundvol,
     704           0 :         middlepan = 255/2;
     705           0 :     if(hasloc())
     706             :     {
     707           0 :         vec v;
     708           0 :         float dist = loc.dist(camera1->o, v);
     709           0 :         int rad = parent->maxsoundradius;
     710           0 :         if(ent)
     711             :         {
     712           0 :             rad = ent->attr2;
     713           0 :             if(ent->attr3)
     714             :             {
     715           0 :                 rad -= ent->attr3;
     716           0 :                 dist -= ent->attr3;
     717             :             }
     718             :         }
     719           0 :         else if(radius > 0)
     720             :         {
     721           0 :             rad = parent->maxsoundradius ? std::min(parent->maxsoundradius, radius) : radius;
     722             :         }
     723           0 :         if(rad > 0) //rad = 0 means no attenuation ever
     724             :         {
     725           0 :             vol -= static_cast<int>(std::clamp(dist/rad, 0.0f, 1.0f)*parent->soundvol); // simple mono distance attenuation
     726             :         }
     727           0 :         if(parent->stereo && (v.x != 0 || v.y != 0) && dist>0)
     728             :         {
     729           0 :             v.rotate_around_z(-camera1->yaw/RAD);
     730           0 :             pan = static_cast<int>(255.9f*(0.5f - 0.5f*v.x/v.magnitude2())); // range is from 0 (left) to 255 (right)
     731             :         }
     732             :     }
     733           0 :     vol = (vol*MIX_MAX_VOLUME*slot->volume)/255/255;
     734           0 :     vol = std::min(vol, MIX_MAX_VOLUME);
     735           0 :     if(vol == volume && pan == middlepan)
     736             :     {
     737           0 :         return false;
     738             :     }
     739           0 :     volume = vol;
     740           0 :     pan = middlepan;
     741           0 :     dirty = true;
     742           0 :     return true;
     743             : }
     744             : 
     745             : //free channels that are not playing sounds
     746           0 : void SoundEngine::reclaimchannels()
     747             : {
     748           0 :     for(uint i = 0; i < channels.size(); i++)
     749             :     {
     750           0 :         SoundChannel &chan = channels[i];
     751           0 :         if(chan.inuse && !Mix_Playing(i))
     752             :         {
     753           0 :             freechannel(i);
     754             :         }
     755             :     }
     756           0 : }
     757             : 
     758           0 : void SoundEngine::syncchannels()
     759             : {
     760           0 :     for(uint i = 0; i < channels.size(); i++)
     761             :     {
     762           0 :         SoundChannel &chan = channels[i];
     763           0 :         if(chan.inuse && chan.hasloc() && chan.updatechannel())
     764             :         {
     765           0 :             chan.syncchannel();
     766             :         }
     767             :     }
     768           0 : }
     769             : 
     770             : //VARP(minimizedsounds, 0, 0, 1); //toggles playing sound when window minimized
     771           0 : int SoundEngine::getminimizedsounds()
     772             : {
     773           0 :     return minimizedsounds;
     774             : }
     775           0 : void SoundEngine::setminimizedsounds(int minimize)
     776             : {
     777           0 :     minimizedsounds = minimize;
     778           0 : }
     779             : 
     780             : //number of sounds before the game will refuse to play another sound (with `playsound()`);
     781             : //set to 0 to disable checking (0 does not set no sounds to be playable)
     782             : //VARP(maxsoundsatonce, 0, 7, 100);
     783           0 : int SoundEngine::getmaxsoundsatonce()
     784             : {
     785           0 :     return maxsoundsatonce;
     786             : }
     787           0 : void SoundEngine::setmaxsoundsatonce(const int * num)
     788             : {
     789           0 :     maxsoundsatonce = std::clamp(*num, 0, 100);
     790           0 : }
     791             : 
     792             : //used in iengine.h
     793           0 : void SoundEngine::preloadsound(int n)
     794             : {
     795           0 :     gamesounds.preloadsound(n);
     796           0 : }
     797             : 
     798           0 : void SoundEngine::preloadmapsounds()
     799             : {
     800           0 :     const std::vector<extentity *> &ents = entities::getents();
     801           0 :     for(uint i = 0; i < ents.size(); i++)
     802             :     {
     803           0 :         extentity &e = *ents[i];
     804           0 :         if(e.type==EngineEnt_Sound)
     805             :         {
     806           0 :             mapsounds.preloadsound(e.attr1); //load sounds by first ent attr (index)
     807             :         }
     808             :     }
     809           0 : }
     810             : 
     811             : //used in iengine.h
     812           0 : int SoundEngine::playsound(int n, const vec *loc, extentity *ent, int flags, int loops, int fade, int chanid, int radius, int expire)
     813             : {
     814           0 :     if(nosound || !soundvol || (minimized && !minimizedsounds)) //mute check
     815             :     {
     816           0 :         return -1;
     817             :     }
     818           0 :     SoundType &sounds = ent || flags&Music_Map ? mapsounds : gamesounds;
     819           0 :     if(!(static_cast<long>(sounds.configs.size()) > n)) //sound isn't within index
     820             :     {
     821           0 :         conoutf(Console_Warn, "unregistered sound: %d", n);
     822           0 :         return -1;
     823             :     }
     824           0 :     SoundConfig &config = sounds.configs[n];
     825           0 :     if(loc && (maxsoundradius || radius > 0))
     826             :     {
     827             :         // cull sounds that are unlikely to be heard
     828             :         //if radius is greater than zero, clamp to maxsoundradius if maxsound radius is nonzero; if radius is zero, clamp to maxsoundradius
     829           0 :         int rad = radius > 0 ? (maxsoundradius ? std::min(maxsoundradius, radius) : radius) : maxsoundradius;
     830           0 :         if(camera1->o.dist(*loc) > 1.5f*rad)
     831             :         {
     832           0 :             if(channels.size() > static_cast<size_t>(chanid) && sounds.playing(channels[chanid], config))
     833             :             {
     834           0 :                 Mix_HaltChannel(chanid);
     835           0 :                 freechannel(chanid);
     836             :             }
     837           0 :             return -1;
     838             :         }
     839             :     }
     840           0 :     if(chanid < 0)
     841             :     {
     842           0 :         if(config.maxuses)
     843             :         {
     844           0 :             int uses = 0;
     845           0 :             for(const SoundChannel &s : channels)
     846             :             {
     847           0 :                 if(sounds.playing(s, config) && ++uses >= config.maxuses)
     848             :                 {
     849           0 :                     return -1;
     850             :                 }
     851             :             }
     852             :         }
     853             :         // avoid bursts of sounds with heavy packetloss and in sp
     854             :         static int soundsatonce = 0,
     855             :                    lastsoundmillis = 0;
     856           0 :         if(totalmillis == lastsoundmillis)
     857             :         {
     858           0 :             soundsatonce++;
     859             :         }
     860             :         else
     861             :         {
     862           0 :             soundsatonce = 1;
     863             :         }
     864           0 :         lastsoundmillis = totalmillis;
     865           0 :         if(maxsoundsatonce && soundsatonce > maxsoundsatonce)
     866             :         {
     867           0 :             return -1;
     868             :         }
     869             :     }
     870           0 :     if(channels.size() > static_cast<size_t>(chanid))
     871             :     {
     872           0 :         SoundChannel &chan = channels[chanid];
     873           0 :         if(sounds.playing(chan, config))
     874             :         {
     875           0 :             if(loc)
     876             :             {
     877           0 :                 chan.setloc(*loc);
     878             :             }
     879           0 :             else if(chan.hasloc())
     880             :             {
     881           0 :                 chan.clearloc();
     882             :             }
     883           0 :             return chanid;
     884             :         }
     885             :     }
     886           0 :     if(fade < 0) //attenuation past zero
     887             :     {
     888           0 :         return -1;
     889             :     }
     890           0 :     soundslot &slot = sounds.slots[config.chooseslot(flags)];
     891           0 :     if(!slot.sample->chunk && !slot.sample->load(sounds.dir))
     892             :     {
     893           0 :         return -1;
     894             :     }
     895           0 :     if(debugsound)
     896             :     {
     897           0 :         conoutf("sound: %s%s", sounds.dir, slot.sample->name.c_str());
     898             :     }
     899           0 :     chanid = -1;
     900           0 :     for(uint i = 0; i < channels.size(); i++)
     901             :     {
     902           0 :         if(!channels[i].inuse)
     903             :         {
     904           0 :             chanid = i;
     905           0 :             break;
     906             :         }
     907             :     }
     908           0 :     if(chanid < 0 && static_cast<long>(channels.size()) < maxchannels)
     909             :     {
     910           0 :         chanid = channels.size();
     911             :     }
     912           0 :     if(chanid < 0)
     913             :     {
     914           0 :         for(uint i = 0; i < channels.size(); i++)
     915             :         {
     916           0 :             if(!channels[i].volume)
     917             :             {
     918           0 :                 Mix_HaltChannel(i);
     919           0 :                 freechannel(i);
     920           0 :                 chanid = i;
     921           0 :                 break;
     922             :             }
     923             :         }
     924             :     }
     925           0 :     if(chanid < 0)
     926             :     {
     927           0 :         return -1;
     928             :     }
     929           0 :     SoundChannel &chan = newchannel(chanid, &slot, loc, ent, flags, radius);
     930           0 :     chan.updatechannel();
     931           0 :     int playing = -1;
     932             :     //some ugly ternary assignments
     933           0 :     if(fade)
     934             :     {
     935           0 :         Mix_Volume(chanid, chan.volume);
     936           0 :         playing = expire >= 0 ?
     937           0 :                   Mix_FadeInChannelTimed(chanid, slot.sample->chunk, loops, fade, expire) :
     938           0 :                   Mix_FadeInChannel(chanid, slot.sample->chunk, loops, fade);
     939             :     }
     940             :     else
     941             :     {
     942           0 :         playing = expire >= 0 ?
     943           0 :                   Mix_PlayChannelTimed(chanid, slot.sample->chunk, loops, expire) :
     944           0 :                   Mix_PlayChannel(chanid, slot.sample->chunk, loops);
     945             :     }
     946           0 :     if(playing >= 0)
     947             :     {
     948           0 :         chan.syncchannel();
     949             :     }
     950             :     else
     951             :     {
     952           0 :         freechannel(chanid);
     953             :     }
     954           0 :     return playing;
     955             : }
     956             : 
     957           0 : bool SoundEngine::stopsound(int n, int chanid, int fade)
     958             : {
     959           0 :     if(!(static_cast<long>(gamesounds.configs.size()) > n) || !(static_cast<long>(channels.size()) > chanid) || !gamesounds.playing(channels[chanid], gamesounds.configs[n]))
     960             :     {
     961           0 :         return false;
     962             :     }
     963           0 :     if(debugsound)
     964             :     {
     965           0 :         conoutf("stopsound: %s%s", gamesounds.dir, channels[chanid].slot->sample->name.c_str());
     966             :     }
     967           0 :     if(!fade || !Mix_FadeOutChannel(chanid, fade)) //clear and free channel allocation
     968             :     {
     969           0 :         Mix_HaltChannel(chanid);
     970           0 :         freechannel(chanid);
     971             :     }
     972           0 :     return true;
     973             : }
     974             : 
     975           0 : void SoundEngine::stopmapsounds()
     976             : {
     977           0 :     for(uint i = 0; i < channels.size(); i++)
     978             :     {
     979           0 :         if(channels[i].inuse && channels[i].ent)
     980             :         {
     981           0 :             Mix_HaltChannel(i);
     982           0 :             freechannel(i);
     983             :         }
     984             :     }
     985           0 : }
     986             : 
     987             : //check map entities to see what sounds need to be played because of them
     988           0 : void SoundEngine::checkmapsounds()
     989             : {
     990           0 :     const std::vector<extentity *> &ents = entities::getents();
     991           0 :     for(uint i = 0; i < ents.size(); i++)
     992             :     {
     993           0 :         extentity &e = *ents[i];
     994           0 :         if(e.type!=EngineEnt_Sound) //ents that aren't soundents don't make sound (!)
     995             :         {
     996           0 :             continue;
     997             :         }
     998           0 :         if(camera1->o.dist(e.o) < e.attr2) //if distance to entity < ent attr 2 (radius)
     999             :         {
    1000           0 :             if(!(e.flags&EntFlag_Sound))
    1001             :             {
    1002           0 :                 playsound(e.attr1, nullptr, &e, Music_Map, -1);
    1003             :             }
    1004             :         }
    1005           0 :         else if(e.flags&EntFlag_Sound)
    1006             :         {
    1007           0 :             stopmapsound(&e);
    1008             :         }
    1009             :     }
    1010           0 : }
    1011             : 
    1012           0 : void SoundEngine::stopsounds()
    1013             : {
    1014           0 :     for(uint i = 0; i < channels.size(); i++)
    1015             :     {
    1016           0 :         if(channels[i].inuse)
    1017             :         {
    1018           0 :             Mix_HaltChannel(i);
    1019           0 :             freechannel(i);
    1020             :         }
    1021             :     }
    1022           0 : }
    1023             : 
    1024           0 : void SoundEngine::updatesounds()
    1025             : {
    1026           0 :     if(nosound) //don't update sounds if disabled
    1027             :     {
    1028           0 :         return;
    1029             :     }
    1030           0 :     if(minimized && !minimizedsounds)//minimizedsounds check
    1031             :     {
    1032           0 :         stopsounds();
    1033             :     }
    1034             :     else
    1035             :     {
    1036           0 :         reclaimchannels(); //cull channels first
    1037           0 :         if(mainmenu) //turn off map sounds if you reach main menu
    1038             :         {
    1039           0 :             stopmapsounds();
    1040             :         }
    1041             :         else
    1042             :         {
    1043           0 :             checkmapsounds();
    1044             :         }
    1045           0 :         syncchannels();
    1046             :     }
    1047           0 :     if(music)
    1048             :     {
    1049           0 :         if(!Mix_PlayingMusic())
    1050             :         {
    1051           0 :             musicdone();
    1052             :         }
    1053           0 :         else if(Mix_PausedMusic())
    1054             :         {
    1055           0 :             Mix_ResumeMusic();
    1056             :         }
    1057             :     }
    1058             : }
    1059             : 
    1060             : //used in iengine.h
    1061           0 : int SoundEngine::playsoundname(const char *s, const vec *loc, int vol, int flags, int loops, int fade, int chanid, int radius, int expire)
    1062             : {
    1063           0 :     if(!vol) //default to 100 volume
    1064             :     {
    1065           0 :         vol = 100;
    1066             :     }
    1067           0 :     int id = gamesounds.findsound(s, vol);
    1068           0 :     if(id < 0)
    1069             :     {
    1070           0 :         id = gamesounds.addsound(s, vol);
    1071             :     }
    1072           0 :     return playsound(id, loc, nullptr, flags, loops, fade, chanid, radius, expire);
    1073             : }
    1074             : 
    1075           0 : void SoundEngine::resetsound()
    1076             : {
    1077           0 :     clearchanges(Change_Sound);
    1078           0 :     if(!nosound)
    1079             :     {
    1080           0 :         gamesounds.cleanupsamples();
    1081           0 :         mapsounds.cleanupsamples();
    1082           0 :         if(music)
    1083             :         {
    1084           0 :             Mix_HaltMusic();
    1085           0 :             Mix_FreeMusic(music);
    1086             :         }
    1087           0 :         if(musicstream)
    1088             :         {
    1089           0 :             musicstream->seek(0, SEEK_SET);
    1090             :         }
    1091           0 :         Mix_CloseAudio();
    1092             :     }
    1093           0 :     initsound();
    1094           0 :     resetchannels();
    1095           0 :     if(nosound) //clear stuff if muted
    1096             :     {
    1097           0 :         delete[] musicfile;
    1098           0 :         delete[] musicdonecmd;
    1099             : 
    1100           0 :         musicfile = musicdonecmd = nullptr;
    1101           0 :         music = nullptr;
    1102           0 :         gamesounds.cleanupsamples();
    1103           0 :         mapsounds.cleanupsamples();
    1104           0 :         return;
    1105             :     }
    1106           0 :     if(music && loadmusic(musicfile))
    1107             :     {
    1108           0 :         Mix_PlayMusic(music, musicdonecmd ? 0 : -1);
    1109           0 :         Mix_VolumeMusic((musicvol*MIX_MAX_VOLUME)/255);
    1110             :     }
    1111             :     else
    1112             :     {
    1113           0 :         delete[] musicfile;
    1114           0 :         delete[] musicdonecmd;
    1115             : 
    1116           0 :         musicfile = musicdonecmd = nullptr;
    1117             :     }
    1118             : }
    1119             : 
    1120           0 : void SoundEngine::registersound (char *name, int *vol)
    1121             : {
    1122           0 :     intret(gamesounds.addsound(name, *vol, 0));
    1123           0 : }
    1124             : 
    1125           0 : void SoundEngine::mapsound(char *name, int *vol, int *maxuses)
    1126             : {
    1127           0 :     intret(mapsounds.addsound(name, *vol, *maxuses < 0 ? 0 : std::max(1, *maxuses)));
    1128           0 : }
    1129             : 
    1130           0 : void SoundEngine::altsound(char *name, int *vol)
    1131             : {
    1132           0 :     gamesounds.addalt(name, *vol);
    1133           0 : }
    1134             : 
    1135           0 : void SoundEngine::altmapsound(char *name, int *vol)
    1136             : {
    1137           0 :     mapsounds.addalt(name, *vol);
    1138           0 : }
    1139             : 
    1140           0 : void SoundEngine::numsounds()
    1141             : {
    1142           0 :     intret(gamesounds.configs.size());
    1143           0 : }
    1144             : 
    1145           0 : void SoundEngine::nummapsounds()
    1146             : {
    1147           0 :     intret(mapsounds.configs.size());
    1148           0 : }
    1149             : 
    1150           0 : void SoundEngine::soundreset()
    1151             : {
    1152           0 :     gamesounds.reset();
    1153           0 : }
    1154             : 
    1155           0 : void SoundEngine::mapsoundreset()
    1156             : {
    1157           0 :     mapsounds.reset();
    1158           0 : }

Generated by: LCOV version 1.14