summaryrefslogtreecommitdiff
path: root/src/engine/sound.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/sound.cpp')
-rw-r--r--src/engine/sound.cpp1278
1 files changed, 636 insertions, 642 deletions
diff --git a/src/engine/sound.cpp b/src/engine/sound.cpp
index 18cc0ab..d10386d 100644
--- a/src/engine/sound.cpp
+++ b/src/engine/sound.cpp
@@ -7,115 +7,115 @@ bool nosound = true;
struct soundsample
{
- char *name;
- Mix_Chunk *chunk;
+ char *name;
+ Mix_Chunk *chunk;
- soundsample() : name(NULL), chunk(NULL) {}
- ~soundsample() { DELETEA(name); }
+ soundsample() : name(NULL), chunk(NULL) {}
+ ~soundsample() { DELETEA(name); }
- void cleanup() { if(chunk) { Mix_FreeChunk(chunk); chunk = NULL; } }
- bool load(bool msg = false);
+ void cleanup() { if(chunk) { Mix_FreeChunk(chunk); chunk = NULL; } }
+ bool load(bool msg = false);
};
struct soundslot
{
- soundsample *sample;
- int volume;
+ soundsample *sample;
+ int volume;
};
struct soundconfig
{
- int slots, numslots;
- int maxuses;
+ int slots, numslots;
+ int maxuses;
- bool hasslot(const soundslot *p, const vector<soundslot> &v) const
- {
- return p >= v.getbuf() + slots && p < v.getbuf() + slots+numslots && slots+numslots < v.length();
- }
+ bool hasslot(const soundslot *p, const vector<soundslot> &v) const
+ {
+ return p >= v.getbuf() + slots && p < v.getbuf() + slots+numslots && slots+numslots < v.length();
+ }
- int chooseslot(int flags) const
- {
- if(flags&SND_NO_ALT || numslots <= 1) return slots;
- if(flags&SND_USE_ALT) return slots + 1 + rnd(numslots - 1);
- return slots + rnd(numslots);
- }
+ int chooseslot(int flags) const
+ {
+ if(flags&SND_NO_ALT || numslots <= 1) return slots;
+ if(flags&SND_USE_ALT) return slots + 1 + rnd(numslots - 1);
+ return slots + rnd(numslots);
+ }
};
struct soundchannel
{
- int id;
- bool inuse;
- vec loc;
- soundslot *slot;
- extentity *ent;
- int radius, volume, pan, flags;
- bool dirty;
-
- soundchannel(int id) : id(id) { reset(); }
-
- bool hasloc() const { return loc.x >= -1e15f; }
- void clearloc() { loc = vec(-1e16f, -1e16f, -1e16f); }
-
- void reset()
- {
- inuse = false;
- clearloc();
- slot = NULL;
- ent = NULL;
- radius = 0;
- volume = -1;
- pan = -1;
- flags = 0;
- dirty = false;
- }
+ int id;
+ bool inuse;
+ vec loc;
+ soundslot *slot;
+ extentity *ent;
+ int radius, volume, pan, flags;
+ bool dirty;
+
+ soundchannel(int id) : id(id) { reset(); }
+
+ bool hasloc() const { return loc.x >= -1e15f; }
+ void clearloc() { loc = vec(-1e16f, -1e16f, -1e16f); }
+
+ void reset()
+ {
+ inuse = false;
+ clearloc();
+ slot = NULL;
+ ent = NULL;
+ radius = 0;
+ volume = -1;
+ pan = -1;
+ flags = 0;
+ dirty = false;
+ }
};
vector<soundchannel> channels;
int maxchannels = 0;
soundchannel &newchannel(int n, soundslot *slot, const vec *loc = NULL, extentity *ent = NULL, int flags = 0, int radius = 0)
{
- if(ent)
- {
- loc = &ent->o;
- ent->flags |= EF_SOUND;
- }
- while(!channels.inrange(n)) channels.add(channels.length());
- soundchannel &chan = channels[n];
- chan.reset();
- chan.inuse = true;
- if(loc) chan.loc = *loc;
- chan.slot = slot;
- chan.ent = ent;
- chan.flags = 0;
- chan.radius = radius;
- return chan;
+ if(ent)
+ {
+ loc = &ent->o;
+ ent->flags |= EF_SOUND;
+ }
+ while(!channels.inrange(n)) channels.add(channels.length());
+ soundchannel &chan = channels[n];
+ chan.reset();
+ chan.inuse = true;
+ if(loc) chan.loc = *loc;
+ chan.slot = slot;
+ chan.ent = ent;
+ chan.flags = 0;
+ chan.radius = radius;
+ return chan;
}
void freechannel(int n)
{
- if(!channels.inrange(n) || !channels[n].inuse) return;
- soundchannel &chan = channels[n];
- chan.inuse = false;
- if(chan.ent) chan.ent->flags &= ~EF_SOUND;
+ if(!channels.inrange(n) || !channels[n].inuse) return;
+ soundchannel &chan = channels[n];
+ chan.inuse = false;
+ if(chan.ent) chan.ent->flags &= ~EF_SOUND;
}
void syncchannel(soundchannel &chan)
{
- if(!chan.dirty) return;
- if(!Mix_FadingChannel(chan.id)) Mix_Volume(chan.id, chan.volume);
- Mix_SetPanning(chan.id, 255-chan.pan, chan.pan);
- chan.dirty = false;
+ if(!chan.dirty) return;
+ if(!Mix_FadingChannel(chan.id)) Mix_Volume(chan.id, chan.volume);
+ Mix_SetPanning(chan.id, 255-chan.pan, chan.pan);
+ chan.dirty = false;
}
void stopchannels()
{
- loopv(channels)
- {
- soundchannel &chan = channels[i];
- if(!chan.inuse) continue;
- Mix_HaltChannel(i);
- freechannel(i);
- }
+ loopv(channels)
+ {
+ soundchannel &chan = channels[i];
+ if(!chan.inuse) continue;
+ Mix_HaltChannel(i);
+ freechannel(i);
+ }
}
void setmusicvol(int musicvol);
@@ -123,9 +123,9 @@ extern int musicvol;
static int curvol = 0;
VARFP(soundvol, 0, 255, 255,
{
- if(!soundvol) { stopchannels(); setmusicvol(0); }
- else if(!curvol) setmusicvol(musicvol);
- curvol = soundvol;
+ if(!soundvol) { stopchannels(); setmusicvol(0); }
+ else if(!curvol) setmusicvol(musicvol);
+ curvol = soundvol;
});
VARFP(musicvol, 0, 128, 255, setmusicvol(soundvol ? musicvol : 0));
@@ -137,28 +137,27 @@ stream *musicstream = NULL;
void setmusicvol(int musicvol)
{
- if(nosound) return;
- if(music) Mix_VolumeMusic((musicvol*MIX_MAX_VOLUME)/255);
+ if(nosound) return;
+ if(music) Mix_VolumeMusic((musicvol*MIX_MAX_VOLUME)/255);
}
void stopmusic()
{
- if(nosound) return;
- DELETEA(musicfile);
- DELETEA(musicdonecmd);
- if(music)
- {
- Mix_HaltMusic();
- Mix_FreeMusic(music);
- music = NULL;
- }
- if(musicrw) { SDL_FreeRW(musicrw); musicrw = NULL; }
- DELETEP(musicstream);
+ if(nosound) return;
+ DELETEA(musicfile);
+ DELETEA(musicdonecmd);
+ if(music)
+ {
+ Mix_HaltMusic();
+ Mix_FreeMusic(music);
+ music = NULL;
+ }
+ if(musicrw) { SDL_FreeRW(musicrw); musicrw = NULL; }
+ DELETEP(musicstream);
}
-#define AUDIODRIVER ""
bool shouldinitaudio = true;
-SVARF(audiodriver, AUDIODRIVER, { shouldinitaudio = true; initwarning("sound configuration", INIT_RESET, CHANGE_SOUND); });
+SVARF(audiodriver, "", { shouldinitaudio = true; initwarning("sound configuration", INIT_RESET, CHANGE_SOUND); });
VARF(usesound, 0, 1, 1, { shouldinitaudio = true; initwarning("sound configuration", INIT_RESET, CHANGE_SOUND); });
VARF(soundchans, 1, 32, 128, initwarning("sound configuration", INIT_RESET, CHANGE_SOUND));
VARF(soundfreq, 0, MIX_DEFAULT_FREQUENCY, 48000, initwarning("sound configuration", INIT_RESET, CHANGE_SOUND));
@@ -166,377 +165,377 @@ VARF(soundbufferlen, 128, 1024, 4096, initwarning("sound configuration", INIT_RE
bool initaudio()
{
- static string fallback = "";
- static bool initfallback = true;
- static bool restorefallback = false;
- if(initfallback)
- {
- initfallback = false;
- if(char *env = SDL_getenv("SDL_AUDIODRIVER")) copystring(fallback, env);
- }
- if(!fallback[0] && audiodriver[0])
- {
- vector<char*> drivers;
- explodelist(audiodriver, drivers);
- loopv(drivers)
- {
- restorefallback = true;
- SDL_setenv("SDL_AUDIODRIVER", drivers[i], 1);
- if(SDL_InitSubSystem(SDL_INIT_AUDIO) >= 0)
- {
- drivers.deletearrays();
- return true;
- }
- }
- drivers.deletearrays();
- }
- if(restorefallback)
- {
- restorefallback = false;
- unsetenv("SDL_AUDIODRIVER");
- }
- if(SDL_InitSubSystem(SDL_INIT_AUDIO) >= 0) return true;
- conoutf(CON_ERROR, "sound init failed: %s", SDL_GetError());
- return false;
+ static string fallback = "";
+ static bool initfallback = true;
+ static bool restorefallback = false;
+ if(initfallback)
+ {
+ initfallback = false;
+ if(char *env = SDL_getenv("SDL_AUDIODRIVER")) copystring(fallback, env);
+ }
+ if(!fallback[0] && audiodriver[0])
+ {
+ vector<char*> drivers;
+ explodelist(audiodriver, drivers);
+ loopv(drivers)
+ {
+ restorefallback = true;
+ SDL_setenv("SDL_AUDIODRIVER", drivers[i], 1);
+ if(SDL_InitSubSystem(SDL_INIT_AUDIO) >= 0)
+ {
+ drivers.deletearrays();
+ return true;
+ }
+ }
+ drivers.deletearrays();
+ }
+ if(restorefallback)
+ {
+ restorefallback = false;
+ unsetenv("SDL_AUDIODRIVER");
+ }
+ if(SDL_InitSubSystem(SDL_INIT_AUDIO) >= 0) return true;
+ conoutf(CON_ERROR, "sound init failed: %s", SDL_GetError());
+ return false;
}
void initsound()
{
- SDL_version version;
- SDL_GetVersion(&version);
- if(version.major == 2 && version.minor == 0 && version.patch == 6)
- {
- nosound = true;
- if(usesound) conoutf(CON_ERROR, "audio is broken in SDL 2.0.6");
- return;
- }
-
- if(shouldinitaudio)
- {
- shouldinitaudio = false;
- if(SDL_WasInit(SDL_INIT_AUDIO)) SDL_QuitSubSystem(SDL_INIT_AUDIO);
- if(!usesound || !initaudio())
- {
- nosound = true;
- return;
- }
- }
-
- if(Mix_OpenAudio(soundfreq, MIX_DEFAULT_FORMAT, 2, soundbufferlen)<0)
- {
- nosound = true;
- conoutf(CON_ERROR, "sound init failed (SDL_mixer): %s", Mix_GetError());
- return;
- }
+ SDL_version version;
+ SDL_GetVersion(&version);
+ if(version.major == 2 && version.minor == 0 && version.patch == 6)
+ {
+ nosound = true;
+ if(usesound) conoutf(CON_ERROR, "audio is broken in SDL 2.0.6");
+ return;
+ }
+
+ if(shouldinitaudio)
+ {
+ shouldinitaudio = false;
+ if(SDL_WasInit(SDL_INIT_AUDIO)) SDL_QuitSubSystem(SDL_INIT_AUDIO);
+ if(!usesound || !initaudio())
+ {
+ nosound = true;
+ return;
+ }
+ }
+
+ if(Mix_OpenAudio(soundfreq, MIX_DEFAULT_FORMAT, 2, soundbufferlen)<0)
+ {
+ nosound = true;
+ conoutf(CON_ERROR, "sound init failed (SDL_mixer): %s", Mix_GetError());
+ return;
+ }
Mix_AllocateChannels(soundchans);
- maxchannels = soundchans;
- nosound = false;
+ maxchannels = soundchans;
+ nosound = false;
}
void musicdone()
{
- if(music) { Mix_HaltMusic(); Mix_FreeMusic(music); music = NULL; }
- if(musicrw) { SDL_FreeRW(musicrw); musicrw = NULL; }
- DELETEP(musicstream);
- DELETEA(musicfile);
- if(!musicdonecmd) return;
- char *cmd = musicdonecmd;
- musicdonecmd = NULL;
- execute(cmd);
- delete[] cmd;
+ if(music) { Mix_HaltMusic(); Mix_FreeMusic(music); music = NULL; }
+ if(musicrw) { SDL_FreeRW(musicrw); musicrw = NULL; }
+ DELETEP(musicstream);
+ DELETEA(musicfile);
+ if(!musicdonecmd) return;
+ char *cmd = musicdonecmd;
+ musicdonecmd = NULL;
+ execute(cmd);
+ delete[] cmd;
}
Mix_Music *loadmusic(const char *name)
{
- if(!musicstream) musicstream = openzipfile(name, "rb");
- if(musicstream)
- {
- if(!musicrw) musicrw = musicstream->rwops();
- if(!musicrw) DELETEP(musicstream);
- }
- if(musicrw) music = Mix_LoadMUSType_RW(musicrw, MUS_NONE, 0);
- else music = Mix_LoadMUS(findfile(name, "rb"));
- if(!music)
- {
- if(musicrw) { SDL_FreeRW(musicrw); musicrw = NULL; }
- DELETEP(musicstream);
- }
- return music;
+ if(!musicstream) musicstream = openzipfile(name, "rb");
+ if(musicstream)
+ {
+ if(!musicrw) musicrw = musicstream->rwops();
+ if(!musicrw) DELETEP(musicstream);
+ }
+ if(musicrw) music = Mix_LoadMUSType_RW(musicrw, MUS_NONE, 0);
+ else music = Mix_LoadMUS(findfile(name, "rb"));
+ if(!music)
+ {
+ if(musicrw) { SDL_FreeRW(musicrw); musicrw = NULL; }
+ DELETEP(musicstream);
+ }
+ return music;
}
void startmusic(char *name, char *cmd)
{
- if(nosound) return;
- stopmusic();
- if(soundvol && musicvol && *name)
- {
- defformatstring(file, "packages/%s", name);
- path(file);
- if(loadmusic(file))
- {
- DELETEA(musicfile);
- DELETEA(musicdonecmd);
- musicfile = newstring(file);
- if(cmd[0]) musicdonecmd = newstring(cmd);
- Mix_PlayMusic(music, cmd[0] ? 0 : -1);
- Mix_VolumeMusic((musicvol*MIX_MAX_VOLUME)/255);
- intret(1);
- }
- else
- {
- conoutf(CON_ERROR, "could not play music: %s", file);
- intret(0);
- }
- }
+ if(nosound) return;
+ stopmusic();
+ if(soundvol && musicvol && *name)
+ {
+ defformatstring(file, "packages/%s", name);
+ path(file);
+ if(loadmusic(file))
+ {
+ DELETEA(musicfile);
+ DELETEA(musicdonecmd);
+ musicfile = newstring(file);
+ if(cmd[0]) musicdonecmd = newstring(cmd);
+ Mix_PlayMusic(music, cmd[0] ? 0 : -1);
+ Mix_VolumeMusic((musicvol*MIX_MAX_VOLUME)/255);
+ intret(1);
+ }
+ else
+ {
+ conoutf(CON_ERROR, "could not play music: %s", file);
+ intret(0);
+ }
+ }
}
COMMANDN(music, startmusic, "ss");
static Mix_Chunk *loadwav(const char *name)
{
- Mix_Chunk *c = NULL;
- stream *z = openzipfile(name, "rb");
- if(z)
- {
- SDL_RWops *rw = z->rwops();
- if(rw)
- {
- c = Mix_LoadWAV_RW(rw, 0);
- SDL_FreeRW(rw);
- }
- delete z;
- }
- if(!c) c = Mix_LoadWAV(findfile(name, "rb"));
- return c;
+ Mix_Chunk *c = NULL;
+ stream *z = openzipfile(name, "rb");
+ if(z)
+ {
+ SDL_RWops *rw = z->rwops();
+ if(rw)
+ {
+ c = Mix_LoadWAV_RW(rw, 0);
+ SDL_FreeRW(rw);
+ }
+ delete z;
+ }
+ if(!c) c = Mix_LoadWAV(findfile(name, "rb"));
+ return c;
}
template<class T> static void scalewav(T* dst, T* src, size_t len, int scale)
{
- len /= sizeof(T);
- const T* end = src + len;
- if(scale==2) for(; src < end; src++, dst += scale)
- {
- T s = src[0];
- dst[0] = s;
- dst[1] = s;
- }
- else if(scale==4) for(; src < end; src++, dst += scale)
- {
- T s = src[0];
- dst[0] = s;
- dst[1] = s;
- dst[2] = s;
- dst[3] = s;
- }
- else for(; src < end; src++)
- {
- T s = src[0];
- loopi(scale) *dst++ = s;
- }
+ len /= sizeof(T);
+ const T* end = src + len;
+ if(scale==2) for(; src < end; src++, dst += scale)
+ {
+ T s = src[0];
+ dst[0] = s;
+ dst[1] = s;
+ }
+ else if(scale==4) for(; src < end; src++, dst += scale)
+ {
+ T s = src[0];
+ dst[0] = s;
+ dst[1] = s;
+ dst[2] = s;
+ dst[3] = s;
+ }
+ else for(; src < end; src++)
+ {
+ T s = src[0];
+ loopi(scale) *dst++ = s;
+ }
}
static Mix_Chunk *loadwavscaled(const char *name)
{
- int mixerfreq = 0;
- Uint16 mixerformat = 0;
- int mixerchannels = 0;
- if(!Mix_QuerySpec(&mixerfreq, &mixerformat, &mixerchannels)) return NULL;
-
- SDL_AudioSpec spec;
- Uint8 *audiobuf = NULL;
- Uint32 audiolen = 0;
- stream *z = openzipfile(name, "rb");
- if(z)
- {
- SDL_RWops *rw = z->rwops();
- if(rw)
- {
- SDL_LoadWAV_RW(rw, 0, &spec, &audiobuf, &audiolen);
- SDL_FreeRW(rw);
- }
- delete z;
- }
- if(!audiobuf) SDL_LoadWAV(findfile(name, "rb"), &spec, &audiobuf, &audiolen);
- if(!audiobuf) return NULL;
- int samplesize = ((spec.format&0xFF)/8) * spec.channels;
- int scale = mixerfreq / spec.freq;
- if(scale >= 2)
- {
- Uint8 *scalebuf = (Uint8*)SDL_malloc(audiolen * scale);
- if(scalebuf)
- {
- switch(samplesize)
- {
- case 1: scalewav((uchar*)scalebuf, (uchar*)audiobuf, audiolen, scale); break;
- case 2: scalewav((ushort*)scalebuf, (ushort*)audiobuf, audiolen, scale); break;
- case 4: scalewav((uint*)scalebuf, (uint*)audiobuf, audiolen, scale); break;
- case 8: scalewav((ullong*)scalebuf, (ullong*)audiobuf, audiolen, scale); break;
- default: SDL_free(scalebuf); scalebuf = NULL; break;
- }
- if(scalebuf)
- {
- SDL_free(audiobuf);
- audiobuf = scalebuf;
- audiolen *= scale;
- spec.freq *= scale;
- }
- }
- }
- if(spec.freq != mixerfreq || spec.format != mixerformat || spec.channels != mixerchannels)
- {
- SDL_AudioCVT cvt;
- if(SDL_BuildAudioCVT(&cvt, spec.format, spec.channels, spec.freq, mixerformat, mixerchannels, mixerfreq) < 0)
- {
- SDL_free(audiobuf);
- return NULL;
- }
- if(cvt.filters[0])
- {
- cvt.len = audiolen & ~(samplesize-1);
- cvt.buf = (Uint8*)SDL_malloc(cvt.len * cvt.len_mult);
- if(!cvt.buf) { SDL_free(audiobuf); return NULL; }
- SDL_memcpy(cvt.buf, audiobuf, cvt.len);
- SDL_free(audiobuf);
- if(SDL_ConvertAudio(&cvt) < 0) { SDL_free(cvt.buf); return NULL; }
- audiobuf = cvt.buf;
- audiolen = cvt.len_cvt;
- }
- }
- Mix_Chunk *c = Mix_QuickLoad_RAW(audiobuf, audiolen);
- if(!c) { SDL_free(audiobuf); return NULL; }
- c->allocated = 1;
- return c;
+ int mixerfreq = 0;
+ Uint16 mixerformat = 0;
+ int mixerchannels = 0;
+ if(!Mix_QuerySpec(&mixerfreq, &mixerformat, &mixerchannels)) return NULL;
+
+ SDL_AudioSpec spec;
+ Uint8 *audiobuf = NULL;
+ Uint32 audiolen = 0;
+ stream *z = openzipfile(name, "rb");
+ if(z)
+ {
+ SDL_RWops *rw = z->rwops();
+ if(rw)
+ {
+ SDL_LoadWAV_RW(rw, 0, &spec, &audiobuf, &audiolen);
+ SDL_FreeRW(rw);
+ }
+ delete z;
+ }
+ if(!audiobuf) SDL_LoadWAV(findfile(name, "rb"), &spec, &audiobuf, &audiolen);
+ if(!audiobuf) return NULL;
+ int samplesize = ((spec.format&0xFF)/8) * spec.channels;
+ int scale = mixerfreq / spec.freq;
+ if(scale >= 2)
+ {
+ Uint8 *scalebuf = (Uint8*)SDL_malloc(audiolen * scale);
+ if(scalebuf)
+ {
+ switch(samplesize)
+ {
+ case 1: scalewav((uchar*)scalebuf, (uchar*)audiobuf, audiolen, scale); break;
+ case 2: scalewav((ushort*)scalebuf, (ushort*)audiobuf, audiolen, scale); break;
+ case 4: scalewav((uint*)scalebuf, (uint*)audiobuf, audiolen, scale); break;
+ case 8: scalewav((ullong*)scalebuf, (ullong*)audiobuf, audiolen, scale); break;
+ default: SDL_free(scalebuf); scalebuf = NULL; break;
+ }
+ if(scalebuf)
+ {
+ SDL_free(audiobuf);
+ audiobuf = scalebuf;
+ audiolen *= scale;
+ spec.freq *= scale;
+ }
+ }
+ }
+ if(spec.freq != mixerfreq || spec.format != mixerformat || spec.channels != mixerchannels)
+ {
+ SDL_AudioCVT cvt;
+ if(SDL_BuildAudioCVT(&cvt, spec.format, spec.channels, spec.freq, mixerformat, mixerchannels, mixerfreq) < 0)
+ {
+ SDL_free(audiobuf);
+ return NULL;
+ }
+ if(cvt.filters[0])
+ {
+ cvt.len = audiolen & ~(samplesize-1);
+ cvt.buf = (Uint8*)SDL_malloc(cvt.len * cvt.len_mult);
+ if(!cvt.buf) { SDL_free(audiobuf); return NULL; }
+ SDL_memcpy(cvt.buf, audiobuf, cvt.len);
+ SDL_free(audiobuf);
+ if(SDL_ConvertAudio(&cvt) < 0) { SDL_free(cvt.buf); return NULL; }
+ audiobuf = cvt.buf;
+ audiolen = cvt.len_cvt;
+ }
+ }
+ Mix_Chunk *c = Mix_QuickLoad_RAW(audiobuf, audiolen);
+ if(!c) { SDL_free(audiobuf); return NULL; }
+ c->allocated = 1;
+ return c;
}
VARFP(fixwav, 0, 1, 1, initwarning("sound configuration", INIT_LOAD, CHANGE_SOUND));
bool soundsample::load(bool msg)
{
- if(chunk) return true;
- if(!name[0]) return false;
-
- static const char * const exts[] = { "", ".wav", ".ogg" };
- string filename;
- loopi(sizeof(exts)/sizeof(exts[0]))
- {
- formatstring(filename, "packages/sounds/%s%s", name, exts[i]);
- if(msg && !i) renderprogress(0, filename);
- path(filename);
- if(fixwav)
- {
- size_t len = strlen(filename);
- if(len >= 4 && !strcasecmp(filename + len - 4, ".wav"))
- {
- chunk = loadwavscaled(filename);
- if(chunk) return true;
- }
- }
- chunk = loadwav(filename);
- if(chunk) return true;
- }
-
- conoutf(CON_ERROR, "failed to load sample: packages/sounds/%s", name);
- return false;
+ if(chunk) return true;
+ if(!name[0]) return false;
+
+ static const char * const exts[] = { "", ".wav", ".ogg" };
+ string filename;
+ loopi(sizeof(exts)/sizeof(exts[0]))
+ {
+ formatstring(filename, "packages/sounds/%s%s", name, exts[i]);
+ if(msg && !i) renderprogress(0, filename);
+ path(filename);
+ if(fixwav)
+ {
+ size_t len = strlen(filename);
+ if(len >= 4 && !strcasecmp(filename + len - 4, ".wav"))
+ {
+ chunk = loadwavscaled(filename);
+ if(chunk) return true;
+ }
+ }
+ chunk = loadwav(filename);
+ if(chunk) return true;
+ }
+
+ conoutf(CON_ERROR, "failed to load sample: packages/sounds/%s", name);
+ return false;
}
static hashnameset<soundsample> samples;
static void cleanupsamples()
{
- enumerate(samples, soundsample, s, s.cleanup());
+ enumerate(samples, soundsample, s, s.cleanup());
}
static struct soundtype
{
- vector<soundslot> slots;
- vector<soundconfig> configs;
-
- int findsound(const char *name, int vol)
- {
- loopv(configs)
- {
- soundconfig &s = configs[i];
- loopj(s.numslots)
- {
- soundslot &c = slots[s.slots+j];
- if(!strcmp(c.sample->name, name) && (!vol || c.volume==vol)) return i;
- }
- }
- return -1;
- }
-
- int addslot(const char *name, int vol)
- {
- soundsample *s = samples.access(name);
- if(!s)
- {
- char *n = newstring(name);
- s = &samples[n];
- s->name = n;
- s->chunk = NULL;
- }
- soundslot *oldslots = slots.getbuf();
- int oldlen = slots.length();
- soundslot &slot = slots.add();
- // soundslots.add() may relocate slot pointers
- if(slots.getbuf() != oldslots) loopv(channels)
- {
- soundchannel &chan = channels[i];
- if(chan.inuse && chan.slot >= oldslots && chan.slot < &oldslots[oldlen])
- chan.slot = &slots[chan.slot - oldslots];
- }
- slot.sample = s;
- slot.volume = vol ? vol : 100;
- return oldlen;
- }
-
- int addsound(const char *name, int vol, int maxuses = 0)
- {
- soundconfig &s = configs.add();
- s.slots = addslot(name, vol);
- s.numslots = 1;
- s.maxuses = maxuses;
- return configs.length()-1;
- }
-
- void addalt(const char *name, int vol)
- {
- if(configs.empty()) return;
- addslot(name, vol);
- configs.last().numslots++;
- }
-
- void clear()
- {
- slots.setsize(0);
- configs.setsize(0);
- }
-
- void reset()
- {
- loopv(channels)
- {
- soundchannel &chan = channels[i];
- if(chan.inuse && slots.inbuf(chan.slot))
- {
- Mix_HaltChannel(i);
- freechannel(i);
- }
- }
- clear();
- }
-
- void preloadsound(int n)
- {
- if(nosound || !configs.inrange(n)) return;
- soundconfig &config = configs[n];
- loopk(config.numslots) slots[config.slots+k].sample->load(true);
- }
-
- bool playing(const soundchannel &chan, const soundconfig &config) const
- {
- return chan.inuse && config.hasslot(chan.slot, slots);
- }
+ vector<soundslot> slots;
+ vector<soundconfig> configs;
+
+ int findsound(const char *name, int vol)
+ {
+ loopv(configs)
+ {
+ soundconfig &s = configs[i];
+ loopj(s.numslots)
+ {
+ soundslot &c = slots[s.slots+j];
+ if(!strcmp(c.sample->name, name) && (!vol || c.volume==vol)) return i;
+ }
+ }
+ return -1;
+ }
+
+ int addslot(const char *name, int vol)
+ {
+ soundsample *s = samples.access(name);
+ if(!s)
+ {
+ char *n = newstring(name);
+ s = &samples[n];
+ s->name = n;
+ s->chunk = NULL;
+ }
+ soundslot *oldslots = slots.getbuf();
+ int oldlen = slots.length();
+ soundslot &slot = slots.add();
+ // soundslots.add() may relocate slot pointers
+ if(slots.getbuf() != oldslots) loopv(channels)
+ {
+ soundchannel &chan = channels[i];
+ if(chan.inuse && chan.slot >= oldslots && chan.slot < &oldslots[oldlen])
+ chan.slot = &slots[chan.slot - oldslots];
+ }
+ slot.sample = s;
+ slot.volume = vol ? vol : 100;
+ return oldlen;
+ }
+
+ int addsound(const char *name, int vol, int maxuses = 0)
+ {
+ soundconfig &s = configs.add();
+ s.slots = addslot(name, vol);
+ s.numslots = 1;
+ s.maxuses = maxuses;
+ return configs.length()-1;
+ }
+
+ void addalt(const char *name, int vol)
+ {
+ if(configs.empty()) return;
+ addslot(name, vol);
+ configs.last().numslots++;
+ }
+
+ void clear()
+ {
+ slots.setsize(0);
+ configs.setsize(0);
+ }
+
+ void reset()
+ {
+ loopv(channels)
+ {
+ soundchannel &chan = channels[i];
+ if(chan.inuse && slots.inbuf(chan.slot))
+ {
+ Mix_HaltChannel(i);
+ freechannel(i);
+ }
+ }
+ clear();
+ }
+
+ void preloadsound(int n)
+ {
+ if(nosound || !configs.inrange(n)) return;
+ soundconfig &config = configs[n];
+ loopk(config.numslots) slots[config.slots+k].sample->load(true);
+ }
+
+ bool playing(const soundchannel &chan, const soundconfig &config) const
+ {
+ return chan.inuse && config.hasslot(chan.slot, slots);
+ }
} gamesounds, mapsounds;
void registersound(char *name, int *vol) { intret(gamesounds.addsound(name, *vol, 0)); }
@@ -556,318 +555,313 @@ ICOMMAND(nummapsounds, "", (), intret(mapsounds.configs.length()));
void soundreset()
{
- gamesounds.reset();
+ gamesounds.reset();
}
COMMAND(soundreset, "");
void mapsoundreset()
{
- mapsounds.reset();
+ mapsounds.reset();
}
COMMAND(mapsoundreset, "");
void resetchannels()
{
- loopv(channels) if(channels[i].inuse) freechannel(i);
- channels.shrink(0);
+ loopv(channels) if(channels[i].inuse) freechannel(i);
+ channels.shrink(0);
}
void clear_sound()
{
- if(nosound) return;
- stopmusic();
+ if(nosound) return;
+ stopmusic();
- cleanupsamples();
- gamesounds.clear();
- mapsounds.clear();
- samples.clear();
- Mix_CloseAudio();
- resetchannels();
+ cleanupsamples();
+ gamesounds.clear();
+ mapsounds.clear();
+ samples.clear();
+ Mix_CloseAudio();
+ resetchannels();
}
void stopmapsounds()
{
- loopv(channels) if(channels[i].inuse && channels[i].ent)
- {
- Mix_HaltChannel(i);
- freechannel(i);
- }
+ loopv(channels) if(channels[i].inuse && channels[i].ent)
+ {
+ Mix_HaltChannel(i);
+ freechannel(i);
+ }
}
void clearmapsounds()
{
- stopmapsounds();
- mapsounds.clear();
+ stopmapsounds();
+ mapsounds.clear();
}
void stopmapsound(extentity *e)
{
- loopv(channels)
- {
- soundchannel &chan = channels[i];
- if(chan.inuse && chan.ent == e)
- {
- Mix_HaltChannel(i);
- freechannel(i);
- }
- }
+ loopv(channels)
+ {
+ soundchannel &chan = channels[i];
+ if(chan.inuse && chan.ent == e)
+ {
+ Mix_HaltChannel(i);
+ freechannel(i);
+ }
+ }
}
void checkmapsounds()
{
- const vector<extentity *> &ents = entities::getents();
- loopv(ents)
- {
- extentity &e = *ents[i];
- if(e.type!=ET_SOUND) continue;
- if(camera1->o.dist(e.o) < e.attr2)
- {
- if(!(e.flags&EF_SOUND)) playsound(e.attr1, NULL, &e, SND_MAP, -1);
- }
- else if(e.flags&EF_SOUND) stopmapsound(&e);
- }
+ const vector<extentity *> &ents = entities::getents();
+ loopv(ents)
+ {
+ extentity &e = *ents[i];
+ if(e.type!=ET_SOUND) continue;
+ if(camera1->o.dist(e.o) < e.attr2)
+ {
+ if(!(e.flags&EF_SOUND)) playsound(e.attr1, NULL, &e, SND_MAP, -1);
+ }
+ else if(e.flags&EF_SOUND) stopmapsound(&e);
+ }
}
VAR(stereo, 0, 1, 1);
bool updatechannel(soundchannel &chan)
{
- if(!chan.slot) return false;
- int vol = soundvol, pan = 255/2;
- if(chan.hasloc())
- {
- vec v;
- float dist = chan.loc.dist(camera1->o, v);
- int rad = 0;
- if(chan.ent)
- {
- rad = chan.ent->attr2;
- if(chan.ent->attr3)
- {
- rad -= chan.ent->attr3;
- dist -= chan.ent->attr3;
- }
- }
- else if(chan.radius > 0) rad = chan.radius;
- if(rad > 0) vol -= int(clamp(dist/rad, 0.0f, 1.0f)*soundvol); // simple mono distance attenuation
- if(stereo && (v.x != 0 || v.y != 0) && dist>0)
- {
- v.rotate_around_z(-camera1->yaw*RAD);
- pan = int(255.9f*(0.5f - 0.5f*v.x/v.magnitude2())); // range is from 0 (left) to 255 (right)
- }
- }
- vol = (vol*MIX_MAX_VOLUME*chan.slot->volume)/255/255;
- vol = min(vol, MIX_MAX_VOLUME);
- if(vol == chan.volume && pan == chan.pan) return false;
- chan.volume = vol;
- chan.pan = pan;
- chan.dirty = true;
- return true;
+ if(!chan.slot) return false;
+ int vol = soundvol, pan = 255/2;
+ if(chan.hasloc())
+ {
+ vec v;
+ float dist = chan.loc.dist(camera1->o, v);
+ int rad = 0;
+ if(chan.ent)
+ {
+ rad = chan.ent->attr2;
+ if(chan.ent->attr3)
+ {
+ rad -= chan.ent->attr3;
+ dist -= chan.ent->attr3;
+ }
+ }
+ else if(chan.radius > 0) rad = chan.radius;
+ if(rad > 0) vol -= int(clamp(dist/rad, 0.0f, 1.0f)*soundvol); // simple mono distance attenuation
+ if(stereo && (v.x != 0 || v.y != 0) && dist>0)
+ {
+ v.rotate_around_z(-camera1->yaw*RAD);
+ pan = int(255.9f*(0.5f - 0.5f*v.x/v.magnitude2())); // range is from 0 (left) to 255 (right)
+ }
+ }
+ vol = (vol*MIX_MAX_VOLUME*chan.slot->volume)/255/255;
+ vol = min(vol, MIX_MAX_VOLUME);
+ if(vol == chan.volume && pan == chan.pan) return false;
+ chan.volume = vol;
+ chan.pan = pan;
+ chan.dirty = true;
+ return true;
}
void reclaimchannels()
{
- loopv(channels)
- {
- soundchannel &chan = channels[i];
- if(chan.inuse && !Mix_Playing(i)) freechannel(i);
- }
+ loopv(channels)
+ {
+ soundchannel &chan = channels[i];
+ if(chan.inuse && !Mix_Playing(i)) freechannel(i);
+ }
}
void syncchannels()
{
- loopv(channels)
- {
- soundchannel &chan = channels[i];
- if(chan.inuse && chan.hasloc() && updatechannel(chan)) syncchannel(chan);
- }
+ loopv(channels)
+ {
+ soundchannel &chan = channels[i];
+ if(chan.inuse && chan.hasloc() && updatechannel(chan)) syncchannel(chan);
+ }
}
VARP(minimizedsounds, 0, 0, 1);
void updatesounds()
{
- if(nosound) return;
- if(minimized && !minimizedsounds) stopsounds();
- else
- {
- reclaimchannels();
- if(mainmenu) stopmapsounds();
- else checkmapsounds();
- syncchannels();
- }
- if(music)
- {
- if(!Mix_PlayingMusic()) musicdone();
- else if(Mix_PausedMusic()) Mix_ResumeMusic();
- }
+ if(nosound) return;
+ if(minimized && !minimizedsounds) stopsounds();
+ else
+ {
+ reclaimchannels();
+ if(mainmenu) stopmapsounds();
+ else checkmapsounds();
+ syncchannels();
+ }
+ if(music)
+ {
+ if(!Mix_PlayingMusic()) musicdone();
+ else if(Mix_PausedMusic()) Mix_ResumeMusic();
+ }
}
VARP(maxsoundsatonce, 0, 7, 100);
-VAR(dbgsound, 0, 0, 1);
-
void preloadsound(int n)
{
- gamesounds.preloadsound(n);
+ gamesounds.preloadsound(n);
}
void preloadmapsound(int n)
{
- mapsounds.preloadsound(n);
+ mapsounds.preloadsound(n);
}
void preloadmapsounds()
{
- const vector<extentity *> &ents = entities::getents();
- loopv(ents)
- {
- extentity &e = *ents[i];
- if(e.type==ET_SOUND) mapsounds.preloadsound(e.attr1);
- }
+ const vector<extentity *> &ents = entities::getents();
+ loopv(ents)
+ {
+ extentity &e = *ents[i];
+ if(e.type==ET_SOUND) mapsounds.preloadsound(e.attr1);
+ }
}
int playsound(int n, const vec *loc, extentity *ent, int flags, int loops, int fade, int chanid, int radius, int expire)
{
- if(nosound || !soundvol || (minimized && !minimizedsounds)) return -1;
-
- soundtype &sounds = ent || flags&SND_MAP ? mapsounds : gamesounds;
- if(!sounds.configs.inrange(n)) { conoutf(CON_WARN, "unregistered sound: %d", n); return -1; }
- soundconfig &config = sounds.configs[n];
-
- if(loc)
- {
- // cull sounds that are unlikely to be heard
- int maxrad = game::maxsoundradius(n);
- if(radius <= 0 || maxrad < radius) radius = maxrad;
- if(camera1->o.dist(*loc) > 1.5f*radius)
- {
- if(channels.inrange(chanid) && sounds.playing(channels[chanid], config))
- {
- Mix_HaltChannel(chanid);
- freechannel(chanid);
- }
- return -1;
- }
- }
-
- if(chanid < 0)
- {
- if(config.maxuses)
- {
- int uses = 0;
- loopv(channels) if(sounds.playing(channels[i], config) && ++uses >= config.maxuses) return -1;
- }
-
- // avoid bursts of sounds with heavy packetloss and in sp
- static int soundsatonce = 0, lastsoundmillis = 0;
- if(totalmillis == lastsoundmillis) soundsatonce++; else soundsatonce = 1;
- lastsoundmillis = totalmillis;
- if(maxsoundsatonce && soundsatonce > maxsoundsatonce) return -1;
- }
-
- if(channels.inrange(chanid))
- {
- soundchannel &chan = channels[chanid];
- if(sounds.playing(chan, config))
- {
- if(loc) chan.loc = *loc;
- else if(chan.hasloc()) chan.clearloc();
- return chanid;
- }
- }
- if(fade < 0) return -1;
-
- soundslot &slot = sounds.slots[config.chooseslot(flags)];
- if(!slot.sample->chunk && !slot.sample->load()) return -1;
-
- if(dbgsound) conoutf(CON_DEBUG, "sound: %s", slot.sample->name);
-
- chanid = -1;
- loopv(channels) if(!channels[i].inuse) { chanid = i; break; }
- if(chanid < 0 && channels.length() < maxchannels) chanid = channels.length();
- if(chanid < 0) loopv(channels) if(!channels[i].volume) { chanid = i; break; }
- if(chanid < 0) return -1;
-
- soundchannel &chan = newchannel(chanid, &slot, loc, ent, flags, radius);
- updatechannel(chan);
- int playing = -1;
- if(fade)
- {
- Mix_Volume(chanid, chan.volume);
- playing = expire >= 0 ? Mix_FadeInChannelTimed(chanid, slot.sample->chunk, loops, fade, expire) : Mix_FadeInChannel(chanid, slot.sample->chunk, loops, fade);
- }
- else playing = expire >= 0 ? Mix_PlayChannelTimed(chanid, slot.sample->chunk, loops, expire) : Mix_PlayChannel(chanid, slot.sample->chunk, loops);
- if(playing >= 0) syncchannel(chan);
- else freechannel(chanid);
- return playing;
+ if(nosound || !soundvol || (minimized && !minimizedsounds)) return -1;
+
+ soundtype &sounds = ent || flags&SND_MAP ? mapsounds : gamesounds;
+ if(!sounds.configs.inrange(n)) { conoutf(CON_WARN, "unregistered sound: %d", n); return -1; }
+ soundconfig &config = sounds.configs[n];
+
+ if(loc)
+ {
+ // cull sounds that are unlikely to be heard
+ int maxrad = game::maxsoundradius(n);
+ if(radius <= 0 || maxrad < radius) radius = maxrad;
+ if(camera1->o.dist(*loc) > 1.5f*radius)
+ {
+ if(channels.inrange(chanid) && sounds.playing(channels[chanid], config))
+ {
+ Mix_HaltChannel(chanid);
+ freechannel(chanid);
+ }
+ return -1;
+ }
+ }
+
+ if(chanid < 0)
+ {
+ if(config.maxuses)
+ {
+ int uses = 0;
+ loopv(channels) if(sounds.playing(channels[i], config) && ++uses >= config.maxuses) return -1;
+ }
+
+ // avoid bursts of sounds with heavy packetloss and in sp
+ static int soundsatonce = 0, lastsoundmillis = 0;
+ if(totalmillis == lastsoundmillis) soundsatonce++; else soundsatonce = 1;
+ lastsoundmillis = totalmillis;
+ if(maxsoundsatonce && soundsatonce > maxsoundsatonce) return -1;
+ }
+
+ if(channels.inrange(chanid))
+ {
+ soundchannel &chan = channels[chanid];
+ if(sounds.playing(chan, config))
+ {
+ if(loc) chan.loc = *loc;
+ else if(chan.hasloc()) chan.clearloc();
+ return chanid;
+ }
+ }
+ if(fade < 0) return -1;
+
+ soundslot &slot = sounds.slots[config.chooseslot(flags)];
+ if(!slot.sample->chunk && !slot.sample->load()) return -1;
+
+ chanid = -1;
+ loopv(channels) if(!channels[i].inuse) { chanid = i; break; }
+ if(chanid < 0 && channels.length() < maxchannels) chanid = channels.length();
+ if(chanid < 0) loopv(channels) if(!channels[i].volume) { chanid = i; break; }
+ if(chanid < 0) return -1;
+
+ soundchannel &chan = newchannel(chanid, &slot, loc, ent, flags, radius);
+ updatechannel(chan);
+ int playing = -1;
+ if(fade)
+ {
+ Mix_Volume(chanid, chan.volume);
+ playing = expire >= 0 ? Mix_FadeInChannelTimed(chanid, slot.sample->chunk, loops, fade, expire) : Mix_FadeInChannel(chanid, slot.sample->chunk, loops, fade);
+ }
+ else playing = expire >= 0 ? Mix_PlayChannelTimed(chanid, slot.sample->chunk, loops, expire) : Mix_PlayChannel(chanid, slot.sample->chunk, loops);
+ if(playing >= 0) syncchannel(chan);
+ else freechannel(chanid);
+ return playing;
}
void stopsounds()
{
- loopv(channels) if(channels[i].inuse)
- {
- Mix_HaltChannel(i);
- freechannel(i);
- }
+ loopv(channels) if(channels[i].inuse)
+ {
+ Mix_HaltChannel(i);
+ freechannel(i);
+ }
}
bool stopsound(int n, int chanid, int fade)
{
- if(!gamesounds.configs.inrange(n) || !channels.inrange(chanid) || !channels[chanid].inuse || !gamesounds.playing(channels[chanid], gamesounds.configs[n])) return false;
- if(dbgsound) conoutf(CON_DEBUG, "stopsound: %s", channels[chanid].slot->sample->name);
- if(!fade || !Mix_FadeOutChannel(chanid, fade))
- {
- Mix_HaltChannel(chanid);
- freechannel(chanid);
- }
- return true;
+ if(!gamesounds.configs.inrange(n) || !channels.inrange(chanid) || !channels[chanid].inuse || !gamesounds.playing(channels[chanid], gamesounds.configs[n])) return false;
+ if(!fade || !Mix_FadeOutChannel(chanid, fade))
+ {
+ Mix_HaltChannel(chanid);
+ freechannel(chanid);
+ }
+ return true;
}
int playsoundname(const char *s, const vec *loc, int vol, int flags, int loops, int fade, int chanid, int radius, int expire)
{
- if(!vol) vol = 100;
- int id = gamesounds.findsound(s, vol);
- if(id < 0) id = gamesounds.addsound(s, vol);
- return playsound(id, loc, NULL, flags, loops, fade, chanid, radius, expire);
+ if(!vol) vol = 100;
+ int id = gamesounds.findsound(s, vol);
+ if(id < 0) id = gamesounds.addsound(s, vol);
+ return playsound(id, loc, NULL, flags, loops, fade, chanid, radius, expire);
}
ICOMMAND(sound, "i", (int *n), playsound(*n));
void resetsound()
{
- clearchanges(CHANGE_SOUND);
- if(!nosound)
- {
- cleanupsamples();
- if(music)
- {
- Mix_HaltMusic();
- Mix_FreeMusic(music);
- }
- if(musicstream) musicstream->seek(0, SEEK_SET);
- Mix_CloseAudio();
- }
- initsound();
- resetchannels();
- if(nosound)
- {
- DELETEA(musicfile);
- DELETEA(musicdonecmd);
- music = NULL;
- cleanupsamples();
- return;
- }
- if(music && loadmusic(musicfile))
- {
- Mix_PlayMusic(music, musicdonecmd ? 0 : -1);
- Mix_VolumeMusic((musicvol*MIX_MAX_VOLUME)/255);
- }
- else
- {
- DELETEA(musicfile);
- DELETEA(musicdonecmd);
- }
+ clearchanges(CHANGE_SOUND);
+ if(!nosound)
+ {
+ cleanupsamples();
+ if(music)
+ {
+ Mix_HaltMusic();
+ Mix_FreeMusic(music);
+ }
+ if(musicstream) musicstream->seek(0, SEEK_SET);
+ Mix_CloseAudio();
+ }
+ initsound();
+ resetchannels();
+ if(nosound)
+ {
+ DELETEA(musicfile);
+ DELETEA(musicdonecmd);
+ music = NULL;
+ cleanupsamples();
+ return;
+ }
+ if(music && loadmusic(musicfile))
+ {
+ Mix_PlayMusic(music, musicdonecmd ? 0 : -1);
+ Mix_VolumeMusic((musicvol*MIX_MAX_VOLUME)/255);
+ }
+ else
+ {
+ DELETEA(musicfile);
+ DELETEA(musicdonecmd);
+ }
}
COMMAND(resetsound, "");