From 82023866ab4c7483652e9d4605290e39ced3bec3 Mon Sep 17 00:00:00 2001 From: Magnus Auvinen Date: Thu, 24 May 2007 20:54:08 +0000 Subject: large change. moved around all source. splitted server and client into separate files --- src/engine/client/snd.cpp | 520 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 520 insertions(+) create mode 100644 src/engine/client/snd.cpp (limited to 'src/engine/client/snd.cpp') diff --git a/src/engine/client/snd.cpp b/src/engine/client/snd.cpp new file mode 100644 index 00000000..dd6baa9a --- /dev/null +++ b/src/engine/client/snd.cpp @@ -0,0 +1,520 @@ +#include +#include +#include + +#include + +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 const int64 GLOBAL_SOUND_DELAY = 1000; + +// --- sound --- +class sound_data +{ +public: + short *data; + int num_samples; + int rate; + int channels; + int sustain_start; + int sustain_end; + int64 last_played; +}; + +inline short clamp(int i) +{ + if(i > 0x7fff) + return 0x7fff; + if(i < -0x7fff) + return -0x7fff; + return i; +} + +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=8, + }; + + channel channels[MAX_CHANNELS]; + + virtual void fill(void *output, unsigned long frames) + { + //dbg_msg("snd", "mixing!"); + + short *out = (short*)output; + bool clamp_flag = false; + + int active_channels = 0; + for(unsigned long i = 0; i < frames; i++) + { + int left = 0; + int right = 0; + + for(int c = 0; c < MAX_CHANNELS; c++) + { + if(channels[c].data) + { + if(channels[c].data->channels == 1) + { + left += (1.0f-(channels[c].pan+1.0f)*0.5f) * channels[c].vol * channels[c].data->data[channels[c].tick]; + right += (channels[c].pan+1.0f)*0.5f * channels[c].vol * channels[c].data->data[channels[c].tick]; + channels[c].tick++; + } + else + { + float pl = channels[c].pan<0.0f?-channels[c].pan:1.0f; + float pr = channels[c].pan>0.0f?1.0f-channels[c].pan:1.0f; + left += pl*channels[c].vol * channels[c].data->data[channels[c].tick]; + right += pr*channels[c].vol * channels[c].data->data[channels[c].tick + 1]; + channels[c].tick += 2; + } + + if(channels[c].loop) + { + if(channels[c].data->sustain_start >= 0 && channels[c].tick >= channels[c].data->sustain_end) + channels[c].tick = channels[c].data->sustain_start; + else if(channels[c].tick > channels[c].data->num_samples) + channels[c].tick = 0; + } + else if(channels[c].tick > channels[c].data->num_samples) + channels[c].data = 0; + + if(channels[c].stop == 0) + { + channels[c].stop = -1; + channels[c].data = 0; + } + else if(channels[c].stop > 0) + { + channels[c].vol = channels[c].old_vol * (float)channels[c].stop * NUM_FRAMES_STOP_INV; + channels[c].stop--; + } + if(channels[c].lerp > 0) + { + channels[c].vol = (1.0f - (float)channels[c].lerp * NUM_FRAMES_LERP_INV) * channels[c].new_vol + + (float)channels[c].lerp * NUM_FRAMES_LERP_INV * channels[c].old_vol; + channels[c].lerp--; + } + active_channels++; + } + } + + // TODO: remove these + + *out = clamp(left); // left + if(*out != left) clamp_flag = true; + out++; + *out = clamp(right); // right + if(*out != right) clamp_flag = true; + out++; + } + + if(clamp_flag) + dbg_msg("snd", "CLAMPED!"); + } + + int play(sound_data *sound, unsigned loop, float vol, float pan) + { + if(time_get() - sound->last_played < 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; + 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; + } +}; + +static mixer mixer; +//static sound_data test_sound; + +/* +extern "C" +{ +#include "wavpack/wavpack.h" +}*/ + +/* +static file_stream *read_func_filestream; +static int32_t read_func(void *buff, int32_t bcount) +{ + return read_func_filestream->read(buff, bcount); +} +static uchar *format_samples(int bps, uchar *dst, int32_t *src, uint32_t samcnt) +{ + int32_t temp; + + switch (bps) { + + case 1: + while (samcnt--) + *dst++ = *src++ + 128; + + break; + + case 2: + while (samcnt--) { + *dst++ = (uchar)(temp = *src++); + *dst++ = (uchar)(temp >> 8); + } + + break; + + case 3: + while (samcnt--) { + *dst++ = (uchar)(temp = *src++); + *dst++ = (uchar)(temp >> 8); + *dst++ = (uchar)(temp >> 16); + } + + break; + + case 4: + while (samcnt--) { + *dst++ = (uchar)(temp = *src++); + *dst++ = (uchar)(temp >> 8); + *dst++ = (uchar)(temp >> 16); + *dst++ = (uchar)(temp >> 24); + } + + break; + } + + return dst; +}*/ + +/* +struct sound_holder +{ + sound_data sound; + int next; +}; + +static const int MAX_SOUNDS = 256; +static sound_holder sounds[MAX_SOUNDS]; +static int first_free_sound; + +bool snd_load_wv(const char *filename, sound_data *snd) +{ + // open file + file_stream file; + if(!file.open_r(filename)) + { + dbg_msg("sound/wv", "failed to open file. filename='%s'", filename); + return false; + } + read_func_filestream = &file; + + // get info + WavpackContext *wpc; + char error[128]; + wpc = WavpackOpenFileInput(read_func, error); + if(!wpc) + { + dbg_msg("sound/wv", "failed to open file. err=%s filename='%s'", error, filename); + return false; + } + + + snd->num_samples = WavpackGetNumSamples(wpc); + int bps = WavpackGetBytesPerSample(wpc); + int channels = WavpackGetReducedChannels(wpc); + snd->rate = WavpackGetSampleRate(wpc); + int bits = WavpackGetBitsPerSample(wpc); + + (void)bps; + (void)channels; + (void)bits; + + // decompress + int datasize = snd->num_samples*2; + snd->data = (short*)mem_alloc(datasize, 1); + int totalsamples = 0; + while(1) + { + int buffer[1024*4]; + int samples_unpacked = WavpackUnpackSamples(wpc, buffer, 1024*4); + totalsamples += samples_unpacked; + + if(samples_unpacked) + { + // convert + } + } + + if(snd->num_samples != totalsamples) + { + dbg_msg("sound/wv", "wrong amount of samples. filename='%s'", filename); + mem_free(snd->data); + return false;; + } + + return false; +}*/ + +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; +} + +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; +} + +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; + } + + // skip extra bytes (not used for uncompressed) + //int extra_bytes = fmt[14] | (fmt[15]<<8); + //dbg_msg("sound/wav", "%d", extra_bytes); + //file.skip(extra_bytes); + + // 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); + 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') + { + int smpl[9]; + int loop[6]; + + file.read(smpl, sizeof(smpl)); + + if(smpl[7] > 0) + { + file.read(loop, sizeof(loop)); + sounds[id].sound.sustain_start = loop[2] * sounds[id].sound.channels; + sounds[id].sound.sustain_end = loop[3] * sounds[id].sound.channels; + } + + if(smpl[7] > 1) + file.skip((smpl[7]-1) * sizeof(loop)); + + file.skip(smpl[8]); + state++; + } + else + file.skip(chunk_size); + } + else + file.skip(chunk_size); + } + + if(id >= 0) + 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); +} -- cgit 1.4.1