diff options
Diffstat (limited to 'src/engine/client/snd.cpp')
| -rw-r--r-- | src/engine/client/snd.cpp | 550 |
1 files changed, 0 insertions, 550 deletions
diff --git a/src/engine/client/snd.cpp b/src/engine/client/snd.cpp deleted file mode 100644 index 2fefd6f2..00000000 --- a/src/engine/client/snd.cpp +++ /dev/null @@ -1,550 +0,0 @@ -#include <baselib/system.h> -#include <baselib/audio.h> -#include <baselib/stream/file.h> - -#include <engine/interface.h> -#include <engine/config.h> - -extern "C" { -#include "../../wavpack/wavpack.h" -} - -using namespace baselib; - -static const int NUM_FRAMES_STOP = 512; -static const float NUM_FRAMES_STOP_INV = 1.0f/(float)NUM_FRAMES_STOP; -static const int NUM_FRAMES_LERP = 512; -static const float NUM_FRAMES_LERP_INV = 1.0f/(float)NUM_FRAMES_LERP; - -static const float GLOBAL_VOLUME_SCALE = 0.75f; -static float master_volume = 1.0f; - -static const float GLOBAL_SOUND_DELAY = 0.05f; - -// --- sound --- -class sound_data -{ -public: - sound_data() : - data(0x0), - num_samples(0), - rate(0), - channels(0), - sustain_start(-1), - sustain_end(-1), - last_played(0) - { } - - short *data; - int num_samples; - int rate; - int channels; - int sustain_start; - int sustain_end; - int64 last_played; -}; - -template<typename T> -inline const T clamp(const T val, const T lower, const T upper) -{ - if(val > upper) - return upper; - if(val < lower) - return lower; - return val; -} - -static class mixer : public audio_stream -{ -public: - class channel - { - public: - channel() - { data = 0; lerp = -1; stop = -1; } - - sound_data *data; - int tick; - int loop; - float pan; - float vol; - float old_vol; - float new_vol; - int lerp; - int stop; - }; - - enum - { - MAX_CHANNELS=32, - MAX_FILL_FRAMES=256, - }; - - channel channels[MAX_CHANNELS]; - int buffer[MAX_FILL_FRAMES*2]; - - void fill_mono(int *out, unsigned long frames, channel *c, float dv = 0.0f) - { - float pl = clamp(1.0f - c->pan, 0.0f, 1.0f); - float pr = clamp(1.0f + c->pan, 0.0f, 1.0f); - - for(unsigned long i = 0; i < frames; i++) - { - float val = c->vol * master_volume * c->data->data[c->tick]; - - out[i<<1] += (int)(pl*val); - out[(i<<1)+1] += (int)(pr*val); - c->tick++; - c->vol += dv; - if(c->vol < 0.0f) c->vol = 0.0f; - } - } - - void fill_stereo(int *out, unsigned long frames, channel *c, float dv = 0.0f) - { - float pl = clamp(1.0f - c->pan, 0.0f, 1.0f); - float pr = clamp(1.0f + c->pan, 0.0f, 1.0f); - - for(unsigned long i = 0; i < frames; i++) - { - int vl = (int)(pl*c->vol * master_volume * c->data->data[c->tick]); - int vr = (int)(pr*c->vol * master_volume * c->data->data[c->tick + 1]); - out[i<<1] += vl; - out[(i<<1)+1] += vr; - c->tick += 2; - c->vol += dv; - if(c->vol < 0.0f) c->vol = 0.0f; - } - } - - virtual void fill(void *output, unsigned long frames) - { - short *out = (short*)output; - - dbg_assert(frames <= MAX_FILL_FRAMES, "not enough fill frames in buffer"); - - for(unsigned long i = 0; i < frames; i++) - { - buffer[i<<1] = 0; - buffer[(i<<1)+1] = 0; - } - - for(int c = 0; c < MAX_CHANNELS; c++) - { - unsigned long filled = 0; - while(channels[c].data && filled < frames) - { - unsigned long frames_left = (channels[c].data->num_samples - channels[c].tick) >> (channels[c].data->channels-1); - unsigned long to_fill = frames>frames_left?frames_left:frames; - float dv = 0.0f; - - if(channels[c].stop >= 0) - to_fill = (unsigned)channels[c].stop>frames_left?frames:channels[c].stop; - if(channels[c].loop >= 0 && - channels[c].data->sustain_start >= 0) - { - unsigned long tmp = channels[c].data->sustain_end - channels[c].tick; - to_fill = tmp>frames?frames:tmp; - } - - if(channels[c].lerp >= 0) - { - dv = (channels[c].new_vol - channels[c].old_vol) * NUM_FRAMES_LERP_INV; - } - - if(channels[c].data->channels == 1) - fill_mono(buffer, to_fill, &channels[c], dv); - else - fill_stereo(buffer, to_fill, &channels[c], dv); - - if(channels[c].loop >= 0 && - channels[c].data->sustain_start >= 0 && - channels[c].tick >= channels[c].data->sustain_end) - channels[c].tick = channels[c].data->sustain_start; - - if(channels[c].stop >= 0) - channels[c].stop -= to_fill; - if(channels[c].tick >= channels[c].data->num_samples || - channels[c].stop == 0) - channels[c].data = 0; - - channels[c].lerp -= to_fill; - if(channels[c].lerp < 0) - channels[c].lerp = -1; - - - filled += to_fill; - } - } - - for(unsigned long i = 0; i < frames; i++) - { - out[i<<1] = (short)clamp(buffer[i<<1], -0x7fff, 0x7fff); - out[(i<<1)+1] = (short)clamp(buffer[(i<<1)+1], -0x7fff, 0x7fff); - } - } - - int play(sound_data *sound, unsigned loop, float vol, float pan) - { - if(time_get() - sound->last_played < (int64)(time_freq()*GLOBAL_SOUND_DELAY)) - return -1; - - for(int c = 0; c < MAX_CHANNELS; c++) - { - if(channels[c].data == 0) - { - channels[c].data = sound; - channels[c].tick = 0; - channels[c].loop = loop; - channels[c].vol = vol * GLOBAL_VOLUME_SCALE; - channels[c].pan = pan; - channels[c].stop = -1; - channels[c].lerp = -1; - sound->last_played = time_get(); - return c; - } - } - - return -1; - } - - void stop(int id) - { - dbg_assert(id >= 0 && id < MAX_CHANNELS, "id out of bounds"); - channels[id].old_vol = channels[id].vol; - channels[id].stop = NUM_FRAMES_STOP; - } - - void set_vol(int id, float vol) - { - dbg_assert(id >= 0 && id < MAX_CHANNELS, "id out of bounds"); - channels[id].new_vol = vol * GLOBAL_VOLUME_SCALE; - channels[id].old_vol = channels[id].vol; - channels[id].lerp = NUM_FRAMES_LERP; - } -} mixer; - -struct sound_holder -{ - sound_data sound; - int next; -}; - -static const int MAX_SOUNDS = 1024; -static sound_holder sounds[MAX_SOUNDS]; -static int first_free_sound; - -bool snd_init() -{ - first_free_sound = 0; - for(int i = 0; i < MAX_SOUNDS; i++) - sounds[i].next = i+1; - sounds[MAX_SOUNDS-1].next = -1; - return mixer.create(); -} - -bool snd_shutdown() -{ - mixer.destroy(); - return true; -} - -float snd_get_master_volume() -{ - return master_volume; -} - -void snd_set_master_volume(float val) -{ - if(val < 0.0f) - val = 0.0f; - else if(val > 1.0f) - val = 1.0f; - - master_volume = val; -} - -static int snd_alloc_sound() -{ - if(first_free_sound < 0) - return -1; - int id = first_free_sound; - first_free_sound = sounds[id].next; - sounds[id].next = -1; - return id; -} - -static FILE *file = NULL; - -static int read_data(void *buffer, int size) -{ - return fread(buffer, 1, size, file); -} - -int snd_load_wv(const char *filename) -{ - sound_data snd; - int id = -1; - - char error[100]; - - file = fopen(filename, "rb"); - - WavpackContext *context = WavpackOpenFileInput(read_data, error); - if (context) - { - int samples = WavpackGetNumSamples(context); - int bitspersample = WavpackGetBitsPerSample(context); - unsigned int samplerate = WavpackGetSampleRate(context); - int channels = WavpackGetNumChannels(context); - - snd.channels = channels; - snd.rate = samplerate; - - if(snd.channels > 2) - { - dbg_msg("sound/wv", "file is not mono or stereo. filename='%s'", filename); - return -1; - } - - if(snd.rate != 44100) - { - dbg_msg("sound/wv", "file is %d Hz, not 44100 Hz. filename='%s'", snd.rate, filename); - return -1; - } - - if(bitspersample != 16) - { - dbg_msg("sound/wv", "bps is %d, not 16, filname='%s'", bitspersample, filename); - return -1; - } - - int *data = (int *)mem_alloc(4*samples*channels, 1); - WavpackUnpackSamples(context, data, samples); // TODO: check return value - int *src = data; - - snd.data = (short *)mem_alloc(2*samples*channels, 1); - short *dst = snd.data; - - for (int i = 0; i < samples*channels; i++) - *dst++ = (short)*src++; - - mem_free(data); - - snd.num_samples = samples; - snd.sustain_start = -1; - snd.sustain_end = -1; - snd.last_played = 0; - id = snd_alloc_sound(); - sounds[id].sound = snd; - } - else - { - dbg_msg("sound/wv", "failed to open %s: %s", filename, error); - } - - fclose(file); - file = NULL; - - if(id >= 0) - { - if(config.debug) - dbg_msg("sound/wv", "loaded %s", filename); - } - else - { - dbg_msg("sound/wv", "failed to load %s", filename); - } - - return id; -} - -int snd_load_wav(const char *filename) -{ - sound_data snd; - - // open file for reading - file_stream file; - if(!file.open_r(filename)) - { - dbg_msg("sound/wav", "failed to open file. filename='%s'", filename); - return -1; - } - - int id = -1; - int state = 0; - while(1) - { - // read chunk header - unsigned char head[8]; - if(file.read(head, sizeof(head)) != 8) - { - break; - } - - int chunk_size = head[4] | (head[5]<<8) | (head[6]<<16) | (head[7]<<24); - head[4] = 0; - - if(state == 0) - { - // read the riff and wave headers - if(head[0] != 'R' || head[1] != 'I' || head[2] != 'F' || head[3] != 'F') - { - dbg_msg("sound/wav", "not a RIFF file. filename='%s'", filename); - return -1; - } - - unsigned char type[4]; - file.read(type, 4); - - if(type[0] != 'W' || type[1] != 'A' || type[2] != 'V' || type[3] != 'E') - { - dbg_msg("sound/wav", "RIFF file is not a WAVE. filename='%s'", filename); - return -1; - } - - state++; - } - else if(state == 1) - { - // read the format chunk - if(head[0] == 'f' && head[1] == 'm' && head[2] == 't' && head[3] == ' ') - { - unsigned char fmt[16]; - if(file.read(fmt, sizeof(fmt)) != sizeof(fmt)) - { - dbg_msg("sound/wav", "failed to read format. filename='%s'", filename); - return -1; - } - - // decode format - int compression_code = fmt[0] | (fmt[1]<<8); - snd.channels = fmt[2] | (fmt[3]<<8); - snd.rate = fmt[4] | (fmt[5]<<8) | (fmt[6]<<16) | (fmt[7]<<24); - - if(compression_code != 1) - { - dbg_msg("sound/wav", "file is not uncompressed. filename='%s'", filename); - return -1; - } - - if(snd.channels > 2) - { - dbg_msg("sound/wav", "file is not mono or stereo. filename='%s'", filename); - return -1; - } - - if(snd.rate != 44100) - { - dbg_msg("sound/wav", "file is %d Hz, not 44100 Hz. filename='%s'", snd.rate, filename); - return -1; - } - - int bps = fmt[14] | (fmt[15]<<8); - if(bps != 16) - { - dbg_msg("sound/wav", "bps is %d, not 16, filname='%s'", bps, filename); - return -1; - } - - // next state - state++; - } - else - file.skip(chunk_size); - } - else if(state == 2) - { - // read the data - if(head[0] == 'd' && head[1] == 'a' && head[2] == 't' && head[3] == 'a') - { - snd.data = (short*)mem_alloc(chunk_size, 1); - file.read(snd.data, chunk_size); - snd.num_samples = chunk_size/(2); -#if defined(CONF_ARCH_ENDIAN_BIG) - for(unsigned i = 0; i < (unsigned)snd.num_samples; i++) - { - unsigned j = i << 1; - snd.data[i] = ((short)((char*)snd.data)[j]) + ((short)((char*)snd.data)[j+1] << 8); - } -#endif - snd.sustain_start = -1; - snd.sustain_end = -1; - snd.last_played = 0; - id = snd_alloc_sound(); - sounds[id].sound = snd; - state++; - } - else - file.skip(chunk_size); - } - else if(state == 3) - { - if(head[0] == 's' && head[1] == 'm' && head[2] == 'p' && head[3] == 'l') - { - unsigned char smpl[36]; - unsigned char loop[24]; - if(config.debug) - dbg_msg("sound/wav", "got sustain"); - - file.read(smpl, sizeof(smpl)); - unsigned num_loops = (smpl[28] | (smpl[29]<<8) | (smpl[30]<<16) | (smpl[31]<<24)); - unsigned skip = (smpl[32] | (smpl[33]<<8) | (smpl[34]<<16) | (smpl[35]<<24)); - - if(num_loops > 0) - { - file.read(loop, sizeof(loop)); - unsigned start = (loop[8] | (loop[9]<<8) | (loop[10]<<16) | (loop[11]<<24)); - unsigned end = (loop[12] | (loop[13]<<8) | (loop[14]<<16) | (loop[15]<<24)); - sounds[id].sound.sustain_start = start * sounds[id].sound.channels; - sounds[id].sound.sustain_end = end * sounds[id].sound.channels; - } - - if(num_loops > 1) - file.skip((num_loops-1) * sizeof(loop)); - - file.skip(skip); - state++; - } - else - file.skip(chunk_size); - } - else - file.skip(chunk_size); - } - - if(id >= 0) - { - if(config.debug) - dbg_msg("sound/wav", "loaded %s", filename); - } - else - dbg_msg("sound/wav", "failed to load %s", filename); - - return id; -} - -int snd_play(int id, int loop, float vol, float pan) -{ - if(id < 0) - { - dbg_msg("snd", "bad sound id"); - return -1; - } - - dbg_assert(sounds[id].sound.data != 0, "null sound"); - dbg_assert(sounds[id].next == -1, "sound isn't allocated"); - return mixer.play(&sounds[id].sound, loop, vol, pan); -} - -void snd_stop(int id) -{ - if(id >= 0) - mixer.stop(id); -} - -void snd_set_vol(int id, float vol) -{ - if(id >= 0) - mixer.set_vol(id, vol); -} |