From ecfc4fb6deb276a3f9fd2b4d6199b69510917d50 Mon Sep 17 00:00:00 2001 From: avolkov Date: Wed, 29 Jan 2003 19:53:23 +0000 Subject: [PATCH] common decoders for OpenAL and MixSDL git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@595 8092fc87-c524-0410-9efc-e669fe64eaf9 --- sc2/src/sc2code/libs/sound/decoders/Makeinfo | 2 + sc2/src/sc2code/libs/sound/decoders/decoder.c | 709 ++++ sc2/src/sc2code/libs/sound/decoders/decoder.h | 94 + .../libs/sound/decoders/mikmod/AUTHORS | 115 + .../libs/sound/decoders/mikmod/Makeinfo | 3 + .../sc2code/libs/sound/decoders/mikmod/README | 7 + .../libs/sound/decoders/mikmod/drv_nos.c | 103 + .../libs/sound/decoders/mikmod/drv_openal.c | 70 + .../libs/sound/decoders/mikmod/load_mod.c | 452 +++ .../libs/sound/decoders/mikmod/mdreg.c | 53 + .../libs/sound/decoders/mikmod/mdriver.c | 926 ++++++ .../libs/sound/decoders/mikmod/mikmod.h | 710 ++++ .../libs/sound/decoders/mikmod/mikmod_build.h | 9 + .../sound/decoders/mikmod/mikmod_internals.h | 712 ++++ .../libs/sound/decoders/mikmod/mloader.c | 556 ++++ .../libs/sound/decoders/mikmod/mlreg.c | 50 + .../libs/sound/decoders/mikmod/mlutil.c | 301 ++ .../libs/sound/decoders/mikmod/mmalloc.c | 55 + .../libs/sound/decoders/mikmod/mmerror.c | 193 ++ .../sc2code/libs/sound/decoders/mikmod/mmio.c | 437 +++ .../libs/sound/decoders/mikmod/mplayer.c | 2911 +++++++++++++++++ .../libs/sound/decoders/mikmod/munitrk.c | 297 ++ .../sc2code/libs/sound/decoders/mikmod/mwav.c | 186 ++ .../libs/sound/decoders/mikmod/npertab.c | 43 + .../libs/sound/decoders/mikmod/sloader.c | 511 +++ .../libs/sound/decoders/mikmod/virtch.c | 828 +++++ .../libs/sound/decoders/mikmod/virtch2.c | 899 +++++ .../sound/decoders/mikmod/virtch_common.c | 449 +++ sc2/src/sc2code/libs/sound/decoders/wav.c | 184 ++ sc2/src/sc2code/libs/sound/decoders/wav.h | 33 + sc2/src/sc2code/libs/sound/openal/mixer.h | 38 + 31 files changed, 11936 insertions(+) create mode 100644 sc2/src/sc2code/libs/sound/decoders/Makeinfo create mode 100644 sc2/src/sc2code/libs/sound/decoders/decoder.c create mode 100644 sc2/src/sc2code/libs/sound/decoders/decoder.h create mode 100644 sc2/src/sc2code/libs/sound/decoders/mikmod/AUTHORS create mode 100644 sc2/src/sc2code/libs/sound/decoders/mikmod/Makeinfo create mode 100644 sc2/src/sc2code/libs/sound/decoders/mikmod/README create mode 100644 sc2/src/sc2code/libs/sound/decoders/mikmod/drv_nos.c create mode 100644 sc2/src/sc2code/libs/sound/decoders/mikmod/drv_openal.c create mode 100644 sc2/src/sc2code/libs/sound/decoders/mikmod/load_mod.c create mode 100644 sc2/src/sc2code/libs/sound/decoders/mikmod/mdreg.c create mode 100644 sc2/src/sc2code/libs/sound/decoders/mikmod/mdriver.c create mode 100644 sc2/src/sc2code/libs/sound/decoders/mikmod/mikmod.h create mode 100644 sc2/src/sc2code/libs/sound/decoders/mikmod/mikmod_build.h create mode 100644 sc2/src/sc2code/libs/sound/decoders/mikmod/mikmod_internals.h create mode 100644 sc2/src/sc2code/libs/sound/decoders/mikmod/mloader.c create mode 100644 sc2/src/sc2code/libs/sound/decoders/mikmod/mlreg.c create mode 100644 sc2/src/sc2code/libs/sound/decoders/mikmod/mlutil.c create mode 100644 sc2/src/sc2code/libs/sound/decoders/mikmod/mmalloc.c create mode 100644 sc2/src/sc2code/libs/sound/decoders/mikmod/mmerror.c create mode 100644 sc2/src/sc2code/libs/sound/decoders/mikmod/mmio.c create mode 100644 sc2/src/sc2code/libs/sound/decoders/mikmod/mplayer.c create mode 100644 sc2/src/sc2code/libs/sound/decoders/mikmod/munitrk.c create mode 100644 sc2/src/sc2code/libs/sound/decoders/mikmod/mwav.c create mode 100644 sc2/src/sc2code/libs/sound/decoders/mikmod/npertab.c create mode 100644 sc2/src/sc2code/libs/sound/decoders/mikmod/sloader.c create mode 100644 sc2/src/sc2code/libs/sound/decoders/mikmod/virtch.c create mode 100644 sc2/src/sc2code/libs/sound/decoders/mikmod/virtch2.c create mode 100644 sc2/src/sc2code/libs/sound/decoders/mikmod/virtch_common.c create mode 100644 sc2/src/sc2code/libs/sound/decoders/wav.c create mode 100644 sc2/src/sc2code/libs/sound/decoders/wav.h create mode 100644 sc2/src/sc2code/libs/sound/openal/mixer.h diff --git a/sc2/src/sc2code/libs/sound/decoders/Makeinfo b/sc2/src/sc2code/libs/sound/decoders/Makeinfo new file mode 100644 index 000000000..ca15a69eb --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/Makeinfo @@ -0,0 +1,2 @@ +uqm_SUBDIRS="mikmod" +uqm_CFILES="decoder.c wav.c" diff --git a/sc2/src/sc2code/libs/sound/decoders/decoder.c b/sc2/src/sc2code/libs/sound/decoders/decoder.c new file mode 100644 index 000000000..103188d88 --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/decoder.c @@ -0,0 +1,709 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* Sound file decoder for .wav, .mod, .ogg (to be used with OpenAL) + * API is heavily influenced by SDL_sound. + * By Mika Kolehmainen, 2002-10-27 + */ + +#ifdef WIN32 +#include +#include +#endif + +#include +#include +#include +#include "libs/misc.h" +#include "decoder.h" +#include "wav.h" +#include "libs/sound/sound_common.h" +#include "mikmod/mikmod.h" + +MIKMODAPI extern struct MDRIVER drv_openal; + +static char *decoder_info_wav = "Wav"; +static char *decoder_info_mod = "MikMod"; +static char *decoder_info_ogg = "Ogg Vorbis"; + +TFB_DecoderFormats decoder_formats; + +static int file_exists (char *filename) +{ + FILE *fp; + if ((fp = fopen(filename, "rb")) == NULL) + return 0; + + fclose (fp); + return 1; +} + +static size_t ogg_read (void *ptr, size_t size, size_t nmemb, void *datasource) +{ + return fread (ptr, size, nmemb, datasource); +} + +static int ogg_seek (void *datasource, ogg_int64_t offset, int whence) +{ + long off = (long) offset; + return fseek (datasource, off, whence); +} + +static int ogg_close (void *datasource) +{ + return fclose (datasource); +} + +static long ogg_tell(void *datasource) +{ + return ftell (datasource); +} + +static const ov_callbacks ogg_callbacks = +{ + ogg_read, + ogg_seek, + ogg_close, + ogg_tell, +}; + +/* change endianness of 16bit words + * Only works optimal when 'data' is aligned on a 32 bits boundary. + */ +void +SoundDecoder_SwapWords (uint16* data, uint32 size) +{ + uint32 fsize = size & (~3U); + + size -= fsize; + fsize >>= 2; + for (; fsize; fsize--, data += 2) + { + uint32 v = *(uint32*)data; + *(uint32*)data = ((v & 0x00ff00ff) << 8) + | ((v & 0xff00ff00) >> 8); + } + if (size) + { + /* leftover word */ + *data = ((*data & 0x00ff) << 8) | ((*data & 0xff00) >> 8); + } +} + +sint32 SoundDecoder_Init (int flags, TFB_DecoderFormats *formats) +{ + MikMod_RegisterDriver (&drv_openal); + MikMod_RegisterAllLoaders (); + + if (!formats) + { + fprintf (stderr, "SoundDecoder_Init(): missing decoder formats\n"); + return 1; + } + decoder_formats = *formats; + + if (flags & TFB_SOUNDFLAGS_HQAUDIO) + { + md_mode = DMODE_HQMIXER|DMODE_STEREO|DMODE_16BITS|DMODE_INTERP|DMODE_SURROUND; + md_mixfreq = 44100; + md_reverb = 1; + } + else if (flags & TFB_SOUNDFLAGS_LQAUDIO) + { + md_mode = DMODE_SOFT_MUSIC|DMODE_STEREO|DMODE_16BITS; + md_mixfreq = 22050; + md_reverb = 0; + } + else + { + md_mode = DMODE_SOFT_MUSIC|DMODE_STEREO|DMODE_16BITS|DMODE_INTERP; + md_mixfreq = 44100; + md_reverb = 0; + } + + md_pansep = 64; + + if (MikMod_Init ("")) + { + fprintf (stderr, "SoundDecoder_Init(): MikMod_Init failed, %s\n", + MikMod_strerror (MikMod_errno)); + return 1; + } + + return 0; +} + +void SoundDecoder_Uninit (void) +{ + MikMod_Exit (); +} + +TFB_SoundDecoder* SoundDecoder_Load (char *filename, uint32 buffer_size, + uint32 startTime, uint32 runTime) +{ + int i; + + if (!file_exists (filename)) + { + fprintf (stderr, "SoundDecoder_Load(): %s doesn't exist\n", filename); + return NULL; + } + + i = strlen (filename) - 3; + if (!strcmp (&filename[i], "wav")) + { + TFB_SoundDecoder *decoder; + + //fprintf (stderr, "SoundDecoder_Load(): %s interpreted as wav\n", filename); + + decoder = (TFB_SoundDecoder *) HMalloc (sizeof (TFB_SoundDecoder)); + decoder->buffer = NULL; + decoder->buffer_size = 0; + decoder->format = 0; + decoder->frequency = 0; + decoder->looping = false; + decoder->error = SOUNDDECODER_OK; + decoder->length = 0; // FIXME should be calculated + decoder->decoder_info = decoder_info_wav; + decoder->type = SOUNDDECODER_WAV; + decoder->filename = (char *) HMalloc (strlen (filename) + 1); + decoder->start_sample = 0; + decoder->end_sample = 0; + decoder->pos = 0; + strcpy (decoder->filename, filename); + + return decoder; + } + else if (!strcmp (&filename[i], "mod")) + { + TFB_SoundDecoder *decoder; + MODULE *mod; + + //fprintf (stderr, "SoundDecoder_Load(): %s interpreted as mod\n", filename); + + mod = Player_Load (filename, 4, 0); + if (!mod) + { + fprintf (stderr, "SoundDecoder_Load(): couldn't load %s\n", filename); + return NULL; + } + + mod->extspd = 1; + mod->panflag = 1; + mod->wrap = 0; + mod->loop = 1; + + decoder = (TFB_SoundDecoder *) HMalloc (sizeof (TFB_SoundDecoder)); + decoder->buffer = HMalloc (buffer_size); + decoder->buffer_size = buffer_size; + decoder->format = decoder_formats.stereo16; + decoder->frequency = md_mixfreq; + decoder->looping = false; + decoder->error = SOUNDDECODER_OK; + decoder->length = 0; // FIXME way to obtain this from mikmod? + decoder->decoder_info = decoder_info_mod; + decoder->type = SOUNDDECODER_MOD; + decoder->filename = (char *) HMalloc (strlen (filename) + 1); + decoder->start_sample = 0; + decoder->end_sample = 0; + decoder->pos = 0; + strcpy (decoder->filename, filename); + decoder->data = mod; + + return decoder; + } + else if (!strcmp (&filename[i], "ogg")) + { + int rc; + FILE *vfp; + OggVorbis_File *vf; + vorbis_info *vinfo; + TFB_SoundDecoder *decoder; + + //fprintf (stderr, "SoundDecoder_Load(): %s interpreted as ogg\n", filename); + + vf = (OggVorbis_File *) HMalloc (sizeof (OggVorbis_File)); + if (!vf) + { + fprintf (stderr, "SoundDecoder_Load(): couldn't allocate mem for OggVorbis_File\n"); + return NULL; + } + if ((vfp = fopen (filename, "r")) == NULL) + { + fprintf (stderr, "SoundDecoder_Load(): couldn't open %s\n", filename); + HFree (vf); + return NULL; + } + +#ifdef WIN32 + _setmode( _fileno(vfp), _O_BINARY); +#endif + + rc = ov_open_callbacks (vfp, vf, NULL, 0, ogg_callbacks); + if (rc != 0) + { + fprintf (stderr, "SoundDecoder_Load(): ov_open_callbacks failed for %s, error code %d\n", filename, rc); + fclose (vfp); + HFree (vf); + return NULL; + } + + vinfo = ov_info (vf, -1); + if (!vinfo) + { + fprintf (stderr, "SoundDecoder_Load(): failed to retrieve ogg bitstream info for %s\n", filename); + ov_clear (vf); + HFree (vf); + return NULL; + } + + //fprintf (stderr, "SoundDecoder_Load(): ogg bitstream version %d, channels %d, rate %d, length %.2f seconds\n", vinfo->version, vinfo->channels, vinfo->rate, ov_time_total (vf, -1)); + + decoder = (TFB_SoundDecoder *) HMalloc (sizeof (TFB_SoundDecoder)); + decoder->buffer = HMalloc (buffer_size); + decoder->buffer_size = buffer_size; + decoder->frequency = vinfo->rate; + decoder->looping = false; + decoder->error = SOUNDDECODER_OK; + decoder->length = (float) ov_time_total (vf, -1) - (startTime / 1000.0f); + if (runTime && runTime / 1000.0 < decoder->length) + decoder->length = (float)(runTime / 1000.0); + + decoder->start_sample = decoder->frequency * startTime / 1000; + decoder->end_sample = decoder->start_sample + + (unsigned long)(decoder->length * decoder->frequency); + if (decoder->start_sample) + ov_pcm_seek (vf, decoder->start_sample); + + if (vinfo->channels == 1) + { + decoder->format = decoder_formats.mono16; + decoder->pos = decoder->start_sample * 2; + } + else + { + decoder->format = decoder_formats.stereo16; + decoder->pos = decoder->start_sample * 4; + } + + decoder->decoder_info = decoder_info_ogg; + decoder->type = SOUNDDECODER_OGG; + decoder->filename = (char *) HMalloc (strlen (filename) + 1); + strcpy (decoder->filename, filename); + decoder->data = vf; + + return decoder; + } + else + { + fprintf (stderr, "SoundDecoder_Load(): %s file format is unsupported\n", filename); + } + + return NULL; +} + +uint32 SoundDecoder_Decode (TFB_SoundDecoder *decoder) +{ + switch (decoder->type) + { + case SOUNDDECODER_WAV: + { + fprintf (stderr, "SoundDecoder_Decode(): unimplemented for wav\n"); + break; + } + case SOUNDDECODER_MOD: + { + MODULE *mod = (MODULE *) decoder->data; + uint32 decoded_bytes; + + Player_Start (mod); + if (!Player_Active()) + { + if (decoder->looping) + { + fprintf (stderr, "SoundDecoder_Decode(): looping %s\n", decoder->filename); + Player_SetPosition (0); + Player_Start (mod); + } + else + { + fprintf (stderr, "SoundDecoder_Decode(): eof for %s\n", decoder->filename); + decoder->error = SOUNDDECODER_EOF; + return 0; + } + } + decoder->error = SOUNDDECODER_OK; + decoded_bytes = VC_WriteBytes (decoder->buffer, decoder->buffer_size); + + /* MikMod produces output in machine byte-order + * may need to do a word-swap + */ + if (decoded_bytes > 0 && + decoder_formats.big_endian != decoder_formats.want_big_endian && + (decoder->format == decoder_formats.stereo16 || + decoder->format == decoder_formats.mono16)) + { + SoundDecoder_SwapWords ( + decoder->buffer, decoded_bytes); + } + return decoded_bytes; + } + case SOUNDDECODER_OGG: + { + OggVorbis_File *vf = (OggVorbis_File *) decoder->data; + uint32 decoded_bytes = 0, count = 0; + long rc; + int bitstream; + uint32 max_bytes, buffer_size, close_on_done = 0; + char *buffer = (char *) decoder->buffer; + + if (! decoder->looping) + { + if (decoder->format == decoder_formats.stereo16) + max_bytes = decoder->end_sample * 4; + else + max_bytes = decoder->end_sample * 2; + if (max_bytes - decoder->pos < decoder->buffer_size) + { + buffer_size = max_bytes - decoder->pos; + close_on_done = 1; + } + else + { + buffer_size = decoder->buffer_size; + } + } + else + buffer_size = decoder->buffer_size; + + while (decoded_bytes < buffer_size) + { + /* Produce output in requested byte-order */ + rc = ov_read (vf, &buffer[decoded_bytes], + buffer_size - decoded_bytes, + decoder_formats.want_big_endian, 2, 1, &bitstream); + if (rc < 0) + { + decoder->error = SOUNDDECODER_ERROR; + fprintf (stderr, "SoundDecoder_Decode(): error decoding %s, code %d\n", decoder->filename, rc); + return decoded_bytes; + } + if (rc == 0 || close_on_done && buffer_size == decoded_bytes + rc) + { + if (close_on_done) + { + decoded_bytes += rc; + } + if (decoder->looping) + { + SoundDecoder_Rewind (decoder); + if (decoder->error) + { + fprintf (stderr, "SoundDecoder_Decode(): tried to loop %s but couldn't rewind, error code %d\n", decoder->filename, decoder->error); + } + else + { + fprintf (stderr, "SoundDecoder_Decode(): looping %s\n", decoder->filename); + count++; + continue; + } + } + else + decoder->pos += decoded_bytes; + + fprintf (stderr, "SoundDecoder_Decode(): eof for %s\n", decoder->filename); + decoder->error = SOUNDDECODER_EOF; + return decoded_bytes; + } + + decoded_bytes += rc; + //fprintf (stderr, "iter %d rc %d decoded_bytes %d remaining %d\n", count, rc, decoded_bytes, decoder->buffer_size - decoded_bytes); + count++; + } + decoder->pos += decoded_bytes; + decoder->error = SOUNDDECODER_OK; + //fprintf (stderr, "SoundDecoder_Decode(): decoded %d bytes from %s\n", decoded_bytes, decoder->filename); + return decoded_bytes; + } + case SOUNDDECODER_BUF: + decoder->buffer = (char *)decoder->data + decoder->pos; + decoder->error = SOUNDDECODER_EOF; + return (decoder->buffer_size - decoder->pos); + default: + { + fprintf (stderr, "SoundDecoder_Decode(): unknown type %d\n", decoder->type); + break; + } + } + + decoder->error = SOUNDDECODER_ERROR; + return 0; +} + +uint32 SoundDecoder_DecodeAll (TFB_SoundDecoder *decoder) +{ + switch (decoder->type) + { + case SOUNDDECODER_WAV: + { + bool loop; + + LoadWAVFile (decoder->filename ,&decoder->format, &decoder->buffer, + &decoder->buffer_size, &decoder->frequency, &loop); + + if (decoder->buffer_size != 0) + { + + /* WAVs are loaded in little-endian order + * may need to do a word-swap + */ + if (decoder_formats.want_big_endian && + (decoder->format == decoder_formats.stereo16 + || decoder->format == decoder_formats.mono16)) + { + SoundDecoder_SwapWords ( + decoder->buffer, decoder->buffer_size); + } + decoder->type = SOUNDDECODER_BUF; + decoder->data = decoder->buffer; + decoder->pos = 0; + decoder->error = SOUNDDECODER_OK; + } + else + { + decoder->error = SOUNDDECODER_ERROR; + } + + return decoder->buffer_size; + } + case SOUNDDECODER_MOD: + { + fprintf (stderr, "SoundDecoder_DecodeAll(): unimplemented for mod\n"); + break; + } + case SOUNDDECODER_OGG: + { + OggVorbis_File *vf = (OggVorbis_File *) decoder->data; + uint32 decoded_bytes = 0; + long rc; + int bitstream; + uint32 reqbufsize = decoder->buffer_size; + + if (decoder->looping) + { + fprintf (stderr, "SoundDecoder_DecodeAll(): " + "called for %s with looping\n", decoder->filename); + return 0; + } + + if (reqbufsize < 4096) + reqbufsize = 4096; + +#ifdef DEBUG + if (reqbufsize < 16384) + fprintf (stderr, "SoundDecoder_DecodeAll(): WARNING, " + "called with a small buffer (%u)\n", reqbufsize); +#endif + + for (rc = 1; rc > 0; ) + { + if (decoded_bytes >= decoder->buffer_size) + { // need to grow buffer + decoder->buffer_size += reqbufsize; + decoder->buffer = HRealloc ( + decoder->buffer, decoder->buffer_size); + } + + rc = ov_read (vf, (char*)decoder->buffer + decoded_bytes, + decoder->buffer_size - decoded_bytes, + decoder_formats.want_big_endian, 2, 1, &bitstream); + if (rc < 0) + { + decoder->error = SOUNDDECODER_ERROR; + fprintf (stderr, "SoundDecoder_DecodeAll(): " + "error decoding %s, code %d\n", + decoder->filename, rc); + return decoded_bytes; + } + + decoded_bytes += rc; + } + + decoder->buffer_size = decoded_bytes; + ov_clear (vf); + HFree (vf); + decoder->type = SOUNDDECODER_BUF; + decoder->data = decoder->buffer; + decoder->pos = 0; + + decoder->error = SOUNDDECODER_OK; + return decoded_bytes; + break; + } + default: + { + fprintf (stderr, "SoundDecoder_DecodeAll(): unknown type %d\n", decoder->type); + break; + } + } + + decoder->error = SOUNDDECODER_ERROR; + return 0; +} + +void SoundDecoder_Rewind (TFB_SoundDecoder *decoder) +{ + SoundDecoder_Seek (decoder, 0); +} + +// seekTime is specified in mili-seconds +void SoundDecoder_Seek (TFB_SoundDecoder *decoder, uint32 seekTime) +{ + switch (decoder->type) + { + case SOUNDDECODER_WAV: + { + fprintf (stderr, "SoundDecoder_Seek(): unimplemented for wav\n"); + break; + } + case SOUNDDECODER_MOD: + { + MODULE *mod = (MODULE *)decoder->data; + Player_Start (mod); + if (seekTime) + fprintf (stderr, "SoundDecoder_Seek(): non-zero seek positions not supported for mod\n"); + Player_SetPosition (0); + //fprintf (stderr, "SoundDecoder_Rewind(): rewound %s\n", decoder->filename); + decoder->error = SOUNDDECODER_OK; + return; + } + case SOUNDDECODER_OGG: + { + int err; + uint32 pcm_pos = 0; + OggVorbis_File *vf = (OggVorbis_File *) decoder->data; + if (seekTime) + pcm_pos = seekTime * decoder->frequency / 1000; + if ((err = ov_pcm_seek (vf, decoder->start_sample + pcm_pos)) != 0) + { + fprintf (stderr, "SoundDecoder_Seek(): couldn't seek %s, error code %d\n", decoder->filename, err); + break; + } + if (decoder->format == decoder_formats.mono16) + decoder->pos = (pcm_pos + decoder->start_sample) * 2; + else + decoder->pos = (pcm_pos + decoder->start_sample) * 4; + + //fprintf (stderr, "SoundDecoder_Seek(): seek %s\n", decoder->filename); + decoder->error = SOUNDDECODER_OK; + return; + } + case SOUNDDECODER_BUF: + { + decoder->pos = seekTime * decoder->frequency * + (decoder->format == decoder_formats.mono16 ? 2 : 4) / 1000; + decoder->error = SOUNDDECODER_OK; + return; + } + default: + { + fprintf (stderr, "SoundDecoder_Seek(): unknown type %d\n", decoder->type); + break; + } + } + + decoder->error = SOUNDDECODER_ERROR; +} + +void SoundDecoder_Free (TFB_SoundDecoder *decoder) +{ + if (!decoder) + { + return; + } + + switch (decoder->type) + { + case SOUNDDECODER_WAV: + { + //fprintf (stderr, "SoundDecoder_Free(): freeing %s\n", decoder->filename); + break; + } + case SOUNDDECODER_MOD: + { + MODULE *mod = (MODULE *) decoder->data; + //fprintf (stderr, "SoundDecoder_Free(): freeing %s\n", decoder->filename); + Player_Free (mod); + break; + } + case SOUNDDECODER_OGG: + { + OggVorbis_File *vf = (OggVorbis_File *) decoder->data; + //fprintf (stderr, "SoundDecoder_Free(): freeing %s\n", decoder->filename); + ov_clear (vf); + HFree (vf); + break; + } + case SOUNDDECODER_BUF: + break; + default: + { + fprintf (stderr, "SoundDecoder_Free(): unknown type %d\n", decoder->type); + break; + } + } + + HFree (decoder->buffer); + HFree (decoder->filename); + HFree (decoder); +} + +float SoundDecoder_GetTime (TFB_SoundDecoder *decoder) +{ + if (!decoder) + { + return 0.0f; + } + + switch (decoder->type) + { + case SOUNDDECODER_WAV: + { + //fprintf (stderr, "SoundDecoder_GetTime not supported for wav\n"); + return 0.0f; + } + case SOUNDDECODER_MOD: + { + //fprintf (stderr, "SoundDecoder_GetTime not supported for mod\n"); + return 0.0f; + } + case SOUNDDECODER_OGG: + case SOUNDDECODER_BUF: + { + //fprintf (stderr, "SoundDecoder_GetTime not supported for mod\n"); + return ( + (float)( + (decoder->pos >> (decoder->format == decoder_formats.mono16 ? 1 : 2)) + - decoder->start_sample + ) / decoder->frequency); + } + default: + { + fprintf (stderr, "SoundDecoder_GetTime(): unknown type %d\n", decoder->type); + return 0.0f; + } + } +} diff --git a/sc2/src/sc2code/libs/sound/decoders/decoder.h b/sc2/src/sc2code/libs/sound/decoders/decoder.h new file mode 100644 index 000000000..c177453e1 --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/decoder.h @@ -0,0 +1,94 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* Sound file decoder for .wav, .mod, .ogg (to be used with OpenAL) + * API is heavily influenced by SDL_sound. + * By Mika Kolehmainen, 2002-10-27 + */ + +#ifndef DECODER_H +#define DECODER_H + +#include "types.h" + +#ifdef WIN32 +#pragma comment (lib, "vorbisfile.lib") +#endif + +typedef struct tfb_decoderformats +{ + bool big_endian; + bool want_big_endian; + uint32 mono8; + uint32 stereo8; + uint32 mono16; + uint32 stereo16; +} TFB_DecoderFormats; + +typedef struct tfb_sounddecoder +{ + // public + void *buffer; + uint32 buffer_size; + uint32 format; + uint32 frequency; + bool looping; + sint32 error; + float length; // total length in seconds + char *decoder_info; + + // semi-private + sint32 type; + char *filename; + void *data; + uint32 pos; + uint32 start_sample; + uint32 end_sample; + +} TFB_SoundDecoder; + +// return values +enum +{ + SOUNDDECODER_OK, + SOUNDDECODER_ERROR, + SOUNDDECODER_EOF, +}; + +// types +enum +{ + SOUNDDECODER_NONE, + SOUNDDECODER_WAV, + SOUNDDECODER_MOD, + SOUNDDECODER_OGG, + SOUNDDECODER_BUF, +}; + +extern TFB_DecoderFormats decoder_formats; + +sint32 SoundDecoder_Init (int flags, TFB_DecoderFormats* formats); +void SoundDecoder_Uninit (void); +TFB_SoundDecoder* SoundDecoder_Load (char *filename, uint32 buffer_size, + uint32 startTime, uint32 runTime); +uint32 SoundDecoder_Decode (TFB_SoundDecoder *decoder); +uint32 SoundDecoder_DecodeAll (TFB_SoundDecoder *decoder); +float SoundDecoder_GetTime (TFB_SoundDecoder *decoder); +void SoundDecoder_Seek (TFB_SoundDecoder *decoder, uint32 pcm_pos); +void SoundDecoder_Rewind (TFB_SoundDecoder *decoder); +void SoundDecoder_Free (TFB_SoundDecoder *decoder); + +#endif diff --git a/sc2/src/sc2code/libs/sound/decoders/mikmod/AUTHORS b/sc2/src/sc2code/libs/sound/decoders/mikmod/AUTHORS new file mode 100644 index 000000000..e3c638b3f --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/mikmod/AUTHORS @@ -0,0 +1,115 @@ +libmikmod main authors +---------------------- + +* Jean-Paul Mikkers (MikMak) + wrote MikMod and maintained it until version 3. +* Jake Stine (Air Richter) [email doesn't work anymore...] + made decisive contributions to the code (esp. IT support) and maintained + MikMod version 3 until it was discontinued. He still works on the WinAmp + module plugin, roughly based on MikMod. +* Miodrag Vallat + current libmikmod maintainer (since version 3.0.4), made an audit of the + code resulting in many bugs fixed. + +Previous Unix maintainers +------------------------- + +* Steve McIntyre + maintained MikMod'Unix version 2. +* Peter Amstutz + maintained MikMod'Unix version 3.0. + +General contributors +-------------------- + +* Arne de Bruijn + wrote the compressed IT sample support. +* Claudio Matsuoka + wrote the STX loader and submitted bug fixes. +* Sebastiaan A. Megens + fixed various bugs (memory leaks, endianness issues, etc). +* ``UFO'' + wrote the OKT loader. +* Kev Vance + wrote the GDM loader. + +* Paul Fisher made decisive contributions and improvements. +* Alexander Kerkhove fixed an ULT panning effect bug. +* ``Kodiak'' helped on the interfaces of libmikmod. +* Sylvain Marchand make MikMod more portable and GCC compilable. + + +Contributors on the Unix side +----------------------------- + +* Douglas Carmichael + made MikMod work under FreeBSD. +* Chris Conn + wrote the OSS driver. +* Roine Gustaffson + wrote the Digital AudioFile driver. +* Stephan Kanthak + wrote the SGI driver. +* Lutz Vieweg + wrote the AIX and HP-UX drivers. +* Valtteri Vuorikoski + wrote the Sun driver. +* Andy Lo A Foe + wrote the Ultra driver (for the Gravis Ultrasound sound card). +* C Ray C + updated the Ultra driver to work with libmikmod 3. +* ``MenTaLguY'' + autoconfized the Unix libmikmod distribution. +* Tobias Gloth + created the new I/O interface, made the code MT-safe and submitted bug fixes. +* Simon Hosie + wrote the piped output driver, and submitted speed optimizations and bugfixes + for the software mixer. +* Gerd Rausch + wrote the sam9407 driver. + +Contributors on the Windows side +-------------------------------- + +* Brian McKinney + created the DirectSound driver. +* Bjornar Henden + created the Multimedia API windows driver. + +Contributors on the Dos side +---------------------------- + +Their code isn't there anymore, but they contributed to the success of +libmikmod... + +* Jean-Philippe Ajirent wrote the EMS memory routines. +* Peter Breitling ported MikMod to DJGPP. +* Arnout Cosman wrote the PAS driver. +* Mario Koeppen wrote the WSS driver. +* Mike Leibow wrote the GUS driver. +* Jeremy McDonald wrote a fast assembly-language mixer. +* Steffen Rusitschka and Vince Vu wrote the AWE driver. + +Contributors on the Macintosh side +---------------------------------- + +* Anders Bjoerklund + ported libmikmod to the Macintosh. + +Contributors on the OS/2 side +----------------------------- + +* Stefan Tibus + ported libmikmod to OS/2. +* Andrew Zabolotny + improved the existing OS/2 drivers. + +Contributors on the BeOS side +----------------------------- + +* Thomas Neumann + integrated libmikmod into his BeOS APlayer, and contributed many bug fixes. + +-- +If your name is missing, don't hesitate to remind me at + diff --git a/sc2/src/sc2code/libs/sound/decoders/mikmod/Makeinfo b/sc2/src/sc2code/libs/sound/decoders/mikmod/Makeinfo new file mode 100644 index 000000000..d90e0f14c --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/mikmod/Makeinfo @@ -0,0 +1,3 @@ +uqm_CFILES="drv_nos.c drv_openal.c load_mod.c mdreg.c mdriver.c mloader.c + mlreg.c mlutil.c mmalloc.c mmerror.c mmio.c mplayer.c munitrk.c + mwav.c npertab.c sloader.c virtch.c virtch2.c virtch_common.c" diff --git a/sc2/src/sc2code/libs/sound/decoders/mikmod/README b/sc2/src/sc2code/libs/sound/decoders/mikmod/README new file mode 100644 index 000000000..3f7f2bb96 --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/mikmod/README @@ -0,0 +1,7 @@ +NOTE by UQM developers: this is a modified version of mikmod what was +included in SDL_mixer 1.2.4, so some code are (c) Sam Lantinga. + + --> MikMod Sound Libraries Version 3.1.9a + +This version of the library is based on the official UNIX MikMod +library version 3.1.9, found at: http://www.mikmod.org/ diff --git a/sc2/src/sc2code/libs/sound/decoders/mikmod/drv_nos.c b/sc2/src/sc2code/libs/sound/decoders/mikmod/drv_nos.c new file mode 100644 index 000000000..29bdc6609 --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/mikmod/drv_nos.c @@ -0,0 +1,103 @@ +/* MikMod sound library + (c) 1998, 1999, 2000 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Driver for no output + +==============================================================================*/ + +/* + + Written by Jean-Paul Mikkers + +*/ + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "mikmod_internals.h" + +#define ZEROLEN 32768 + +static SBYTE *zerobuf=NULL; + +static BOOL NS_IsThere(void) +{ + return 1; +} + +static BOOL NS_Init(void) +{ + zerobuf=(SBYTE*)_mm_malloc(ZEROLEN); + return VC_Init(); +} + +static void NS_Exit(void) +{ + VC_Exit(); + _mm_free(zerobuf); +} + +static void NS_Update(void) +{ + if (zerobuf) + VC_WriteBytes(zerobuf,ZEROLEN); +} + +MIKMODAPI MDRIVER drv_nos={ + NULL, + "No Sound", + "Nosound Driver v3.0", + 255,255, + "nosound", + + NULL, + NS_IsThere, + VC_SampleLoad, + VC_SampleUnload, + VC_SampleSpace, + VC_SampleLength, + NS_Init, + NS_Exit, + NULL, + VC_SetNumVoices, + VC_PlayStart, + VC_PlayStop, + NS_Update, + NULL, + VC_VoiceSetVolume, + VC_VoiceGetVolume, + VC_VoiceSetFrequency, + VC_VoiceGetFrequency, + VC_VoiceSetPanning, + VC_VoiceGetPanning, + VC_VoicePlay, + VC_VoiceStop, + VC_VoiceStopped, + VC_VoiceGetPosition, + VC_VoiceRealVolume +}; + + +/* ex:set ts=4: */ diff --git a/sc2/src/sc2code/libs/sound/decoders/mikmod/drv_openal.c b/sc2/src/sc2code/libs/sound/decoders/mikmod/drv_openal.c new file mode 100644 index 000000000..b235481eb --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/mikmod/drv_openal.c @@ -0,0 +1,70 @@ +// OpenAL driver for mikmod +// based on SDL driver (c) Sam Lantinga + +#include "mikmod_internals.h" + + +static BOOL ALDRV_IsThere(void) +{ + return 1; +} + + +static BOOL ALDRV_Init(void) +{ + md_mode |= DMODE_SOFT_MUSIC | DMODE_SOFT_SNDFX; + return(VC_Init()); +} + + +static void ALDRV_Exit(void) +{ + VC_Exit(); +} + + +static void ALDRV_Update(void) +{ + /* does nothing, buffers are updated in the background */ +} + + +static BOOL ALDRV_Reset(void) +{ + return 0; +} + + +MIKMODAPI MDRIVER drv_openal = +{ NULL, + "OpenAL", + "MikMod OpenAL driver v1.0", + 0,255, + "OpenAL", + + NULL, + ALDRV_IsThere, + VC_SampleLoad, + VC_SampleUnload, + VC_SampleSpace, + VC_SampleLength, + ALDRV_Init, + ALDRV_Exit, + ALDRV_Reset, + VC_SetNumVoices, + VC_PlayStart, + VC_PlayStop, + ALDRV_Update, + NULL, /* FIXME: Pause */ + VC_VoiceSetVolume, + VC_VoiceGetVolume, + VC_VoiceSetFrequency, + VC_VoiceGetFrequency, + VC_VoiceSetPanning, + VC_VoiceGetPanning, + VC_VoicePlay, + VC_VoiceStop, + VC_VoiceStopped, + VC_VoiceGetPosition, + VC_VoiceRealVolume +}; diff --git a/sc2/src/sc2code/libs/sound/decoders/mikmod/load_mod.c b/sc2/src/sc2code/libs/sound/decoders/mikmod/load_mod.c new file mode 100644 index 000000000..92c674368 --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/mikmod/load_mod.c @@ -0,0 +1,452 @@ +/* MikMod sound library + (c) 1998, 1999, 2000 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Generic MOD loader (Protracker, StarTracker, FastTracker, etc) + +==============================================================================*/ + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include + +#include "mikmod_internals.h" + +/*========== Module structure */ + +typedef struct MSAMPINFO { + CHAR samplename[23]; /* 22 in module, 23 in memory */ + UWORD length; + UBYTE finetune; + UBYTE volume; + UWORD reppos; + UWORD replen; +} MSAMPINFO; + +typedef struct MODULEHEADER { + CHAR songname[21]; /* the songname.. 20 in module, 21 in memory */ + MSAMPINFO samples[31]; /* all sampleinfo */ + UBYTE songlength; /* number of patterns used */ + UBYTE magic1; /* should be 127 */ + UBYTE positions[128]; /* which pattern to play at pos */ + UBYTE magic2[4]; /* string "M.K." or "FLT4" or "FLT8" */ +} MODULEHEADER; + +typedef struct MODTYPE { + CHAR id[5]; + UBYTE channels; + CHAR *name; +} MODTYPE; + +typedef struct MODNOTE { + UBYTE a, b, c, d; +} MODNOTE; + +/*========== Loader variables */ + +#define MODULEHEADERSIZE 0x438 + +static CHAR protracker[] = "Protracker"; +static CHAR startrekker[] = "Startrekker"; +static CHAR fasttracker[] = "Fasttracker"; +static CHAR oktalyser[] = "Oktalyser"; +static CHAR oktalyzer[] = "Oktalyzer"; +static CHAR taketracker[] = "TakeTracker"; +static CHAR orpheus[] = "Imago Orpheus (MOD format)"; + +static MODULEHEADER *mh = NULL; +static MODNOTE *patbuf = NULL; +static int modtype = 0; + +/*========== Loader code */ + +/* given the module ID, determine the number of channels and the tracker + description ; also alters modtype */ +static BOOL MOD_CheckType(UBYTE *id, UBYTE *numchn, CHAR **descr) +{ + /* Protracker and variants */ + if ((!memcmp(id, "M.K.", 4)) || (!memcmp(id, "M!K!", 4))) { + *descr = protracker; + modtype = 0; + *numchn = 4; + return 1; + } + + /* Star Tracker */ + if (((!memcmp(id, "FLT", 3)) || (!memcmp(id, "EXO", 3))) && + (isdigit(id[3]))) { + *descr = startrekker; + modtype = 1; + *numchn = id[3] - '0'; + if (*numchn == 4) + return 1; +#ifdef MIKMOD_DEBUG + if (*numchn == 8) + fprintf(stderr, "\rFLT8 modules not supported yet\n"); + else + fprintf(stderr, "\rUnknown FLT%d module type\n", *numchn); +#endif + return 0; + } + + /* Oktalyzer (Amiga) */ + if (!memcmp(id, "OKTA", 4)) { + *descr = oktalyzer; + modtype = 1; + *numchn = 8; + return 1; + } + + /* Oktalyser (Atari) */ + if (!memcmp(id, "CD81", 4)) { + *descr = oktalyser; + modtype = 1; + *numchn = 8; + return 1; + } + + /* Fasttracker */ + if ((!memcmp(id + 1, "CHN", 3)) && (isdigit(id[0]))) { + *descr = fasttracker; + modtype = 1; + *numchn = id[0] - '0'; + return 1; + } + /* Fasttracker or Taketracker */ + if (((!memcmp(id + 2, "CH", 2)) || (!memcmp(id + 2, "CN", 2))) + && (isdigit(id[0])) && (isdigit(id[1]))) { + if (id[3] == 'H') { + *descr = fasttracker; + modtype = 2; /* this can also be Imago Orpheus */ + } else { + *descr = taketracker; + modtype = 1; + } + *numchn = (id[0] - '0') * 10 + (id[1] - '0'); + return 1; + } + + return 0; +} + +static BOOL MOD_Test(void) +{ + UBYTE id[4], numchn; + CHAR *descr; + + _mm_fseek(modreader, MODULEHEADERSIZE, SEEK_SET); + if (!_mm_read_UBYTES(id, 4, modreader)) + return 0; + + if (MOD_CheckType(id, &numchn, &descr)) + return 1; + + return 0; +} + +static BOOL MOD_Init(void) +{ + if (!(mh = (MODULEHEADER *)_mm_malloc(sizeof(MODULEHEADER)))) + return 0; + return 1; +} + +static void MOD_Cleanup(void) +{ + _mm_free(mh); + _mm_free(patbuf); +} + +/* +Old (amiga) noteinfo: + +_____byte 1_____ byte2_ _____byte 3_____ byte4_ +/ \ / \ / \ / \ +0000 0000-00000000 0000 0000-00000000 + +Upper four 12 bits for Lower four Effect command. +bits of sam- note period. bits of sam- +ple number. ple number. + +*/ + +static void ConvertNote(MODNOTE *n) +{ + UBYTE instrument, effect, effdat, note; + UWORD period; + UBYTE lastnote = 0; + + /* extract the various information from the 4 bytes that make up a note */ + instrument = (n->a & 0x10) | (n->c >> 4); + period = (((UWORD)n->a & 0xf) << 8) + n->b; + effect = n->c & 0xf; + effdat = n->d; + + /* Convert the period to a note number */ + note = 0; + if (period) { + for (note = 0; note < 7 * OCTAVE; note++) + if (period >= npertab[note]) + break; + if (note == 7 * OCTAVE) + note = 0; + else + note++; + } + + if (instrument) { + /* if instrument does not exist, note cut */ + if ((instrument > 31) || (!mh->samples[instrument - 1].length)) { + UniPTEffect(0xc, 0); + if (effect == 0xc) + effect = effdat = 0; + } else { + /* Protracker handling */ + if (!modtype) { + /* if we had a note, then change instrument... */ + if (note) + UniInstrument(instrument - 1); + /* ...otherwise, only adjust volume... */ + else { + /* ...unless an effect was specified, which forces a new + note to be played */ + if (effect || effdat) { + UniInstrument(instrument - 1); + note = lastnote; + } else + UniPTEffect(0xc, + mh->samples[instrument - + 1].volume & 0x7f); + } + } else { + /* Fasttracker handling */ + UniInstrument(instrument - 1); + if (!note) + note = lastnote; + } + } + } + if (note) { + UniNote(note + 2 * OCTAVE - 1); + lastnote = note; + } + + /* Convert pattern jump from Dec to Hex */ + if (effect == 0xd) + effdat = (((effdat & 0xf0) >> 4) * 10) + (effdat & 0xf); + + /* Volume slide, up has priority */ + if ((effect == 0xa) && (effdat & 0xf) && (effdat & 0xf0)) + effdat &= 0xf0; + + /* Handle ``heavy'' volumes correctly */ + if ((effect == 0xc) && (effdat > 0x40)) + effdat = 0x40; + + /* Ignore 100, 200 and 300 (there is no porta memory in mod files) */ + if ((!effdat) && ((effect == 1)||(effect == 2)||(effect ==3))) + effect = 0; + + UniPTEffect(effect, effdat); +} + +static UBYTE *ConvertTrack(MODNOTE *n) +{ + int t; + + UniReset(); + for (t = 0; t < 64; t++) { + ConvertNote(n); + UniNewline(); + n += of.numchn; + } + return UniDup(); +} + +/* Loads all patterns of a modfile and converts them into the 3 byte format. */ +static BOOL ML_LoadPatterns(void) +{ + int t, s, tracks = 0; + + if (!AllocPatterns()) + return 0; + if (!AllocTracks()) + return 0; + + /* Allocate temporary buffer for loading and converting the patterns */ + if (!(patbuf = (MODNOTE *)_mm_calloc(64U * of.numchn, sizeof(MODNOTE)))) + return 0; + + for (t = 0; t < of.numpat; t++) { + /* Load the pattern into the temp buffer and convert it */ + for (s = 0; s < (64U * of.numchn); s++) { + patbuf[s].a = _mm_read_UBYTE(modreader); + patbuf[s].b = _mm_read_UBYTE(modreader); + patbuf[s].c = _mm_read_UBYTE(modreader); + patbuf[s].d = _mm_read_UBYTE(modreader); + } + for (s = 0; s < of.numchn; s++) + if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s))) + return 0; + } + return 1; +} + +static BOOL MOD_Load(BOOL curious) +{ + int t, scan; + SAMPLE *q; + MSAMPINFO *s; + CHAR *descr; + + /* try to read module header */ + _mm_read_string((CHAR *)mh->songname, 20, modreader); + mh->songname[20] = 0; /* just in case */ + + for (t = 0; t < 31; t++) { + s = &mh->samples[t]; + _mm_read_string(s->samplename, 22, modreader); + s->samplename[22] = 0; /* just in case */ + s->length = _mm_read_M_UWORD(modreader); + s->finetune = _mm_read_UBYTE(modreader); + s->volume = _mm_read_UBYTE(modreader); + s->reppos = _mm_read_M_UWORD(modreader); + s->replen = _mm_read_M_UWORD(modreader); + } + + mh->songlength = _mm_read_UBYTE(modreader); + mh->magic1 = _mm_read_UBYTE(modreader); + _mm_read_UBYTES(mh->positions, 128, modreader); + _mm_read_UBYTES(mh->magic2, 4, modreader); + + if (_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + /* set module variables */ + of.initspeed = 6; + of.inittempo = 125; + if (!(MOD_CheckType(mh->magic2, &of.numchn, &descr))) { + _mm_errno = MMERR_NOT_A_MODULE; + return 0; + } + of.songname = DupStr(mh->songname, 21, 1); + of.numpos = mh->songlength; + of.reppos = 0; + + /* Count the number of patterns */ + of.numpat = 0; + for (t = 0; t < of.numpos; t++) + if (mh->positions[t] > of.numpat) + of.numpat = mh->positions[t]; + /* since some old modules embed extra patterns, we have to check the + whole list to get the samples' file offsets right - however we can find + garbage here, so check carefully */ + scan = 1; + for (t = of.numpos; t < 128; t++) + if (mh->positions[t] >= 0x80) + scan = 0; + if (scan) + for (t = of.numpos; t < 128; t++) { + if (mh->positions[t] > of.numpat) + of.numpat = mh->positions[t]; + if ((curious) && (mh->positions[t])) + of.numpos = t + 1; + } + of.numpat++; + of.numtrk = of.numpat * of.numchn; + + if (!AllocPositions(of.numpos)) + return 0; + for (t = 0; t < of.numpos; t++) + of.positions[t] = mh->positions[t]; + + /* Finally, init the sampleinfo structures */ + of.numins = of.numsmp = 31; + if (!AllocSamples()) + return 0; + s = mh->samples; + q = of.samples; + for (t = 0; t < of.numins; t++) { + /* convert the samplename */ + q->samplename = DupStr(s->samplename, 23, 1); + /* init the sampleinfo variables and convert the size pointers */ + q->speed = finetune[s->finetune & 0xf]; + q->volume = s->volume & 0x7f; + q->loopstart = (ULONG)s->reppos << 1; + q->loopend = q->loopstart + ((ULONG)s->replen << 1); + q->length = (ULONG)s->length << 1; + q->flags = SF_SIGNED; + /* Imago Orpheus creates MODs with 16 bit samples, check */ + if ((modtype == 2) && (s->volume & 0x80)) { + q->flags |= SF_16BITS; + descr = orpheus; + } + + if (s->replen > 2) + q->flags |= SF_LOOP; + /* fix replen if repend > length */ + if (q->loopend > q->length) + q->loopend = q->length; + + s++; + q++; + } + + of.modtype = strdup(descr); + + if (!ML_LoadPatterns()) + return 0; + return 1; +} + +static CHAR *MOD_LoadTitle(void) +{ + CHAR s[21]; + + _mm_fseek(modreader, 0, SEEK_SET); + if (!_mm_read_UBYTES(s, 20, modreader)) + return NULL; + s[20] = 0; /* just in case */ + + return (DupStr(s, 21, 1)); +} + +/*========== Loader information */ + +MIKMODAPI MLOADER load_mod = { + NULL, + "Standard module", + "MOD (31 instruments)", + MOD_Init, + MOD_Test, + MOD_Load, + MOD_Cleanup, + MOD_LoadTitle +}; + +/* ex:set ts=4: */ diff --git a/sc2/src/sc2code/libs/sound/decoders/mikmod/mdreg.c b/sc2/src/sc2code/libs/sound/decoders/mikmod/mdreg.c new file mode 100644 index 000000000..0ada9e005 --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/mikmod/mdreg.c @@ -0,0 +1,53 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Routine for registering all drivers in libmikmod for the current platform. + +==============================================================================*/ + +#include "mikmod_internals.h" + +MIKMODAPI extern struct MDRIVER drv_openal; + +void _mm_registeralldrivers(void) +{ + _mm_registerdriver(&drv_openal); + _mm_registerdriver(&drv_nos); +} + +void MikMod_RegisterAllDrivers(void) +{ + MUTEX_LOCK(lists); + _mm_registeralldrivers(); + MUTEX_UNLOCK(lists); +} + +void MikMod_UnregisterAllDrivers(void) +{ + MUTEX_LOCK(lists); + _mm_unregisterdrivers(); + MUTEX_UNLOCK(lists); +} + +/* ex:set ts=4: */ diff --git a/sc2/src/sc2code/libs/sound/decoders/mikmod/mdriver.c b/sc2/src/sc2code/libs/sound/decoders/mikmod/mdriver.c new file mode 100644 index 000000000..cc277380f --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/mikmod/mdriver.c @@ -0,0 +1,926 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + These routines are used to access the available soundcard drivers. + +==============================================================================*/ + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef unix +#include +#include +#endif + +#include +#ifdef HAVE_STRINGS_H +#include +#endif + +#include "mikmod_internals.h" + + +static MDRIVER *firstdriver=NULL; + MDRIVER *md_driver=NULL; +extern MODULE *pf; /* modfile being played */ + + UWORD md_device = 0; + UWORD md_mixfreq = 44100; + UWORD md_mode = DMODE_STEREO | DMODE_16BITS | DMODE_SURROUND + |DMODE_SOFT_MUSIC | DMODE_SOFT_SNDFX; + UBYTE md_pansep = 128; /* 128 == 100% (full left/right) */ + UBYTE md_reverb = 6; /* Reverb */ + UBYTE md_volume = 128; /* Global sound volume (0-128) */ + UBYTE md_musicvolume = 128; /* volume of song */ + UBYTE md_sndfxvolume = 128; /* volume of sound effects */ + UWORD md_bpm = 125; + +/* Do not modify the numchn variables yourself! use MD_SetVoices() */ + UBYTE md_numchn=0,md_sngchn=0,md_sfxchn=0; + UBYTE md_hardchn=0,md_softchn=0; + + void (*md_player)(void) = Player_HandleTick; +static BOOL isplaying=0, initialized = 0; +static UBYTE *sfxinfo; +static int sfxpool; + +static SAMPLE **md_sample = NULL; + +/* Previous driver in use */ +static UWORD idevice; + +/* Limits the number of hardware voices to the specified amount. + This function should only be used by the low-level drivers. */ +static void LimitHardVoices(int limit) +{ + int t=0; + + if (!(md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>limit)) md_sfxchn=limit; + if (!(md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>limit)) md_sngchn=limit; + + if (!(md_mode & DMODE_SOFT_SNDFX)) + md_hardchn=md_sfxchn; + else + md_hardchn=0; + + if (!(md_mode & DMODE_SOFT_MUSIC)) md_hardchn += md_sngchn; + + while (md_hardchn>limit) { + if (++t & 1) { + if (!(md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>4)) md_sfxchn--; + } else { + if (!(md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>8)) md_sngchn--; + } + + if (!(md_mode & DMODE_SOFT_SNDFX)) + md_hardchn=md_sfxchn; + else + md_hardchn=0; + + if (!(md_mode & DMODE_SOFT_MUSIC)) + md_hardchn+=md_sngchn; + } + md_numchn=md_hardchn+md_softchn; +} + +/* Limits the number of hardware voices to the specified amount. + This function should only be used by the low-level drivers. */ +static void LimitSoftVoices(int limit) +{ + int t=0; + + if ((md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>limit)) md_sfxchn=limit; + if ((md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>limit)) md_sngchn=limit; + + if (md_mode & DMODE_SOFT_SNDFX) + md_softchn=md_sfxchn; + else + md_softchn=0; + + if (md_mode & DMODE_SOFT_MUSIC) md_softchn+=md_sngchn; + + while (md_softchn>limit) { + if (++t & 1) { + if ((md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>4)) md_sfxchn--; + } else { + if ((md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>8)) md_sngchn--; + } + + if (!(md_mode & DMODE_SOFT_SNDFX)) + md_softchn=md_sfxchn; + else + md_softchn=0; + + if (!(md_mode & DMODE_SOFT_MUSIC)) + md_softchn+=md_sngchn; + } + md_numchn=md_hardchn+md_softchn; +} + +/* Note: 'type' indicates whether the returned value should be for music or for + sound effects. */ +ULONG MD_SampleSpace(int type) +{ + if(type==MD_MUSIC) + type=(md_mode & DMODE_SOFT_MUSIC)?MD_SOFTWARE:MD_HARDWARE; + else if(type==MD_SNDFX) + type=(md_mode & DMODE_SOFT_SNDFX)?MD_SOFTWARE:MD_HARDWARE; + + return md_driver->FreeSampleSpace(type); +} + +ULONG MD_SampleLength(int type,SAMPLE* s) +{ + if(type==MD_MUSIC) + type=(md_mode & DMODE_SOFT_MUSIC)?MD_SOFTWARE:MD_HARDWARE; + else + if(type==MD_SNDFX) + type=(md_mode & DMODE_SOFT_SNDFX)?MD_SOFTWARE:MD_HARDWARE; + + return md_driver->RealSampleLength(type,s); +} + +CHAR* MikMod_InfoDriver(void) +{ + int t,len=0; + MDRIVER *l; + CHAR *list=NULL; + + MUTEX_LOCK(lists); + /* compute size of buffer */ + for(l=firstdriver;l;l=l->next) + if(l->Version) + len+=4+(l->next?1:0)+strlen(l->Version); + + if(len) + if((list=_mm_malloc(len*sizeof(CHAR)))) { + list[0]=0; + /* list all registered device drivers : */ + for(t=1,l=firstdriver;l;l=l->next,t++) + if(l->Version) + sprintf(list,(l->next)?"%s%2d %s\n":"%s%2d %s", + list,t,l->Version); + } + MUTEX_UNLOCK(lists); + return list; +} + +void _mm_registerdriver(struct MDRIVER* drv) +{ + MDRIVER *cruise = firstdriver; + + if(cruise) { + while(cruise->next) cruise=cruise->next; + cruise->next=drv; + } else + firstdriver = drv; +} + +void _mm_unregisterdrivers(void) +{ + MDRIVER *cruise=firstdriver, *drv; + + while ( cruise ) { + drv = cruise; + cruise = cruise->next; + drv->next = NULL; + } + firstdriver = NULL; +} + +void MikMod_RegisterDriver(struct MDRIVER* drv) +{ + /* if we try to register an invalid driver, or an already registered driver, + ignore this attempt */ + if ((!drv)||(drv->next)) + return; + + MUTEX_LOCK(lists); + _mm_registerdriver(drv); + MUTEX_UNLOCK(lists); +} + +int MikMod_DriverFromAlias(CHAR *alias) +{ + int rank=1; + MDRIVER *cruise; + + MUTEX_LOCK(lists); + cruise=firstdriver; + while(cruise) { + if (cruise->Alias) { + if (!(strcasecmp(alias,cruise->Alias))) break; + rank++; + } + cruise=cruise->next; + } + if(!cruise) rank=0; + MUTEX_UNLOCK(lists); + + return rank; +} + +SWORD MD_SampleLoad(SAMPLOAD* s, int type) +{ + SWORD result; + + if(type==MD_MUSIC) + type=(md_mode & DMODE_SOFT_MUSIC)?MD_SOFTWARE:MD_HARDWARE; + else if(type==MD_SNDFX) + type=(md_mode & DMODE_SOFT_SNDFX)?MD_SOFTWARE:MD_HARDWARE; + + SL_Init(s); + result=md_driver->SampleLoad(s,type); + SL_Exit(s); + + return result; +} + +void MD_SampleUnload(SWORD handle) +{ + md_driver->SampleUnload(handle); +} + +MikMod_player_t MikMod_RegisterPlayer(MikMod_player_t player) +{ + MikMod_player_t result; + + MUTEX_LOCK(vars); + result=md_player; + md_player=player; + MUTEX_UNLOCK(vars); + + return result; +} + +void MikMod_Update(void) +{ + MUTEX_LOCK(vars); + if(isplaying) { + if((!pf)||(!pf->forbid)) + md_driver->Update(); + else { + if (md_driver->Pause) + md_driver->Pause(); + } + } + MUTEX_UNLOCK(vars); +} + +void Voice_SetVolume_internal(SBYTE voice,UWORD vol) +{ + ULONG tmp; + + if((voice<0)||(voice>=md_numchn)) return; + + /* range checks */ + if(md_musicvolume>128) md_musicvolume=128; + if(md_sndfxvolume>128) md_sndfxvolume=128; + if(md_volume>128) md_volume=128; + + tmp=(ULONG)vol*(ULONG)md_volume* + ((voiceVoiceSetVolume(voice,tmp/16384UL); +} + +void Voice_SetVolume(SBYTE voice,UWORD vol) +{ + MUTEX_LOCK(vars); + Voice_SetVolume_internal(voice,vol); + MUTEX_UNLOCK(vars); +} + +UWORD Voice_GetVolume(SBYTE voice) +{ + UWORD result=0; + + MUTEX_LOCK(vars); + if((voice>=0)&&(voiceVoiceGetVolume(voice); + MUTEX_UNLOCK(vars); + + return result; +} + +void Voice_SetFrequency_internal(SBYTE voice,ULONG frq) +{ + if((voice<0)||(voice>=md_numchn)) return; + if((md_sample[voice])&&(md_sample[voice]->divfactor)) + frq/=md_sample[voice]->divfactor; + md_driver->VoiceSetFrequency(voice,frq); +} + +void Voice_SetFrequency(SBYTE voice,ULONG frq) +{ + MUTEX_LOCK(vars); + Voice_SetFrequency_internal(voice,frq); + MUTEX_UNLOCK(vars); +} + +ULONG Voice_GetFrequency(SBYTE voice) +{ + ULONG result=0; + + MUTEX_LOCK(vars); + if((voice>=0)&&(voiceVoiceGetFrequency(voice); + MUTEX_UNLOCK(vars); + + return result; +} + +void Voice_SetPanning_internal(SBYTE voice,ULONG pan) +{ + if((voice<0)||(voice>=md_numchn)) return; + if(pan!=PAN_SURROUND) { + if(md_pansep>128) md_pansep=128; + if(md_mode & DMODE_REVERSE) pan=255-pan; + pan = (((SWORD)(pan-128)*md_pansep)/128)+128; + } + md_driver->VoiceSetPanning(voice, pan); +} + +void Voice_SetPanning(SBYTE voice,ULONG pan) +{ +#ifdef MIKMOD_DEBUG + if((pan!=PAN_SURROUND)&&((pan<0)||(pan>255))) + fprintf(stderr,"\rVoice_SetPanning called with pan=%ld\n",(long)pan); +#endif + + MUTEX_LOCK(vars); + Voice_SetPanning_internal(voice,pan); + MUTEX_UNLOCK(vars); +} + +ULONG Voice_GetPanning(SBYTE voice) +{ + ULONG result=PAN_CENTER; + + MUTEX_LOCK(vars); + if((voice>=0)&&(voiceVoiceGetPanning(voice); + MUTEX_UNLOCK(vars); + + return result; +} + +void Voice_Play_internal(SBYTE voice,SAMPLE* s,ULONG start) +{ + ULONG repend; + + if((voice<0)||(voice>=md_numchn)) return; + + md_sample[voice]=s; + repend=s->loopend; + + if(s->flags&SF_LOOP) + /* repend can't be bigger than size */ + if(repend>s->length) repend=s->length; + + md_driver->VoicePlay(voice,s->handle,start,s->length,s->loopstart,repend,s->flags); +} + +void Voice_Play(SBYTE voice,SAMPLE* s,ULONG start) +{ + if(start>s->length) return; + + MUTEX_LOCK(vars); + Voice_Play_internal(voice,s,start); + MUTEX_UNLOCK(vars); +} + +void Voice_Stop_internal(SBYTE voice) +{ + if((voice<0)||(voice>=md_numchn)) return; + if(voice>=md_sngchn) + /* It is a sound effects channel, so flag the voice as non-critical! */ + sfxinfo[voice-md_sngchn]=0; + md_driver->VoiceStop(voice); +} + +void Voice_Stop(SBYTE voice) +{ + MUTEX_LOCK(vars); + Voice_Stop_internal(voice); + MUTEX_UNLOCK(vars); +} + +BOOL Voice_Stopped_internal(SBYTE voice) +{ + if((voice<0)||(voice>=md_numchn)) return 0; + return(md_driver->VoiceStopped(voice)); +} + +BOOL Voice_Stopped(SBYTE voice) +{ + BOOL result; + + MUTEX_LOCK(vars); + result=Voice_Stopped_internal(voice); + MUTEX_UNLOCK(vars); + + return result; +} + +SLONG Voice_GetPosition(SBYTE voice) +{ + SLONG result=0; + + MUTEX_LOCK(vars); + if((voice>=0)&&(voiceVoiceGetPosition(voice)); + MUTEX_UNLOCK(vars); + + return result; +} + +ULONG Voice_RealVolume(SBYTE voice) +{ + ULONG result=0; + + MUTEX_LOCK(vars); + if((voice>=0)&&(voiceVoiceRealVolume(voice)); + MUTEX_UNLOCK(vars); + + return result; +} + +static BOOL _mm_init(CHAR *cmdline) +{ + UWORD t; + + _mm_critical = 1; + + /* if md_device==0, try to find a device number */ + if(!md_device) { + cmdline=NULL; + + for(t=1,md_driver=firstdriver;md_driver;md_driver=md_driver->next,t++) + if(md_driver->Version) + if(md_driver->IsPresent()) break; + + if(!md_driver) { + _mm_errno = MMERR_DETECTING_DEVICE; + if(_mm_errorhandler) _mm_errorhandler(); + md_driver = &drv_nos; + return 1; + } + + md_device = t; + } else { + /* if n>0, use that driver */ + for(t=1,md_driver=firstdriver;(md_driver)&&(t!=md_device);md_driver=md_driver->next) + if(md_driver->Version) + t++; + + if(!md_driver) { + _mm_errno = MMERR_INVALID_DEVICE; + if(_mm_errorhandler) _mm_errorhandler(); + md_driver = &drv_nos; + return 1; + } + + /* arguments here might be necessary for the presence check to succeed */ + if(cmdline&&(md_driver->CommandLine)) + md_driver->CommandLine(cmdline); + + if(!md_driver->IsPresent()) { + _mm_errno = MMERR_DETECTING_DEVICE; + if(_mm_errorhandler) _mm_errorhandler(); + md_driver = &drv_nos; + return 1; + } + } + + if(md_driver->Init()) { + md_driver->Exit(); + MikMod_Exit_internal(); + if(_mm_errorhandler) _mm_errorhandler(); + return 1; + } + + initialized=1; + _mm_critical=0; + + return 0; +} + +BOOL MikMod_Init(CHAR *cmdline) +{ + BOOL result; + + MUTEX_LOCK(vars); + MUTEX_LOCK(lists); + result=_mm_init(cmdline); + MUTEX_UNLOCK(lists); + MUTEX_UNLOCK(vars); + + return result; +} + +void MikMod_Exit_internal(void) +{ + MikMod_DisableOutput_internal(); + md_driver->Exit(); + md_numchn = md_sfxchn = md_sngchn = 0; + md_driver = &drv_nos; + + if(sfxinfo) free(sfxinfo); + if(md_sample) free(md_sample); + md_sample = NULL; + sfxinfo = NULL; + + initialized = 0; +} + +void MikMod_Exit(void) +{ + MUTEX_LOCK(vars); + MUTEX_LOCK(lists); + MikMod_Exit_internal(); + MUTEX_UNLOCK(lists); + MUTEX_UNLOCK(vars); +} + +/* Reset the driver using the new global variable settings. + If the driver has not been initialized, it will be now. */ +static BOOL _mm_reset(CHAR *cmdline) +{ + if(!initialized) return _mm_init(cmdline); + + if((!md_driver->Reset)||(md_device != idevice)) { + /* md_driver->Reset was NULL, or md_device was changed, so do a full + reset of the driver. */ + if(isplaying) md_driver->PlayStop(); + md_driver->Exit(); + if(_mm_init(cmdline)) { + MikMod_Exit_internal(); + if(_mm_errno) + if(_mm_errorhandler) _mm_errorhandler(); + return 1; + } + if(isplaying) md_driver->PlayStart(); + } else { + if(md_driver->Reset()) { + MikMod_Exit_internal(); + if(_mm_errno) + if(_mm_errorhandler) _mm_errorhandler(); + return 1; + } + } + return 0; +} + +BOOL MikMod_Reset(CHAR *cmdline) +{ + BOOL result; + + MUTEX_LOCK(vars); + MUTEX_LOCK(lists); + result=_mm_reset(cmdline); + MUTEX_UNLOCK(lists); + MUTEX_UNLOCK(vars); + + return result; +} + +/* If either parameter is -1, the current set value will be retained. */ +BOOL MikMod_SetNumVoices_internal(int music, int sfx) +{ + BOOL resume = 0; + int t, oldchn = 0; + + if((!music)&&(!sfx)) return 1; + _mm_critical = 1; + if(isplaying) { + MikMod_DisableOutput_internal(); + oldchn = md_numchn; + resume = 1; + } + + if(sfxinfo) free(sfxinfo); + if(md_sample) free(md_sample); + md_sample = NULL; + sfxinfo = NULL; + + if(music!=-1) md_sngchn = music; + if(sfx!=-1) md_sfxchn = sfx; + md_numchn = md_sngchn + md_sfxchn; + + LimitHardVoices(md_driver->HardVoiceLimit); + LimitSoftVoices(md_driver->SoftVoiceLimit); + + if(md_driver->SetNumVoices()) { + MikMod_Exit_internal(); + if(_mm_errno) + if(_mm_errorhandler!=NULL) _mm_errorhandler(); + md_numchn = md_softchn = md_hardchn = md_sfxchn = md_sngchn = 0; + return 1; + } + + if(md_sngchn+md_sfxchn) + md_sample=(SAMPLE**)_mm_calloc(md_sngchn+md_sfxchn,sizeof(SAMPLE*)); + if(md_sfxchn) + sfxinfo = (UBYTE *)_mm_calloc(md_sfxchn,sizeof(UBYTE)); + + /* make sure the player doesn't start with garbage */ + for(t=oldchn;tPlayStart()) return 1; + isplaying = 1; + } + _mm_critical = 0; + return 0; +} + +BOOL MikMod_EnableOutput(void) +{ + BOOL result; + + MUTEX_LOCK(vars); + result=MikMod_EnableOutput_internal(); + MUTEX_UNLOCK(vars); + + return result; +} + +void MikMod_DisableOutput_internal(void) +{ + if(isplaying && md_driver) { + isplaying = 0; + md_driver->PlayStop(); + } +} + +void MikMod_DisableOutput(void) +{ + MUTEX_LOCK(vars); + MikMod_DisableOutput_internal(); + MUTEX_UNLOCK(vars); +} + +BOOL MikMod_Active_internal(void) +{ + return isplaying; +} + +BOOL MikMod_Active(void) +{ + BOOL result; + + MUTEX_LOCK(vars); + result=MikMod_Active_internal(); + MUTEX_UNLOCK(vars); + + return result; +} + +/* Plays a sound effects sample. Picks a voice from the number of voices + allocated for use as sound effects (loops through voices, skipping all active + criticals). + + Returns the voice that the sound is being played on. */ +SBYTE Sample_Play_internal(SAMPLE *s,ULONG start,UBYTE flags) +{ + int orig=sfxpool;/* for cases where all channels are critical */ + int c; + + if(!md_sfxchn) return -1; + if(s->volume>64) s->volume = 64; + + /* check the first location after sfxpool */ + do { + if(sfxinfo[sfxpool]&SFX_CRITICAL) { + if(md_driver->VoiceStopped(c=sfxpool+md_sngchn)) { + sfxinfo[sfxpool]=flags; + Voice_Play_internal(c,s,start); + md_driver->VoiceSetVolume(c,s->volume<<2); + Voice_SetPanning_internal(c,s->panning); + md_driver->VoiceSetFrequency(c,s->speed); + sfxpool++; + if(sfxpool>=md_sfxchn) sfxpool=0; + return c; + } + } else { + sfxinfo[sfxpool]=flags; + Voice_Play_internal(c=sfxpool+md_sngchn,s,start); + md_driver->VoiceSetVolume(c,s->volume<<2); + Voice_SetPanning_internal(c,s->panning); + md_driver->VoiceSetFrequency(c,s->speed); + sfxpool++; + if(sfxpool>=md_sfxchn) sfxpool=0; + return c; + } + + sfxpool++; + if(sfxpool>=md_sfxchn) sfxpool = 0; + } while(sfxpool!=orig); + + return -1; +} + +SBYTE Sample_Play(SAMPLE *s,ULONG start,UBYTE flags) +{ + SBYTE result; + + MUTEX_LOCK(vars); + result=Sample_Play_internal(s,start,flags); + MUTEX_UNLOCK(vars); + + return result; +} + +long MikMod_GetVersion(void) +{ + return LIBMIKMOD_VERSION; +} + +/*========== MT-safe stuff */ + +#ifdef HAVE_PTHREAD +#define INIT_MUTEX(name) \ + pthread_mutex_t _mm_mutex_##name=PTHREAD_MUTEX_INITIALIZER +#elif defined(__OS2__)||defined(__EMX__) +#define INIT_MUTEX(name) \ + HMTX _mm_mutex_##name +#elif defined(WIN32) +#define INIT_MUTEX(name) \ + HANDLE _mm_mutex_##name +#else +#define INIT_MUTEX(name) +#endif + +INIT_MUTEX(vars); +INIT_MUTEX(lists); + +BOOL MikMod_InitThreads(void) +{ + static int firstcall=1; + static int result=0; + + if (firstcall) { + firstcall=0; +#ifdef HAVE_PTHREAD + result=1; +#elif defined(__OS2__)||defined(__EMX__) + if(DosCreateMutexSem((PSZ)NULL,&_mm_mutex_lists,0,0) || + DosCreateMutexSem((PSZ)NULL,&_mm_mutex_vars,0,0)) { + _mm_mutex_lists=_mm_mutex_vars=(HMTX)NULL; + result=0; + } else + result=1; +#elif defined(WIN32) + if((!(_mm_mutex_lists=CreateMutex(NULL,FALSE,"libmikmod(lists)")))|| + (!(_mm_mutex_vars=CreateMutex(NULL,FALSE,"libmikmod(vars)")))) + result=0; + else + result=1; +#endif + } + return result; +} + +void MikMod_Unlock(void) +{ + MUTEX_UNLOCK(lists); + MUTEX_UNLOCK(vars); +} + +void MikMod_Lock(void) +{ + MUTEX_LOCK(vars); + MUTEX_LOCK(lists); +} + +/*========== Parameter extraction helper */ + +CHAR *MD_GetAtom(CHAR *atomname,CHAR *cmdline,BOOL implicit) +{ + CHAR *ret=NULL; + + if(cmdline) { + CHAR *buf=strstr(cmdline,atomname); + + if((buf)&&((buf==cmdline)||(*(buf-1)==','))) { + CHAR *ptr=buf+strlen(atomname); + + if(*ptr=='=') { + for(buf=++ptr;(*ptr)&&((*ptr)!=',');ptr++); + ret=_mm_malloc((1+ptr-buf)*sizeof(CHAR)); + if(ret) + strncpy(ret,buf,ptr-buf); + } else if((*ptr==',')||(!*ptr)) { + if(implicit) { + ret=_mm_malloc((1+ptr-buf)*sizeof(CHAR)); + if(ret) + strncpy(ret,buf,ptr-buf); + } + } + } + } + return ret; +} + +#ifdef unix + +#include + +/*========== Posix helper functions */ + +/* Check if the file is a regular or nonexistant file (or a link to a such a + file), and that, should the calling program be setuid, the access rights are + reasonable. Returns 1 if it is safe to rewrite the file, 0 otherwise. + The goal is to prevent a setuid root libmikmod application from overriding + files like /etc/passwd with digital sound... */ +BOOL MD_Access(CHAR *filename) +{ + struct stat buf; + + if(!stat(filename,&buf)) { + /* not a regular file ? */ + if(!S_ISREG(buf.st_mode)) return 0; + /* more than one hard link to the file ? */ + if(buf.st_nlink>1) return 0; + /* check access rights with the real user and group id */ + if(getuid()==buf.st_uid) { + if(!(buf.st_mode&S_IWUSR)) return 0; + } else if(getgid()==buf.st_gid) { + if(!(buf.st_mode&S_IWGRP)) return 0; + } else + if(!(buf.st_mode&S_IWOTH)) return 0; + } + + return 1; +} + +/* Drop all root privileges we might have */ +BOOL MD_DropPrivileges(void) +{ + if(!geteuid()) { + if(getuid()) { + /* we are setuid root -> drop setuid to become the real user */ + if(setuid(getuid())) return 1; + } else { + /* we are run as root -> drop all and become user 'nobody' */ + struct passwd *nobody; + int uid; + + if(!(nobody=getpwnam("nobody"))) return 1; /* no such user ? */ + uid=nobody->pw_uid; + if (!uid) /* user 'nobody' has root privileges ? weird... */ + return 1; + if (setuid(uid)) return 1; + } + } + return 0; +} + +#endif + +/* ex:set ts=4: */ diff --git a/sc2/src/sc2code/libs/sound/decoders/mikmod/mikmod.h b/sc2/src/sc2code/libs/sound/decoders/mikmod/mikmod.h new file mode 100644 index 000000000..a9d4e6b0e --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/mikmod/mikmod.h @@ -0,0 +1,710 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + MikMod sound library include file + +==============================================================================*/ + +#ifndef _MIKMOD_H_ +#define _MIKMOD_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ========== Compiler magic for shared libraries + */ +#if defined(WIN32) && 0 /* We're not building a shared library */ +#ifdef DLL_EXPORTS +#define MIKMODAPI __declspec(dllexport) +#else +#define MIKMODAPI __declspec(dllimport) +#endif +#else +#define MIKMODAPI +#endif + +/* + * ========== Library version + */ + +#define LIBMIKMOD_VERSION_MAJOR 3 +#define LIBMIKMOD_VERSION_MINOR 1 +#define LIBMIKMOD_REVISION 8 + +#define LIBMIKMOD_VERSION \ + ((LIBMIKMOD_VERSION_MAJOR<<16)| \ + (LIBMIKMOD_VERSION_MINOR<< 8)| \ + (LIBMIKMOD_REVISION)) + +MIKMODAPI extern long MikMod_GetVersion(void); + +/* + * ========== Platform independent-type definitions + */ + +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#endif + +#if defined(__OS2__)||defined(__EMX__) +#define INCL_DOSSEMAPHORES +#include +#else +#ifndef WIN32 +typedef char CHAR; +#endif +#endif + +/*@DOES_NOT_HAVE_SIGNED@*/ + +#if defined(__alpha) +/* 64 bit architectures */ + +typedef signed char SBYTE; /* 1 byte, signed */ +typedef unsigned char UBYTE; /* 1 byte, unsigned */ +typedef signed short SWORD; /* 2 bytes, signed */ +typedef unsigned short UWORD; /* 2 bytes, unsigned */ +typedef signed int SLONG; /* 4 bytes, signed */ +typedef unsigned int ULONG; /* 4 bytes, unsigned */ +typedef int BOOL; /* 0=false, <>0 true */ + +#else +/* 32 bit architectures */ + +typedef signed char SBYTE; /* 1 byte, signed */ +typedef unsigned char UBYTE; /* 1 byte, unsigned */ +typedef signed short SWORD; /* 2 bytes, signed */ +typedef unsigned short UWORD; /* 2 bytes, unsigned */ +typedef signed long SLONG; /* 4 bytes, signed */ +#if !defined(__OS2__)&&!defined(__EMX__)&&!defined(WIN32) +typedef unsigned long ULONG; /* 4 bytes, unsigned */ +typedef int BOOL; /* 0=false, <>0 true */ +#endif +#endif + +/* + * ========== Error codes + */ + +enum { + MMERR_OPENING_FILE = 1, + MMERR_OUT_OF_MEMORY, + MMERR_DYNAMIC_LINKING, + + MMERR_SAMPLE_TOO_BIG, + MMERR_OUT_OF_HANDLES, + MMERR_UNKNOWN_WAVE_TYPE, + + MMERR_LOADING_PATTERN, + MMERR_LOADING_TRACK, + MMERR_LOADING_HEADER, + MMERR_LOADING_SAMPLEINFO, + MMERR_NOT_A_MODULE, + MMERR_NOT_A_STREAM, + MMERR_MED_SYNTHSAMPLES, + MMERR_ITPACK_INVALID_DATA, + + MMERR_DETECTING_DEVICE, + MMERR_INVALID_DEVICE, + MMERR_INITIALIZING_MIXER, + MMERR_OPENING_AUDIO, + MMERR_8BIT_ONLY, + MMERR_16BIT_ONLY, + MMERR_STEREO_ONLY, + MMERR_ULAW, + MMERR_NON_BLOCK, + + MMERR_AF_AUDIO_PORT, + + MMERR_AIX_CONFIG_INIT, + MMERR_AIX_CONFIG_CONTROL, + MMERR_AIX_CONFIG_START, + + MMERR_GUS_SETTINGS, + MMERR_GUS_RESET, + MMERR_GUS_TIMER, + + MMERR_HP_SETSAMPLESIZE, + MMERR_HP_SETSPEED, + MMERR_HP_CHANNELS, + MMERR_HP_AUDIO_OUTPUT, + MMERR_HP_AUDIO_DESC, + MMERR_HP_BUFFERSIZE, + + MMERR_OSS_SETFRAGMENT, + MMERR_OSS_SETSAMPLESIZE, + MMERR_OSS_SETSTEREO, + MMERR_OSS_SETSPEED, + + MMERR_SGI_SPEED, + MMERR_SGI_16BIT, + MMERR_SGI_8BIT, + MMERR_SGI_STEREO, + MMERR_SGI_MONO, + + MMERR_SUN_INIT, + + MMERR_OS2_MIXSETUP, + MMERR_OS2_SEMAPHORE, + MMERR_OS2_TIMER, + MMERR_OS2_THREAD, + + MMERR_DS_PRIORITY, + MMERR_DS_BUFFER, + MMERR_DS_FORMAT, + MMERR_DS_NOTIFY, + MMERR_DS_EVENT, + MMERR_DS_THREAD, + MMERR_DS_UPDATE, + + MMERR_WINMM_HANDLE, + MMERR_WINMM_ALLOCATED, + MMERR_WINMM_DEVICEID, + MMERR_WINMM_FORMAT, + MMERR_WINMM_UNKNOWN, + + MMERR_MAC_SPEED, + MMERR_MAC_START, + + MMERR_MAX +}; + +/* + * ========== Error handling + */ + +typedef void (MikMod_handler)(void); +typedef MikMod_handler *MikMod_handler_t; + +MIKMODAPI extern int MikMod_errno; +MIKMODAPI extern BOOL MikMod_critical; +MIKMODAPI extern char *MikMod_strerror(int); + +MIKMODAPI extern MikMod_handler_t MikMod_RegisterErrorHandler(MikMod_handler_t); + +/* + * ========== Library initialization and core functions + */ + +struct MDRIVER; + +MIKMODAPI extern void MikMod_RegisterAllDrivers(void); +MIKMODAPI extern void MikMod_UnregisterAllDrivers(void); + +MIKMODAPI extern CHAR* MikMod_InfoDriver(void); +MIKMODAPI extern void MikMod_RegisterDriver(struct MDRIVER*); +MIKMODAPI extern int MikMod_DriverFromAlias(CHAR*); + +MIKMODAPI extern BOOL MikMod_Init(CHAR*); +MIKMODAPI extern void MikMod_Exit(void); +MIKMODAPI extern BOOL MikMod_Reset(CHAR*); +MIKMODAPI extern BOOL MikMod_SetNumVoices(int,int); +MIKMODAPI extern BOOL MikMod_Active(void); +MIKMODAPI extern BOOL MikMod_EnableOutput(void); +MIKMODAPI extern void MikMod_DisableOutput(void); +MIKMODAPI extern void MikMod_Update(void); + +MIKMODAPI extern BOOL MikMod_InitThreads(void); +MIKMODAPI extern void MikMod_Lock(void); +MIKMODAPI extern void MikMod_Unlock(void); + +/* + * ========== Reader, Writer + */ + +typedef struct MREADER { + BOOL (*Seek)(struct MREADER*,long,int); + long (*Tell)(struct MREADER*); + BOOL (*Read)(struct MREADER*,void*,size_t); + int (*Get)(struct MREADER*); + BOOL (*Eof)(struct MREADER*); +} MREADER; + +typedef struct MWRITER { + BOOL (*Seek)(struct MWRITER*,long,int); + long (*Tell)(struct MWRITER*); + BOOL (*Write)(struct MWRITER*,void*,size_t); + BOOL (*Put)(struct MWRITER*,int); +} MWRITER; + +/* + * ========== Samples + */ + +/* Sample playback should not be interrupted */ +#define SFX_CRITICAL 1 + +/* Sample format [loading and in-memory] flags: */ +#define SF_16BITS 0x0001 +#define SF_STEREO 0x0002 +#define SF_SIGNED 0x0004 +#define SF_BIG_ENDIAN 0x0008 +#define SF_DELTA 0x0010 +#define SF_ITPACKED 0x0020 + +#define SF_FORMATMASK 0x003F + +/* General Playback flags */ + +#define SF_LOOP 0x0100 +#define SF_BIDI 0x0200 +#define SF_REVERSE 0x0400 +#define SF_SUSTAIN 0x0800 + +#define SF_PLAYBACKMASK 0x0C00 + +/* Module-only Playback Flags */ + +#define SF_OWNPAN 0x1000 +#define SF_UST_LOOP 0x2000 + +#define SF_EXTRAPLAYBACKMASK 0x3000 + +/* Panning constants */ +#define PAN_LEFT 0 +#define PAN_CENTER 128 +#define PAN_RIGHT 255 +#define PAN_SURROUND 512 /* panning value for Dolby Surround */ + +typedef struct SAMPLE { + SWORD panning; /* panning (0-255 or PAN_SURROUND) */ + ULONG speed; /* Base playing speed/frequency of note */ + UBYTE volume; /* volume 0-64 */ + UWORD inflags; /* sample format on disk */ + UWORD flags; /* sample format in memory */ + ULONG length; /* length of sample (in samples!) */ + ULONG loopstart; /* repeat position (relative to start, in samples) */ + ULONG loopend; /* repeat end */ + ULONG susbegin; /* sustain loop begin (in samples) \ Not Supported */ + ULONG susend; /* sustain loop end / Yet! */ + + /* Variables used by the module player only! (ignored for sound effects) */ + UBYTE globvol; /* global volume */ + UBYTE vibflags; /* autovibrato flag stuffs */ + UBYTE vibtype; /* Vibratos moved from INSTRUMENT to SAMPLE */ + UBYTE vibsweep; + UBYTE vibdepth; + UBYTE vibrate; + CHAR* samplename; /* name of the sample */ + + /* Values used internally only */ + UWORD avibpos; /* autovibrato pos [player use] */ + UBYTE divfactor; /* for sample scaling, maintains proper period slides */ + ULONG seekpos; /* seek position in file */ + SWORD handle; /* sample handle used by individual drivers */ +} SAMPLE; + +/* Sample functions */ + +MIKMODAPI extern SAMPLE *Sample_Load(CHAR*); +MIKMODAPI extern SAMPLE *Sample_LoadFP(FILE*); +MIKMODAPI extern SAMPLE *Sample_LoadGeneric(MREADER*); +MIKMODAPI extern void Sample_Free(SAMPLE*); +MIKMODAPI extern SBYTE Sample_Play(SAMPLE*,ULONG,UBYTE); + +MIKMODAPI extern void Voice_SetVolume(SBYTE,UWORD); +MIKMODAPI extern UWORD Voice_GetVolume(SBYTE); +MIKMODAPI extern void Voice_SetFrequency(SBYTE,ULONG); +MIKMODAPI extern ULONG Voice_GetFrequency(SBYTE); +MIKMODAPI extern void Voice_SetPanning(SBYTE,ULONG); +MIKMODAPI extern ULONG Voice_GetPanning(SBYTE); +MIKMODAPI extern void Voice_Play(SBYTE,SAMPLE*,ULONG); +MIKMODAPI extern void Voice_Stop(SBYTE); +MIKMODAPI extern BOOL Voice_Stopped(SBYTE); +MIKMODAPI extern SLONG Voice_GetPosition(SBYTE); +MIKMODAPI extern ULONG Voice_RealVolume(SBYTE); + +/* + * ========== Internal module representation (UniMod) + */ + +/* + Instrument definition - for information only, the only field which may be + of use in user programs is the name field +*/ + +/* Instrument note count */ +#define INSTNOTES 120 + +/* Envelope point */ +typedef struct ENVPT { + SWORD pos; + SWORD val; +} ENVPT; + +/* Envelope point count */ +#define ENVPOINTS 32 + +/* Instrument structure */ +typedef struct INSTRUMENT { + CHAR* insname; + + UBYTE flags; + UWORD samplenumber[INSTNOTES]; + UBYTE samplenote[INSTNOTES]; + + UBYTE nnatype; + UBYTE dca; /* duplicate check action */ + UBYTE dct; /* duplicate check type */ + UBYTE globvol; + UWORD volfade; + SWORD panning; /* instrument-based panning var */ + + UBYTE pitpansep; /* pitch pan separation (0 to 255) */ + UBYTE pitpancenter; /* pitch pan center (0 to 119) */ + UBYTE rvolvar; /* random volume varations (0 - 100%) */ + UBYTE rpanvar; /* random panning varations (0 - 100%) */ + + /* volume envelope */ + UBYTE volflg; /* bit 0: on 1: sustain 2: loop */ + UBYTE volpts; + UBYTE volsusbeg; + UBYTE volsusend; + UBYTE volbeg; + UBYTE volend; + ENVPT volenv[ENVPOINTS]; + /* panning envelope */ + UBYTE panflg; /* bit 0: on 1: sustain 2: loop */ + UBYTE panpts; + UBYTE pansusbeg; + UBYTE pansusend; + UBYTE panbeg; + UBYTE panend; + ENVPT panenv[ENVPOINTS]; + /* pitch envelope */ + UBYTE pitflg; /* bit 0: on 1: sustain 2: loop */ + UBYTE pitpts; + UBYTE pitsusbeg; + UBYTE pitsusend; + UBYTE pitbeg; + UBYTE pitend; + ENVPT pitenv[ENVPOINTS]; +} INSTRUMENT; + +struct MP_CONTROL; +struct MP_VOICE; + +/* Module flags */ +#define UF_XMPERIODS 0x0001 /* XM periods / finetuning */ +#define UF_LINEAR 0x0002 /* LINEAR periods (UF_XMPERIODS must be set) */ +#define UF_INST 0x0004 /* Instruments are used */ +#define UF_NNA 0x0008 /* IT: NNA used, set numvoices rather than numchn */ +#define UF_S3MSLIDES 0x0010 /* uses old S3M volume slides */ +#define UF_BGSLIDES 0x0020 /* continue volume slides in the background */ +#define UF_HIGHBPM 0x0040 /* MED: can use >255 bpm */ +#define UF_NOWRAP 0x0080 /* XM-type (i.e. illogical) pattern brk semantics */ +#define UF_ARPMEM 0x0100 /* IT: need arpeggio memory */ +#define UF_FT2QUIRKS 0x0200 /* emulate some FT2 replay quirks */ + +typedef struct MODULE { + /* general module information */ + CHAR* songname; /* name of the song */ + CHAR* modtype; /* string type of module loaded */ + CHAR* comment; /* module comments */ + + UWORD flags; /* See module flags above */ + UBYTE numchn; /* number of module channels */ + UBYTE numvoices; /* max # voices used for full NNA playback */ + UWORD numpos; /* number of positions in this song */ + UWORD numpat; /* number of patterns in this song */ + UWORD numins; /* number of instruments */ + UWORD numsmp; /* number of samples */ +struct INSTRUMENT* instruments; /* all instruments */ +struct SAMPLE* samples; /* all samples */ + UBYTE realchn; /* real number of channels used */ + UBYTE totalchn; /* total number of channels used (incl NNAs) */ + + /* playback settings */ + UWORD reppos; /* restart position */ + UBYTE initspeed; /* initial song speed */ + UWORD inittempo; /* initial song tempo */ + UBYTE initvolume; /* initial global volume (0 - 128) */ + UWORD panning[64]; /* 64 panning positions */ + UBYTE chanvol[64]; /* 64 channel positions */ + UWORD bpm; /* current beats-per-minute speed */ + UWORD sngspd; /* current song speed */ + SWORD volume; /* song volume (0-128) (or user volume) */ + + BOOL extspd; /* extended speed flag (default enabled) */ + BOOL panflag; /* panning flag (default enabled) */ + BOOL wrap; /* wrap module ? (default disabled) */ + BOOL loop; /* allow module to loop ? (default enabled) */ + BOOL fadeout; /* volume fade out during last pattern */ + + UWORD patpos; /* current row number */ + SWORD sngpos; /* current song position */ + ULONG sngtime; /* current song time in 2^-10 seconds */ + + SWORD relspd; /* relative speed factor */ + + /* internal module representation */ + UWORD numtrk; /* number of tracks */ + UBYTE** tracks; /* array of numtrk pointers to tracks */ + UWORD* patterns; /* array of Patterns */ + UWORD* pattrows; /* array of number of rows for each pattern */ + UWORD* positions; /* all positions */ + + BOOL forbid; /* if true, no player update! */ + UWORD numrow; /* number of rows on current pattern */ + UWORD vbtick; /* tick counter (counts from 0 to sngspd) */ + UWORD sngremainder;/* used for song time computation */ + +struct MP_CONTROL* control; /* Effects Channel info (size pf->numchn) */ +struct MP_VOICE* voice; /* Audio Voice information (size md_numchn) */ + + UBYTE globalslide; /* global volume slide rate */ + UBYTE pat_repcrazy;/* module has just looped to position -1 */ + UWORD patbrk; /* position where to start a new pattern */ + UBYTE patdly; /* patterndelay counter (command memory) */ + UBYTE patdly2; /* patterndelay counter (real one) */ + SWORD posjmp; /* flag to indicate a jump is needed... */ +} MODULE; + +/* + * ========== Module loaders + */ + +struct MLOADER; + +MIKMODAPI extern CHAR* MikMod_InfoLoader(void); +MIKMODAPI extern void MikMod_RegisterAllLoaders(void); +MIKMODAPI extern void MikMod_UnregisterAllLoaders(void); +MIKMODAPI extern void MikMod_RegisterLoader(struct MLOADER*); + +MIKMODAPI extern struct MLOADER load_669; /* 669 and Extended-669 (by Tran/Renaissance) */ +MIKMODAPI extern struct MLOADER load_amf; /* DMP Advanced Module Format (by Otto Chrons) */ +MIKMODAPI extern struct MLOADER load_dsm; /* DSIK internal module format */ +MIKMODAPI extern struct MLOADER load_far; /* Farandole Composer (by Daniel Potter) */ +MIKMODAPI extern struct MLOADER load_gdm; /* General DigiMusic (by Edward Schlunder) */ +MIKMODAPI extern struct MLOADER load_it; /* Impulse Tracker (by Jeffrey Lim) */ +MIKMODAPI extern struct MLOADER load_imf; /* Imago Orpheus (by Lutz Roeder) */ +MIKMODAPI extern struct MLOADER load_med; /* Amiga MED modules (by Teijo Kinnunen) */ +MIKMODAPI extern struct MLOADER load_m15; /* Soundtracker 15-instrument */ +MIKMODAPI extern struct MLOADER load_mod; /* Standard 31-instrument Module loader */ +MIKMODAPI extern struct MLOADER load_mtm; /* Multi-Tracker Module (by Renaissance) */ +MIKMODAPI extern struct MLOADER load_stm; /* ScreamTracker 2 (by Future Crew) */ +MIKMODAPI extern struct MLOADER load_stx; /* STMIK 0.2 (by Future Crew) */ +MIKMODAPI extern struct MLOADER load_s3m; /* ScreamTracker 3 (by Future Crew) */ +MIKMODAPI extern struct MLOADER load_ult; /* UltraTracker (by MAS) */ +MIKMODAPI extern struct MLOADER load_uni; /* MikMod and APlayer internal module format */ +MIKMODAPI extern struct MLOADER load_xm; /* FastTracker 2 (by Triton) */ + +/* + * ========== Module player + */ + +/* SDL_RWops compatability */ +#ifdef USE_RWOPS +#include "SDL_rwops.h" +MIKMODAPI extern MODULE* Player_LoadRW(SDL_RWops*,int,BOOL); +#endif /* USE_RWOPS */ +/* End SDL_RWops compatability */ +MIKMODAPI extern MODULE* Player_Load(CHAR*,int,BOOL); +MIKMODAPI extern MODULE* Player_LoadFP(FILE*,int,BOOL); +MIKMODAPI extern MODULE* Player_LoadGeneric(MREADER*,int,BOOL); +MIKMODAPI extern CHAR* Player_LoadTitle(CHAR*); +MIKMODAPI extern void Player_Free(MODULE*); +MIKMODAPI extern void Player_Start(MODULE*); +MIKMODAPI extern BOOL Player_Active(void); +MIKMODAPI extern void Player_Stop(void); +MIKMODAPI extern void Player_TogglePause(void); +MIKMODAPI extern BOOL Player_Paused(void); +MIKMODAPI extern void Player_NextPosition(void); +MIKMODAPI extern void Player_PrevPosition(void); +MIKMODAPI extern void Player_SetPosition(UWORD); +MIKMODAPI extern BOOL Player_Muted(UBYTE); +MIKMODAPI extern void Player_SetVolume(SWORD); +MIKMODAPI extern MODULE* Player_GetModule(void); +MIKMODAPI extern void Player_SetSpeed(UWORD); +MIKMODAPI extern void Player_SetTempo(UWORD); +MIKMODAPI extern void Player_Unmute(SLONG,...); +MIKMODAPI extern void Player_Mute(SLONG,...); +MIKMODAPI extern void Player_ToggleMute(SLONG,...); +MIKMODAPI extern int Player_GetChannelVoice(UBYTE); +MIKMODAPI extern UWORD Player_GetChannelPeriod(UBYTE); +MIKMODAPI extern void Player_SetSynchroValue(int); +MIKMODAPI extern int Player_GetSynchroValue(void); + +typedef void (MikMod_player)(void); +typedef MikMod_player *MikMod_player_t; + +MIKMODAPI extern MikMod_player_t MikMod_RegisterPlayer(MikMod_player_t); + +#define MUTE_EXCLUSIVE 32000 +#define MUTE_INCLUSIVE 32001 + +/* + * ========== Drivers + */ + +enum { + MD_MUSIC = 0, + MD_SNDFX +}; + +enum { + MD_HARDWARE = 0, + MD_SOFTWARE +}; + +/* Mixing flags */ + +/* These ones take effect only after MikMod_Init or MikMod_Reset */ +#define DMODE_16BITS 0x0001 /* enable 16 bit output */ +#define DMODE_STEREO 0x0002 /* enable stereo output */ +#define DMODE_SOFT_SNDFX 0x0004 /* Process sound effects via software mixer */ +#define DMODE_SOFT_MUSIC 0x0008 /* Process music via software mixer */ +#define DMODE_HQMIXER 0x0010 /* Use high-quality (slower) software mixer */ +/* These take effect immediately. */ +#define DMODE_SURROUND 0x0100 /* enable surround sound */ +#define DMODE_INTERP 0x0200 /* enable interpolation */ +#define DMODE_REVERSE 0x0400 /* reverse stereo */ + +struct SAMPLOAD; +typedef struct MDRIVER { +struct MDRIVER* next; + CHAR* Name; + CHAR* Version; + + UBYTE HardVoiceLimit; /* Limit of hardware mixer voices */ + UBYTE SoftVoiceLimit; /* Limit of software mixer voices */ + + CHAR* Alias; + + void (*CommandLine) (CHAR*); + BOOL (*IsPresent) (void); + SWORD (*SampleLoad) (struct SAMPLOAD*,int); + void (*SampleUnload) (SWORD); + ULONG (*FreeSampleSpace) (int); + ULONG (*RealSampleLength) (int,struct SAMPLE*); + BOOL (*Init) (void); + void (*Exit) (void); + BOOL (*Reset) (void); + BOOL (*SetNumVoices) (void); + BOOL (*PlayStart) (void); + void (*PlayStop) (void); + void (*Update) (void); + void (*Pause) (void); + void (*VoiceSetVolume) (UBYTE,UWORD); + UWORD (*VoiceGetVolume) (UBYTE); + void (*VoiceSetFrequency)(UBYTE,ULONG); + ULONG (*VoiceGetFrequency)(UBYTE); + void (*VoiceSetPanning) (UBYTE,ULONG); + ULONG (*VoiceGetPanning) (UBYTE); + void (*VoicePlay) (UBYTE,SWORD,ULONG,ULONG,ULONG,ULONG,UWORD); + void (*VoiceStop) (UBYTE); + BOOL (*VoiceStopped) (UBYTE); + SLONG (*VoiceGetPosition) (UBYTE); + ULONG (*VoiceRealVolume) (UBYTE); +} MDRIVER; + +/* These variables can be changed at ANY time and results will be immediate */ +MIKMODAPI extern UBYTE md_volume; /* global sound volume (0-128) */ +MIKMODAPI extern UBYTE md_musicvolume; /* volume of song */ +MIKMODAPI extern UBYTE md_sndfxvolume; /* volume of sound effects */ +MIKMODAPI extern UBYTE md_reverb; /* 0 = none; 15 = chaos */ +MIKMODAPI extern UBYTE md_pansep; /* 0 = mono; 128 == 100% (full left/right) */ + +/* The variables below can be changed at any time, but changes will not be + implemented until MikMod_Reset is called. A call to MikMod_Reset may result + in a skip or pop in audio (depending on the soundcard driver and the settings + changed). */ +MIKMODAPI extern UWORD md_device; /* device */ +MIKMODAPI extern UWORD md_mixfreq; /* mixing frequency */ +MIKMODAPI extern UWORD md_mode; /* mode. See DMODE_? flags above */ + +/* The following variable should not be changed! */ +MIKMODAPI extern MDRIVER* md_driver; /* Current driver in use. */ + +/* Known drivers list */ + +MIKMODAPI extern struct MDRIVER drv_nos; /* no sound */ +MIKMODAPI extern struct MDRIVER drv_pipe; /* piped output */ +MIKMODAPI extern struct MDRIVER drv_raw; /* raw file disk writer [music.raw] */ +MIKMODAPI extern struct MDRIVER drv_stdout; /* output to stdout */ +MIKMODAPI extern struct MDRIVER drv_wav; /* RIFF WAVE file disk writer [music.wav] */ + +MIKMODAPI extern struct MDRIVER drv_ultra; /* Linux Ultrasound driver */ + +MIKMODAPI extern struct MDRIVER drv_AF; /* Dec Alpha AudioFile */ +MIKMODAPI extern struct MDRIVER drv_aix; /* AIX audio device */ +MIKMODAPI extern struct MDRIVER drv_alsa; /* Advanced Linux Sound Architecture (ALSA) */ +MIKMODAPI extern struct MDRIVER drv_esd; /* Enlightened sound daemon (EsounD) */ +MIKMODAPI extern struct MDRIVER drv_hp; /* HP-UX audio device */ +MIKMODAPI extern struct MDRIVER drv_oss; /* OpenSound System (Linux,FreeBSD...) */ +MIKMODAPI extern struct MDRIVER drv_sgi; /* SGI audio library */ +MIKMODAPI extern struct MDRIVER drv_sun; /* Sun/NetBSD/OpenBSD audio device */ + +MIKMODAPI extern struct MDRIVER drv_dart; /* OS/2 Direct Audio RealTime */ +MIKMODAPI extern struct MDRIVER drv_os2; /* OS/2 MMPM/2 */ + +MIKMODAPI extern struct MDRIVER drv_ds; /* Win32 DirectSound driver */ +MIKMODAPI extern struct MDRIVER drv_win; /* Win32 multimedia API driver */ + +MIKMODAPI extern struct MDRIVER drv_mac; /* Macintosh Sound Manager driver */ + +/*========== Virtual channel mixer interface (for user-supplied drivers only) */ + +MIKMODAPI extern BOOL VC_Init(void); +MIKMODAPI extern void VC_Exit(void); +MIKMODAPI extern BOOL VC_SetNumVoices(void); +MIKMODAPI extern ULONG VC_SampleSpace(int); +MIKMODAPI extern ULONG VC_SampleLength(int,SAMPLE*); + +MIKMODAPI extern BOOL VC_PlayStart(void); +MIKMODAPI extern void VC_PlayStop(void); + +MIKMODAPI extern SWORD VC_SampleLoad(struct SAMPLOAD*,int); +MIKMODAPI extern void VC_SampleUnload(SWORD); + +MIKMODAPI extern ULONG VC_WriteBytes(SBYTE*,ULONG); +MIKMODAPI extern ULONG VC_SilenceBytes(SBYTE*,ULONG); + +MIKMODAPI extern void VC_VoiceSetVolume(UBYTE,UWORD); +MIKMODAPI extern UWORD VC_VoiceGetVolume(UBYTE); +MIKMODAPI extern void VC_VoiceSetFrequency(UBYTE,ULONG); +MIKMODAPI extern ULONG VC_VoiceGetFrequency(UBYTE); +MIKMODAPI extern void VC_VoiceSetPanning(UBYTE,ULONG); +MIKMODAPI extern ULONG VC_VoiceGetPanning(UBYTE); +MIKMODAPI extern void VC_VoicePlay(UBYTE,SWORD,ULONG,ULONG,ULONG,ULONG,UWORD); + +MIKMODAPI extern void VC_VoiceStop(UBYTE); +MIKMODAPI extern BOOL VC_VoiceStopped(UBYTE); +MIKMODAPI extern SLONG VC_VoiceGetPosition(UBYTE); +MIKMODAPI extern ULONG VC_VoiceRealVolume(UBYTE); + +#ifdef __cplusplus +} +#endif + +#endif + +/* ex:set ts=4: */ diff --git a/sc2/src/sc2code/libs/sound/decoders/mikmod/mikmod_build.h b/sc2/src/sc2code/libs/sound/decoders/mikmod/mikmod_build.h new file mode 100644 index 000000000..29fe3b443 --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/mikmod/mikmod_build.h @@ -0,0 +1,9 @@ +#include "mikmod.h" + +#if defined(WIN32) && !defined(__STDC__) +# define __STDC__ 1 +#endif + +#if defined(WIN32) && defined(_MSC_VER) +# pragma warning(disable: 4018 4244) +#endif diff --git a/sc2/src/sc2code/libs/sound/decoders/mikmod/mikmod_internals.h b/sc2/src/sc2code/libs/sound/decoders/mikmod/mikmod_internals.h new file mode 100644 index 000000000..d33d4f01c --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/mikmod/mikmod_internals.h @@ -0,0 +1,712 @@ +/* MikMod sound library + (c) 1998, 1999, 2000 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + MikMod sound library internal definitions + +==============================================================================*/ + +#ifndef _MIKMOD_INTERNALS_H +#define _MIKMOD_INTERNALS_H + +#ifdef HAVE_MALLOC_H +#include +#endif +#include +#if defined(__OS2__)||defined(__EMX__)||defined(WIN32) +#define strcasecmp(s,t) stricmp(s,t) +#endif + +#include "mikmod_build.h" + +#ifdef macintosh +#ifndef __MWERKS__ +#define __STDC__=1 +#endif +static char *strdup(const char *str) +{ + char *newstr; + + newstr = (char *)malloc(strlen(str)+1); + if ( newstr != NULL ) { + strcpy(newstr, str); + } + return(newstr); +} +#endif + +#ifdef WIN32 +#ifndef __STDC__ +#define __STDC__ +#endif +#pragma warning(disable:4761) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/*========== More type definitions */ + +/* SLONGLONG: 64bit, signed */ +#if defined(__alpha) +typedef long SLONGLONG; +#define NATIVE_64BIT_INT +#elif defined(__WATCOMC__) +typedef __int64 SLONGLONG; +#elif defined(WIN32) && !defined(__MWERKS__) +typedef LONGLONG SLONGLONG; +#else +typedef long long SLONGLONG; +#endif + +/*========== Error handling */ + +#define _mm_errno MikMod_errno +#define _mm_critical MikMod_critical +extern MikMod_handler_t _mm_errorhandler; + +/*========== Memory allocation */ + +extern void* _mm_malloc(size_t); +extern void* _mm_calloc(size_t,size_t); +#define _mm_free(p) { if (p) free(p); p = NULL; } + +/*========== MT stuff */ + +#ifdef HAVE_PTHREAD +#if defined(__OpenBSD__) && !defined(_POSIX_THREADS) +#define _POSIX_THREADS +#endif +#include +#define DECLARE_MUTEX(name) \ + extern pthread_mutex_t _mm_mutex_##name +#define MUTEX_LOCK(name) \ + pthread_mutex_lock(&_mm_mutex_##name) +#define MUTEX_UNLOCK(name) \ + pthread_mutex_unlock(&_mm_mutex_##name) +#elif defined(__OS2__)||defined(__EMX__) +#define DECLARE_MUTEX(name) \ + extern HMTX _mm_mutex_##name +#define MUTEX_LOCK(name) \ + if(_mm_mutex_##name) \ + DosRequestMutexSem(_mm_mutex_##name,SEM_INDEFINITE_WAIT) +#define MUTEX_UNLOCK(name) \ + if(_mm_mutex_##name) \ + DosReleaseMutexSem(_mm_mutex_##name) +#elif defined(WIN32) +#include +#define DECLARE_MUTEX(name) \ + extern HANDLE _mm_mutex_##name +#define MUTEX_LOCK(name) \ + if(_mm_mutex_##name) \ + WaitForSingleObject(_mm_mutex_##name,INFINITE) +#define MUTEX_UNLOCK(name) \ + if(_mm_mutex_##name) \ + ReleaseMutex(_mm_mutex_##name) +#else +#define DECLARE_MUTEX(name) +#define MUTEX_LOCK(name) +#define MUTEX_UNLOCK(name) +#endif + +DECLARE_MUTEX(lists); +DECLARE_MUTEX(vars); + +/*========== Portable file I/O */ + +/* SDL_RWops compatability */ +#ifdef USE_RWOPS +extern MREADER *_mm_new_rwops_reader(SDL_RWops * rw); +extern void _mm_delete_rwops_reader (MREADER*); +#endif /* USE_RWOPS */ +/* End SDL_RWops compatability */ + +extern MREADER* _mm_new_file_reader(FILE* fp); +extern void _mm_delete_file_reader(MREADER*); + +extern MWRITER* _mm_new_file_writer(FILE *fp); +extern void _mm_delete_file_writer(MWRITER*); + +extern BOOL _mm_FileExists(CHAR *fname); + +#define _mm_write_SBYTE(x,y) y->Put(y,(int)x) +#define _mm_write_UBYTE(x,y) y->Put(y,(int)x) + +#define _mm_read_SBYTE(x) (SBYTE)x->Get(x) +#define _mm_read_UBYTE(x) (UBYTE)x->Get(x) + +#define _mm_write_SBYTES(x,y,z) z->Write(z,(void *)x,y) +#define _mm_write_UBYTES(x,y,z) z->Write(z,(void *)x,y) +#define _mm_read_SBYTES(x,y,z) z->Read(z,(void *)x,y) +#define _mm_read_UBYTES(x,y,z) z->Read(z,(void *)x,y) + +#define _mm_fseek(x,y,z) x->Seek(x,y,z) +#define _mm_ftell(x) x->Tell(x) +#define _mm_rewind(x) _mm_fseek(x,0,SEEK_SET) + +#define _mm_eof(x) x->Eof(x) + +extern void _mm_iobase_setcur(MREADER*); +extern void _mm_iobase_revert(void); +extern FILE *_mm_fopen(CHAR*,CHAR*); +extern void _mm_write_string(CHAR*,MWRITER*); +extern int _mm_read_string (CHAR*,int,MREADER*); + +extern SWORD _mm_read_M_SWORD(MREADER*); +extern SWORD _mm_read_I_SWORD(MREADER*); +extern UWORD _mm_read_M_UWORD(MREADER*); +extern UWORD _mm_read_I_UWORD(MREADER*); + +extern SLONG _mm_read_M_SLONG(MREADER*); +extern SLONG _mm_read_I_SLONG(MREADER*); +extern ULONG _mm_read_M_ULONG(MREADER*); +extern ULONG _mm_read_I_ULONG(MREADER*); + +extern int _mm_read_M_SWORDS(SWORD*,int,MREADER*); +extern int _mm_read_I_SWORDS(SWORD*,int,MREADER*); +extern int _mm_read_M_UWORDS(UWORD*,int,MREADER*); +extern int _mm_read_I_UWORDS(UWORD*,int,MREADER*); + +extern int _mm_read_M_SLONGS(SLONG*,int,MREADER*); +extern int _mm_read_I_SLONGS(SLONG*,int,MREADER*); +extern int _mm_read_M_ULONGS(ULONG*,int,MREADER*); +extern int _mm_read_I_ULONGS(ULONG*,int,MREADER*); + +extern void _mm_write_M_SWORD(SWORD,MWRITER*); +extern void _mm_write_I_SWORD(SWORD,MWRITER*); +extern void _mm_write_M_UWORD(UWORD,MWRITER*); +extern void _mm_write_I_UWORD(UWORD,MWRITER*); + +extern void _mm_write_M_SLONG(SLONG,MWRITER*); +extern void _mm_write_I_SLONG(SLONG,MWRITER*); +extern void _mm_write_M_ULONG(ULONG,MWRITER*); +extern void _mm_write_I_ULONG(ULONG,MWRITER*); + +extern void _mm_write_M_SWORDS(SWORD*,int,MWRITER*); +extern void _mm_write_I_SWORDS(SWORD*,int,MWRITER*); +extern void _mm_write_M_UWORDS(UWORD*,int,MWRITER*); +extern void _mm_write_I_UWORDS(UWORD*,int,MWRITER*); + +extern void _mm_write_M_SLONGS(SLONG*,int,MWRITER*); +extern void _mm_write_I_SLONGS(SLONG*,int,MWRITER*); +extern void _mm_write_M_ULONGS(ULONG*,int,MWRITER*); +extern void _mm_write_I_ULONGS(ULONG*,int,MWRITER*); + +/*========== Samples */ + +/* This is a handle of sorts attached to any sample registered with + SL_RegisterSample. Generally, this only need be used or changed by the + loaders and drivers of mikmod. */ +typedef struct SAMPLOAD { + struct SAMPLOAD *next; + + ULONG length; /* length of sample (in samples!) */ + ULONG loopstart; /* repeat position (relative to start, in samples) */ + ULONG loopend; /* repeat end */ + UWORD infmt,outfmt; + int scalefactor; + SAMPLE* sample; + MREADER* reader; +} SAMPLOAD; + +/*========== Sample and waves loading interface */ + +extern void SL_HalveSample(SAMPLOAD*,int); +extern void SL_Sample8to16(SAMPLOAD*); +extern void SL_Sample16to8(SAMPLOAD*); +extern void SL_SampleSigned(SAMPLOAD*); +extern void SL_SampleUnsigned(SAMPLOAD*); +extern BOOL SL_LoadSamples(void); +extern SAMPLOAD* SL_RegisterSample(SAMPLE*,int,MREADER*); +extern BOOL SL_Load(void*,SAMPLOAD*,ULONG); +extern BOOL SL_Init(SAMPLOAD*); +extern void SL_Exit(SAMPLOAD*); + +/*========== Internal module representation (UniMod) interface */ + +/* number of notes in an octave */ +#define OCTAVE 12 + +extern void UniSetRow(UBYTE*); +extern UBYTE UniGetByte(void); +extern UWORD UniGetWord(void); +extern UBYTE* UniFindRow(UBYTE*,UWORD); +extern void UniSkipOpcode(UBYTE); +extern void UniReset(void); +extern void UniWriteByte(UBYTE); +extern void UniWriteWord(UWORD); +extern void UniNewline(void); +extern UBYTE* UniDup(void); +extern BOOL UniInit(void); +extern void UniCleanup(void); +extern void UniEffect(UWORD,UWORD); +#define UniInstrument(x) UniEffect(UNI_INSTRUMENT,x) +#define UniNote(x) UniEffect(UNI_NOTE,x) +extern void UniPTEffect(UBYTE,UBYTE); +extern void UniVolEffect(UWORD,UBYTE); + +/*========== Module Commands */ + +enum { + /* Simple note */ + UNI_NOTE = 1, + /* Instrument change */ + UNI_INSTRUMENT, + /* Protracker effects */ + UNI_PTEFFECT0, /* arpeggio */ + UNI_PTEFFECT1, /* porta up */ + UNI_PTEFFECT2, /* porta down */ + UNI_PTEFFECT3, /* porta to note */ + UNI_PTEFFECT4, /* vibrato */ + UNI_PTEFFECT5, /* dual effect 3+A */ + UNI_PTEFFECT6, /* dual effect 4+A */ + UNI_PTEFFECT7, /* tremolo */ + UNI_PTEFFECT8, /* pan */ + UNI_PTEFFECT9, /* sample offset */ + UNI_PTEFFECTA, /* volume slide */ + UNI_PTEFFECTB, /* pattern jump */ + UNI_PTEFFECTC, /* set volume */ + UNI_PTEFFECTD, /* pattern break */ + UNI_PTEFFECTE, /* extended effects */ + UNI_PTEFFECTF, /* set speed */ + /* Scream Tracker effects */ + UNI_S3MEFFECTA, /* set speed */ + UNI_S3MEFFECTD, /* volume slide */ + UNI_S3MEFFECTE, /* porta down */ + UNI_S3MEFFECTF, /* porta up */ + UNI_S3MEFFECTI, /* tremor */ + UNI_S3MEFFECTQ, /* retrig */ + UNI_S3MEFFECTR, /* tremolo */ + UNI_S3MEFFECTT, /* set tempo */ + UNI_S3MEFFECTU, /* fine vibrato */ + UNI_KEYOFF, /* note off */ + /* Fast Tracker effects */ + UNI_KEYFADE, /* note fade */ + UNI_VOLEFFECTS, /* volume column effects */ + UNI_XMEFFECT4, /* vibrato */ + UNI_XMEFFECTA, /* volume slide */ + UNI_XMEFFECTE1, /* fine porta up */ + UNI_XMEFFECTE2, /* fine porta down */ + UNI_XMEFFECTEA, /* fine volume slide up */ + UNI_XMEFFECTEB, /* fine volume slide down */ + UNI_XMEFFECTG, /* set global volume */ + UNI_XMEFFECTH, /* global volume slide */ + UNI_XMEFFECTL, /* set envelope position */ + UNI_XMEFFECTP, /* pan slide */ + UNI_XMEFFECTX1, /* extra fine porta up */ + UNI_XMEFFECTX2, /* extra fine porta down */ + /* Impulse Tracker effects */ + UNI_ITEFFECTG, /* porta to note */ + UNI_ITEFFECTH, /* vibrato */ + UNI_ITEFFECTI, /* tremor (xy not incremented) */ + UNI_ITEFFECTM, /* set channel volume */ + UNI_ITEFFECTN, /* slide / fineslide channel volume */ + UNI_ITEFFECTP, /* slide / fineslide channel panning */ + UNI_ITEFFECTT, /* slide tempo */ + UNI_ITEFFECTU, /* fine vibrato */ + UNI_ITEFFECTW, /* slide / fineslide global volume */ + UNI_ITEFFECTY, /* panbrello */ + UNI_ITEFFECTZ, /* resonant filters */ + UNI_ITEFFECTS0, + /* UltraTracker effects */ + UNI_ULTEFFECT9, /* Sample fine offset */ + /* OctaMED effects */ + UNI_MEDSPEED, + UNI_MEDEFFECTF1, /* play note twice */ + UNI_MEDEFFECTF2, /* delay note */ + UNI_MEDEFFECTF3, /* play note three times */ + UNI_XMEFFECTZ, /* XM Z synchro */ + + UNI_LAST +}; + +extern UWORD unioperands[UNI_LAST]; + +/* IT / S3M Extended SS effects: */ +enum { + SS_GLISSANDO = 1, + SS_FINETUNE, + SS_VIBWAVE, + SS_TREMWAVE, + SS_PANWAVE, + SS_FRAMEDELAY, + SS_S7EFFECTS, + SS_PANNING, + SS_SURROUND, + SS_HIOFFSET, + SS_PATLOOP, + SS_NOTECUT, + SS_NOTEDELAY, + SS_PATDELAY +}; + +/* IT Volume column effects */ +enum { + VOL_VOLUME = 1, + VOL_PANNING, + VOL_VOLSLIDE, + VOL_PITCHSLIDEDN, + VOL_PITCHSLIDEUP, + VOL_PORTAMENTO, + VOL_VIBRATO +}; + +/* IT resonant filter information */ + +#define FILT_CUT 0x80 +#define FILT_RESONANT 0x81 + +typedef struct FILTER { + UBYTE filter,inf; +} FILTER; + +/*========== Instruments */ + +/* Instrument format flags */ +#define IF_OWNPAN 1 +#define IF_PITCHPAN 2 + +/* Envelope flags: */ +#define EF_ON 1 +#define EF_SUSTAIN 2 +#define EF_LOOP 4 +#define EF_VOLENV 8 + +/* New Note Action Flags */ +#define NNA_CUT 0 +#define NNA_CONTINUE 1 +#define NNA_OFF 2 +#define NNA_FADE 3 + +#define NNA_MASK 3 + +#define DCT_OFF 0 +#define DCT_NOTE 1 +#define DCT_SAMPLE 2 +#define DCT_INST 3 + +#define DCA_CUT 0 +#define DCA_OFF 1 +#define DCA_FADE 2 + +#define KEY_KICK 0 +#define KEY_OFF 1 +#define KEY_FADE 2 +#define KEY_KILL (KEY_OFF|KEY_FADE) + +#define KICK_ABSENT 0 +#define KICK_NOTE 1 +#define KICK_KEYOFF 2 +#define KICK_ENV 4 + +#define AV_IT 1 /* IT vs. XM vibrato info */ + +/*========== Playing */ + +#define POS_NONE (-2) /* no loop position defined */ + +typedef struct ENVPR { + UBYTE flg; /* envelope flag */ + UBYTE pts; /* number of envelope points */ + UBYTE susbeg; /* envelope sustain index begin */ + UBYTE susend; /* envelope sustain index end */ + UBYTE beg; /* envelope loop begin */ + UBYTE end; /* envelope loop end */ + SWORD p; /* current envelope counter */ + UWORD a; /* envelope index a */ + UWORD b; /* envelope index b */ + ENVPT* env; /* envelope points */ +} ENVPR; + +typedef struct MP_CONTROL { + INSTRUMENT* i; + SAMPLE* s; + UBYTE sample; /* which sample number */ + UBYTE note; /* the audible note as heard, direct rep of period */ + SWORD outvolume; /* output volume (vol + sampcol + instvol) */ + SBYTE chanvol; /* channel's "global" volume */ + UWORD fadevol; /* fading volume rate */ + SWORD panning; /* panning position */ + UBYTE kick; /* if true = sample has to be restarted */ + UWORD period; /* period to play the sample at */ + UBYTE nna; /* New note action type + master/slave flags */ + + UBYTE volflg; /* volume envelope settings */ + UBYTE panflg; /* panning envelope settings */ + UBYTE pitflg; /* pitch envelope settings */ + + UBYTE keyoff; /* if true = fade out and stuff */ + SWORD handle; /* which sample-handle */ + UBYTE notedelay; /* (used for note delay) */ + SLONG start; /* The starting byte index in the sample */ + +struct MP_VOICE*slave; /* Audio Slave of current effects control channel */ + + UBYTE slavechn; /* Audio Slave of current effects control channel */ + UBYTE muted; /* if set, channel not played */ + UWORD ultoffset; /* fine sample offset memory */ + UBYTE anote; /* the note that indexes the audible */ + UBYTE oldnote; + SWORD ownper; + SWORD ownvol; + UBYTE dca; /* duplicate check action */ + UBYTE dct; /* duplicate check type */ + UBYTE* row; /* row currently playing on this channel */ + SBYTE retrig; /* retrig value (0 means don't retrig) */ + ULONG speed; /* what finetune to use */ + SWORD volume; /* amiga volume (0 t/m 64) to play the sample at */ + + SWORD tmpvolume; /* tmp volume */ + UWORD tmpperiod; /* tmp period */ + UWORD wantedperiod; /* period to slide to (with effect 3 or 5) */ + + UBYTE arpmem; /* arpeggio command memory */ + UBYTE pansspd; /* panslide speed */ + UWORD slidespeed; + UWORD portspeed; /* noteslide speed (toneportamento) */ + + UBYTE s3mtremor; /* s3m tremor (effect I) counter */ + UBYTE s3mtronof; /* s3m tremor ontime/offtime */ + UBYTE s3mvolslide; /* last used volslide */ + SBYTE sliding; + UBYTE s3mrtgspeed; /* last used retrig speed */ + UBYTE s3mrtgslide; /* last used retrig slide */ + + UBYTE glissando; /* glissando (0 means off) */ + UBYTE wavecontrol; + + SBYTE vibpos; /* current vibrato position */ + UBYTE vibspd; /* "" speed */ + UBYTE vibdepth; /* "" depth */ + + SBYTE trmpos; /* current tremolo position */ + UBYTE trmspd; /* "" speed */ + UBYTE trmdepth; /* "" depth */ + + UBYTE fslideupspd; + UBYTE fslidednspd; + UBYTE fportupspd; /* fx E1 (extra fine portamento up) data */ + UBYTE fportdnspd; /* fx E2 (extra fine portamento dn) data */ + UBYTE ffportupspd; /* fx X1 (extra fine portamento up) data */ + UBYTE ffportdnspd; /* fx X2 (extra fine portamento dn) data */ + + ULONG hioffset; /* last used high order of sample offset */ + UWORD soffset; /* last used low order of sample-offset (effect 9) */ + + UBYTE sseffect; /* last used Sxx effect */ + UBYTE ssdata; /* last used Sxx data info */ + UBYTE chanvolslide; /* last used channel volume slide */ + + UBYTE panbwave; /* current panbrello waveform */ + UBYTE panbpos; /* current panbrello position */ + SBYTE panbspd; /* "" speed */ + UBYTE panbdepth; /* "" depth */ + + UWORD newsamp; /* set to 1 upon a sample / inst change */ + UBYTE voleffect; /* Volume Column Effect Memory as used by IT */ + UBYTE voldata; /* Volume Column Data Memory */ + + SWORD pat_reppos; /* patternloop position */ + UWORD pat_repcnt; /* times to loop */ +} MP_CONTROL; + +/* Used by NNA only player (audio control. AUDTMP is used for full effects + control). */ +typedef struct MP_VOICE { + INSTRUMENT* i; + SAMPLE* s; + UBYTE sample; /* which instrument number */ + + UBYTE note; /* the audible note (as heard, direct rep of period) */ + SWORD volume; /* output volume (vol + sampcol + instvol) */ + SBYTE chanvol; /* channel's "global" volume */ + UWORD fadevol; /* fading volume rate */ + SWORD panning; /* panning position */ + UBYTE kick; /* if true = sample has to be restarted */ + UWORD period; /* period to play the sample at */ + UBYTE nna; /* New note action type + master/slave flags */ + UBYTE volflg; /* volume envelope settings */ + UBYTE panflg; /* panning envelope settings */ + UBYTE pitflg; /* pitch envelope settings */ + UBYTE keyoff; /* if true = fade out and stuff */ + SWORD handle; /* which sample-handle */ + SLONG start; /* The start byte index in the sample */ + +/* Below here is info NOT in MP_CONTROL!! */ + ENVPR venv; + ENVPR penv; + ENVPR cenv; + + UWORD avibpos; /* autovibrato pos */ + UWORD aswppos; /* autovibrato sweep pos */ + + ULONG totalvol; /* total volume of channel (before global mixings) */ + + BOOL mflag; + SWORD masterchn; + UWORD masterperiod; + + MP_CONTROL* master; /* index of "master" effects channel */ +} MP_VOICE; + +/*========== Loaders */ + +typedef struct MLOADER { +struct MLOADER* next; + CHAR* type; + CHAR* version; + BOOL (*Init)(void); + BOOL (*Test)(void); + BOOL (*Load)(BOOL); + void (*Cleanup)(void); + CHAR* (*LoadTitle)(void); +} MLOADER; + +/* internal loader variables */ +extern MREADER* modreader; +extern UWORD finetune[16]; +extern MODULE of; /* static unimod loading space */ +extern UWORD npertab[7*OCTAVE]; /* used by the original MOD loaders */ + +extern SBYTE remap[64]; /* for removing empty channels */ +extern UBYTE* poslookup; /* lookup table for pattern jumps after + blank pattern removal */ +extern UBYTE poslookupcnt; +extern UWORD* origpositions; + +extern BOOL filters; /* resonant filters in use */ +extern UBYTE activemacro; /* active midi macro number for Sxx */ +extern UBYTE filtermacros[16]; /* midi macros settings */ +extern FILTER filtersettings[256]; /* computed filter settings */ + +extern int* noteindex; + +/*========== Internal loader interface */ + +extern BOOL ReadComment(UWORD); +extern BOOL ReadLinedComment(UWORD,UWORD); +extern BOOL AllocPositions(int); +extern BOOL AllocPatterns(void); +extern BOOL AllocTracks(void); +extern BOOL AllocInstruments(void); +extern BOOL AllocSamples(void); +extern CHAR* DupStr(CHAR*,UWORD,BOOL); + +/* loader utility functions */ +extern int* AllocLinear(void); +extern void FreeLinear(void); +extern int speed_to_finetune(ULONG,int); +extern void S3MIT_ProcessCmd(UBYTE,UBYTE,BOOL); +extern void S3MIT_CreateOrders(BOOL); + +/* used to convert c4spd to linear XM periods (IT and IMF loaders). */ +extern UWORD getlinearperiod(UWORD,ULONG); +extern ULONG getfrequency(UBYTE,ULONG); + +/* loader shared data */ +#define STM_NTRACKERS 3 +extern CHAR *STM_Signatures[STM_NTRACKERS]; + +/*========== Player interface */ + +extern BOOL Player_Init(MODULE*); +extern void Player_Exit(MODULE*); +extern void Player_HandleTick(void); + +/*========== Drivers */ + +/* max. number of handles a driver has to provide. (not strict) */ +#define MAXSAMPLEHANDLES 384 + +/* These variables can be changed at ANY time and results will be immediate */ +extern UWORD md_bpm; /* current song / hardware BPM rate */ + +/* Variables below can be changed via MD_SetNumVoices at any time. However, a + call to MD_SetNumVoicess while the driver is active will cause the sound to + skip slightly. */ +extern UBYTE md_numchn; /* number of song + sound effects voices */ +extern UBYTE md_sngchn; /* number of song voices */ +extern UBYTE md_sfxchn; /* number of sound effects voices */ +extern UBYTE md_hardchn; /* number of hardware mixed voices */ +extern UBYTE md_softchn; /* number of software mixed voices */ + +/* This is for use by the hardware drivers only. It points to the registered + tickhandler function. */ +extern void (*md_player)(void); + +extern SWORD MD_SampleLoad(SAMPLOAD*,int); +extern void MD_SampleUnload(SWORD); +extern ULONG MD_SampleSpace(int); +extern ULONG MD_SampleLength(int,SAMPLE*); + +/* uLaw conversion */ +extern void unsignedtoulaw(char *,int); + +/* Parameter extraction helper */ +extern CHAR *MD_GetAtom(CHAR*,CHAR*,BOOL); + +/* Internal software mixer stuff */ +extern void VC_SetupPointers(void); +extern BOOL VC1_Init(void); +extern BOOL VC2_Init(void); + +#ifdef unix +/* POSIX helper functions */ +extern BOOL MD_Access(CHAR *); +extern BOOL MD_DropPrivileges(void); +#endif + +/* Macro to define a missing driver, yet allowing binaries to dynamically link + with the library without missing symbol errors */ +#define MISSING(a) MDRIVER a = { NULL, NULL, NULL, 0, 0 } + +/*========== Prototypes for non-MT safe versions of some public functions */ + +extern void _mm_registerdriver(struct MDRIVER*); +extern void _mm_unregisterdrivers(void); +extern void _mm_registerloader(struct MLOADER*); +extern void _mm_unregisterloaders(void); +extern BOOL MikMod_Active_internal(void); +extern void MikMod_DisableOutput_internal(void); +extern BOOL MikMod_EnableOutput_internal(void); +extern void MikMod_Exit_internal(void); +extern BOOL MikMod_SetNumVoices_internal(int,int); +extern void Player_Exit_internal(MODULE*); +extern void Player_Stop_internal(void); +extern BOOL Player_Paused_internal(void); +extern void Sample_Free_internal(SAMPLE*); +extern void Voice_Play_internal(SBYTE,SAMPLE*,ULONG); +extern void Voice_SetFrequency_internal(SBYTE,ULONG); +extern void Voice_SetPanning_internal(SBYTE,ULONG); +extern void Voice_SetVolume_internal(SBYTE,UWORD); +extern void Voice_Stop_internal(SBYTE); +extern BOOL Voice_Stopped_internal(SBYTE); + +#ifdef __cplusplus +} +#endif + +#endif + +/* ex:set ts=4: */ diff --git a/sc2/src/sc2code/libs/sound/decoders/mikmod/mloader.c b/sc2/src/sc2code/libs/sound/decoders/mikmod/mloader.c new file mode 100644 index 000000000..445ac0d80 --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/mikmod/mloader.c @@ -0,0 +1,556 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + These routines are used to access the available module loaders + +==============================================================================*/ + +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + + + MREADER *modreader; + MODULE of; + +static MLOADER *firstloader=NULL; + +UWORD finetune[16]={ + 8363,8413,8463,8529,8581,8651,8723,8757, + 7895,7941,7985,8046,8107,8169,8232,8280 +}; + +CHAR* MikMod_InfoLoader(void) +{ + int len=0; + MLOADER *l; + CHAR *list=NULL; + + MUTEX_LOCK(lists); + /* compute size of buffer */ + for(l=firstloader;l;l=l->next) len+=1+(l->next?1:0)+strlen(l->version); + + if(len) + if((list=_mm_malloc(len*sizeof(CHAR)))) { + list[0]=0; + /* list all registered module loders */ + for(l=firstloader;l;l=l->next) + sprintf(list,(l->next)?"%s%s\n":"%s%s",list,l->version); + } + MUTEX_UNLOCK(lists); + return list; +} + +void _mm_registerloader(MLOADER* ldr) +{ + MLOADER *cruise=firstloader; + + if(cruise) { + while(cruise->next) cruise = cruise->next; + cruise->next=ldr; + } else + firstloader=ldr; +} + +void _mm_unregisterloaders(void) +{ + MLOADER *cruise=firstloader, *ldr; + + while ( cruise ) { + ldr = cruise; + cruise = cruise->next; + ldr->next = NULL; + } + firstloader = NULL; +} + +void MikMod_RegisterLoader(struct MLOADER* ldr) +{ + /* if we try to register an invalid loader, or an already registered loader, + ignore this attempt */ + if ((!ldr)||(ldr->next)) + return; + + MUTEX_LOCK(lists); + _mm_registerloader(ldr); + MUTEX_UNLOCK(lists); +} + +BOOL ReadComment(UWORD len) +{ + if(len) { + int i; + + if(!(of.comment=(CHAR*)_mm_malloc(len+1))) return 0; + _mm_read_UBYTES(of.comment,len,modreader); + + /* translate IT linefeeds */ + for(i=0;i=0)&&(line[i]==' ');i--) line[i]=0; + for(i=0;ilines) { + if(!(of.comment=(CHAR*)_mm_malloc(total+1))) { + free(storage); + free(tempcomment); + return 0; + } + + /* convert message */ + for(line=tempcomment,t=0;tlength) SL_RegisterSample(s,MD_MUSIC,modreader); + + return 1; +} + +/* Creates a CSTR out of a character buffer of 'len' bytes, but strips any + terminating non-printing characters like 0, spaces etc. */ +CHAR *DupStr(CHAR* s,UWORD len,BOOL strict) +{ + UWORD t; + CHAR *d=NULL; + + /* Scan for last printing char in buffer [includes high ascii up to 254] */ + while(len) { + if(s[len-1]>0x20) break; + len--; + } + + /* Scan forward for possible NULL character */ + if(strict) { + for(t=0;thandle>=0) + MD_SampleUnload(s->handle); + if(s->samplename) free(s->samplename); +} + +static void ML_XFreeInstrument(INSTRUMENT *i) +{ + if(i->insname) free(i->insname); +} + +static void ML_FreeEx(MODULE *mf) +{ + UWORD t; + + if(mf->songname) free(mf->songname); + if(mf->comment) free(mf->comment); + + if(mf->modtype) free(mf->modtype); + if(mf->positions) free(mf->positions); + if(mf->patterns) free(mf->patterns); + if(mf->pattrows) free(mf->pattrows); + + if(mf->tracks) { + for(t=0;tnumtrk;t++) + if(mf->tracks[t]) free(mf->tracks[t]); + free(mf->tracks); + } + if(mf->instruments) { + for(t=0;tnumins;t++) + ML_XFreeInstrument(&mf->instruments[t]); + free(mf->instruments); + } + if(mf->samples) { + for(t=0;tnumsmp;t++) + if(mf->samples[t].length) ML_XFreeSample(&mf->samples[t]); + free(mf->samples); + } + memset(mf,0,sizeof(MODULE)); + if(mf!=&of) free(mf); +} + +static MODULE *ML_AllocUniMod(void) +{ + MODULE *mf; + + return (mf=_mm_malloc(sizeof(MODULE))); +} + +void Player_Free_internal(MODULE *mf) +{ + if(mf) { + Player_Exit_internal(mf); + ML_FreeEx(mf); + } +} + +void Player_Free(MODULE *mf) +{ + MUTEX_LOCK(vars); + Player_Free_internal(mf); + MUTEX_UNLOCK(vars); +} + +static CHAR* Player_LoadTitle_internal(MREADER *reader) +{ + MLOADER *l; + + modreader=reader; + _mm_errno = 0; + _mm_critical = 0; + _mm_iobase_setcur(modreader); + + /* Try to find a loader that recognizes the module */ + for(l=firstloader;l;l=l->next) { + _mm_rewind(modreader); + if(l->Test()) break; + } + + if(!l) { + _mm_errno = MMERR_NOT_A_MODULE; + if(_mm_errorhandler) _mm_errorhandler(); + return NULL; + } + + return l->LoadTitle(); +} + +CHAR* Player_LoadTitle(CHAR* filename) +{ + CHAR* result=NULL; + FILE* fp; + MREADER* reader; + + if((fp=_mm_fopen(filename,"rb"))) { + if((reader=_mm_new_file_reader(fp))) { + MUTEX_LOCK(lists); + result=Player_LoadTitle_internal(reader); + MUTEX_UNLOCK(lists); + _mm_delete_file_reader(reader); + } + fclose(fp); + } + return result; +} + +/* Loads a module given an reader */ +MODULE* Player_LoadGeneric_internal(MREADER *reader,int maxchan,BOOL curious) +{ + int t; + MLOADER *l; + BOOL ok; + MODULE *mf; + + modreader = reader; + _mm_errno = 0; + _mm_critical = 0; + _mm_iobase_setcur(modreader); + + /* Try to find a loader that recognizes the module */ + for(l=firstloader;l;l=l->next) { + _mm_rewind(modreader); + if(l->Test()) break; + } + + if(!l) { + _mm_errno = MMERR_NOT_A_MODULE; + if(_mm_errorhandler) _mm_errorhandler(); + _mm_rewind(modreader);_mm_iobase_revert(); + return NULL; + } + + /* init unitrk routines */ + if(!UniInit()) { + if(_mm_errorhandler) _mm_errorhandler(); + _mm_rewind(modreader);_mm_iobase_revert(); + return NULL; + } + + /* load the song using the song's loader variable */ + memset(&of,0,sizeof(MODULE)); + of.initvolume = 128; + + /* init panning array */ + for(t=0; t<64; t++) of.panning[t] = ((t+1)&2) ? 255 : 0; + for(t=0; t<64; t++) of.chanvol[t] = 64; + + /* init module loader and load the header / patterns */ + if(l->Init()) { + _mm_rewind(modreader); + ok = l->Load(curious); + } else + ok = 0; + + /* free loader and unitrk allocations */ + l->Cleanup(); + UniCleanup(); + + if(!ok) { + ML_FreeEx(&of); + if(_mm_errorhandler) _mm_errorhandler(); + _mm_rewind(modreader);_mm_iobase_revert(); + return NULL; + } + + if(!ML_LoadSamples()) { + ML_FreeEx(&of); + if(_mm_errorhandler) _mm_errorhandler(); + _mm_rewind(modreader);_mm_iobase_revert(); + return NULL; + } + + if(!(mf=ML_AllocUniMod())) { + ML_FreeEx(&of); + _mm_rewind(modreader);_mm_iobase_revert(); + if(_mm_errorhandler) _mm_errorhandler(); + return NULL; + } + + /* Copy the static MODULE contents into the dynamic MODULE struct. */ + memcpy(mf,&of,sizeof(MODULE)); + _mm_iobase_revert(); + + if(maxchan>0) { + if(!(mf->flags&UF_NNA)&&(mf->numchnnumchn; + else + if((mf->numvoices)&&(mf->numvoicesnumvoices; + + if(maxchannumchn) mf->flags |= UF_NNA; + + if(MikMod_SetNumVoices_internal(maxchan,-1)) { + Player_Free(mf); + return NULL; + } + } + if(SL_LoadSamples()) { + Player_Free_internal(mf); + return NULL; + } + if(Player_Init(mf)) { + Player_Free_internal(mf); + mf=NULL; + } + return mf; +} + +MODULE* Player_LoadGeneric(MREADER *reader,int maxchan,BOOL curious) +{ + MODULE* result; + + MUTEX_LOCK(vars); + MUTEX_LOCK(lists); + result=Player_LoadGeneric_internal(reader,maxchan,curious); + MUTEX_UNLOCK(lists); + MUTEX_UNLOCK(vars); + + return result; +} + +/* Loads a module given a file pointer. + File is loaded from the current file seek position. */ +MODULE* Player_LoadFP(FILE* fp,int maxchan,BOOL curious) +{ + MODULE* result=NULL; + struct MREADER* reader=_mm_new_file_reader (fp); + + if (reader) { + result=Player_LoadGeneric(reader,maxchan,curious); + _mm_delete_file_reader(reader); + } + return result; +} + +/* Open a module via its filename. The loader will initialize the specified + song-player 'player'. */ +MODULE* Player_Load(CHAR* filename,int maxchan,BOOL curious) +{ + FILE *fp; + MODULE *mf=NULL; + + if((fp=_mm_fopen(filename,"rb"))) { + mf=Player_LoadFP(fp,maxchan,curious); + fclose(fp); + } + return mf; +} + +/* SDL_RWops compatability */ +#ifdef USE_RWOPS + +/* Open a module via an SDL_rwop. The loader will initialize the specified + song-player 'player'. */ +MODULE* Player_LoadRW(SDL_RWops *rw,int maxchan,BOOL curious) +{ + MODULE* result=NULL; + struct MREADER* reader=_mm_new_rwops_reader (rw); + + if (reader) { + result=Player_LoadGeneric(reader,maxchan,curious); + _mm_delete_rwops_reader(reader); + } + return result; +} + +#endif /* USE_RWOPS */ +/* End SDL_RWops compatability */ + +/* ex:set ts=4: */ diff --git a/sc2/src/sc2code/libs/sound/decoders/mikmod/mlreg.c b/sc2/src/sc2code/libs/sound/decoders/mikmod/mlreg.c new file mode 100644 index 000000000..4a8cedd23 --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/mikmod/mlreg.c @@ -0,0 +1,50 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Routine for registering all loaders in libmikmod for the current platform. + +==============================================================================*/ + +#include "mikmod_internals.h" + +void MikMod_RegisterAllLoaders_internal(void) +{ + _mm_registerloader(&load_mod); +} + +void MikMod_RegisterAllLoaders(void) +{ + MUTEX_LOCK(lists); + MikMod_RegisterAllLoaders_internal(); + MUTEX_UNLOCK(lists); +} + +void MikMod_UnregisterAllLoaders(void) +{ + MUTEX_LOCK(lists); + _mm_unregisterloaders(); + MUTEX_UNLOCK(lists); +} + +/* ex:set ts=4: */ diff --git a/sc2/src/sc2code/libs/sound/decoders/mikmod/mlutil.c b/sc2/src/sc2code/libs/sound/decoders/mikmod/mlutil.c new file mode 100644 index 000000000..e2d629edc --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/mikmod/mlutil.c @@ -0,0 +1,301 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Utility functions for the module loader + +==============================================================================*/ + +#ifdef HAVE_MEMORY_H +#include +#endif + +#include +#include "mikmod_internals.h" + +SBYTE remap[64]; /* for removing empty channels */ +UBYTE* poslookup=NULL; /* lookup table for pattern jumps after blank + pattern removal */ +UBYTE poslookupcnt; +UWORD* origpositions=NULL; + +BOOL filters; /* resonant filters in use */ +UBYTE activemacro; /* active midi macro number for Sxx,xx<80h */ +UBYTE filtermacros[16]; /* midi macros settings */ +FILTER filtersettings[256]; /* computed filter settings */ + +/*========== Linear periods stuff */ + +int* noteindex=NULL; /* remap value for linear period modules */ +static int noteindexcount=0; + +int *AllocLinear(void) +{ + if(of.numsmp>noteindexcount) { + noteindexcount=of.numsmp; + noteindex=realloc(noteindex,noteindexcount*sizeof(int)); + } + return noteindex; +} + +void FreeLinear(void) +{ + if(noteindex) { + free(noteindex); + noteindex=NULL; + } + noteindexcount=0; +} + +int speed_to_finetune(ULONG speed,int sample) +{ + int ctmp=0,tmp,note=1,finetune=0; + + speed>>=1; + while((tmp=getfrequency(of.flags,getlinearperiod(note<<1,0)))speed) + tmp=getfrequency(of.flags,getlinearperiod(note<<1,--finetune)); + else { + note--; + while(ctmp>4; + + /* process S3M / IT specific command structure */ + + if(cmd!=255) { + switch(cmd) { + case 1: /* Axx set speed to xx */ + UniEffect(UNI_S3MEFFECTA,inf); + break; + case 2: /* Bxx position jump */ + if (inf>4)*10+(inf&0xf)); + else + UniPTEffect(0xd,inf); + break; + case 4: /* Dxy volumeslide */ + UniEffect(UNI_S3MEFFECTD,inf); + break; + case 5: /* Exy toneslide down */ + UniEffect(UNI_S3MEFFECTE,inf); + break; + case 6: /* Fxy toneslide up */ + UniEffect(UNI_S3MEFFECTF,inf); + break; + case 7: /* Gxx Tone portamento, speed xx */ + UniEffect(UNI_ITEFFECTG,inf); + break; + case 8: /* Hxy vibrato */ + if(oldeffect&1) + UniPTEffect(0x4,inf); + else + UniEffect(UNI_ITEFFECTH,inf); + break; + case 9: /* Ixy tremor, ontime x, offtime y */ + if(oldeffect&1) + UniEffect(UNI_S3MEFFECTI,inf); + else + UniEffect(UNI_ITEFFECTI,inf); + break; + case 0xa: /* Jxy arpeggio */ + UniPTEffect(0x0,inf); + break; + case 0xb: /* Kxy Dual command H00 & Dxy */ + if(oldeffect&1) + UniPTEffect(0x4,0); + else + UniEffect(UNI_ITEFFECTH,0); + UniEffect(UNI_S3MEFFECTD,inf); + break; + case 0xc: /* Lxy Dual command G00 & Dxy */ + if(oldeffect&1) + UniPTEffect(0x3,0); + else + UniEffect(UNI_ITEFFECTG,0); + UniEffect(UNI_S3MEFFECTD,inf); + break; + case 0xd: /* Mxx Set Channel Volume */ + UniEffect(UNI_ITEFFECTM,inf); + break; + case 0xe: /* Nxy Slide Channel Volume */ + UniEffect(UNI_ITEFFECTN,inf); + break; + case 0xf: /* Oxx set sampleoffset xx00h */ + UniPTEffect(0x9,inf); + break; + case 0x10: /* Pxy Slide Panning Commands */ + UniEffect(UNI_ITEFFECTP,inf); + break; + case 0x11: /* Qxy Retrig (+volumeslide) */ + UniWriteByte(UNI_S3MEFFECTQ); + if(inf && !lo && !(oldeffect&1)) + UniWriteByte(1); + else + UniWriteByte(inf); + break; + case 0x12: /* Rxy tremolo speed x, depth y */ + UniEffect(UNI_S3MEFFECTR,inf); + break; + case 0x13: /* Sxx special commands */ + if (inf>=0xf0) { + /* change resonant filter settings if necessary */ + if((filters)&&((inf&0xf)!=activemacro)) { + activemacro=inf&0xf; + for(inf=0;inf<0x80;inf++) + filtersettings[inf].filter=filtermacros[activemacro]; + } + } else + UniEffect(UNI_ITEFFECTS0,inf); + break; + case 0x14: /* Txx tempo */ + if(inf>=0x20) + UniEffect(UNI_S3MEFFECTT,inf); + else { + if(!(oldeffect&1)) + /* IT Tempo slide */ + UniEffect(UNI_ITEFFECTT,inf); + } + break; + case 0x15: /* Uxy Fine Vibrato speed x, depth y */ + if(oldeffect&1) + UniEffect(UNI_S3MEFFECTU,inf); + else + UniEffect(UNI_ITEFFECTU,inf); + break; + case 0x16: /* Vxx Set Global Volume */ + UniEffect(UNI_XMEFFECTG,inf); + break; + case 0x17: /* Wxy Global Volume Slide */ + UniEffect(UNI_ITEFFECTW,inf); + break; + case 0x18: /* Xxx amiga command 8xx */ + if(oldeffect&1) { + if(inf>128) + UniEffect(UNI_ITEFFECTS0,0x91); /* surround */ + else + UniPTEffect(0x8,(inf==128)?255:(inf<<1)); + } else + UniPTEffect(0x8,inf); + break; + case 0x19: /* Yxy Panbrello speed x, depth y */ + UniEffect(UNI_ITEFFECTY,inf); + break; + case 0x1a: /* Zxx midi/resonant filters */ + if(filtersettings[inf].filter) { + UniWriteByte(UNI_ITEFFECTZ); + UniWriteByte(filtersettings[inf].filter); + UniWriteByte(filtersettings[inf].inf); + } + break; + } + } +} + +/*========== Unitrk stuff */ + +/* Generic effect writing routine */ +void UniEffect(UWORD eff,UWORD dat) +{ + if((!eff)||(eff>=UNI_LAST)) return; + + UniWriteByte(eff); + if(unioperands[eff]==2) + UniWriteWord(dat); + else + UniWriteByte(dat); +} + +/* Appends UNI_PTEFFECTX opcode to the unitrk stream. */ +void UniPTEffect(UBYTE eff, UBYTE dat) +{ +#ifdef MIKMOD_DEBUG + if (eff>=0x10) + fprintf(stderr,"UniPTEffect called with incorrect eff value %d\n",eff); + else +#endif + if((eff)||(dat)) UniEffect(UNI_PTEFFECT0+eff,dat); +} + +/* Appends UNI_VOLEFFECT + effect/dat to unistream. */ +void UniVolEffect(UWORD eff,UBYTE dat) +{ + if((eff)||(dat)) { /* don't write empty effect */ + UniWriteByte(UNI_VOLEFFECTS); + UniWriteByte(eff);UniWriteByte(dat); + } +} + +/* ex:set ts=4: */ diff --git a/sc2/src/sc2code/libs/sound/decoders/mikmod/mmalloc.c b/sc2/src/sc2code/libs/sound/decoders/mikmod/mmalloc.c new file mode 100644 index 000000000..58bde663b --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/mikmod/mmalloc.c @@ -0,0 +1,55 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Dynamic memory routines + +==============================================================================*/ + +#include "mikmod_internals.h" + +/* Same as malloc, but sets error variable _mm_error when fails */ +void* _mm_malloc(size_t size) +{ + void *d; + + if(!(d=calloc(1,size))) { + _mm_errno = MMERR_OUT_OF_MEMORY; + if(_mm_errorhandler) _mm_errorhandler(); + } + return d; +} + +/* Same as calloc, but sets error variable _mm_error when fails */ +void* _mm_calloc(size_t nitems,size_t size) +{ + void *d; + + if(!(d=calloc(nitems,size))) { + _mm_errno = MMERR_OUT_OF_MEMORY; + if(_mm_errorhandler) _mm_errorhandler(); + } + return d; +} + +/* ex:set ts=4: */ diff --git a/sc2/src/sc2code/libs/sound/decoders/mikmod/mmerror.c b/sc2/src/sc2code/libs/sound/decoders/mikmod/mmerror.c new file mode 100644 index 000000000..8fdca6c16 --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/mikmod/mmerror.c @@ -0,0 +1,193 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Error handling functions. + Register an error handler with _mm_RegisterErrorHandler() and you're all set. + +==============================================================================*/ + +/* + + The global variables _mm_errno, and _mm_critical are set before the error + handler in called. See below for the values of these variables. + +*/ + +#include "mikmod_internals.h" + +CHAR *_mm_errmsg[MMERR_MAX+1] = +{ +/* No error */ + + "No error", + +/* Generic errors */ + + "Could not open requested file", + "Out of memory", + "Dynamic linking failed", + +/* Sample errors */ + + "Out of memory to load sample", + "Out of sample handles to load sample", + "Sample format not recognized", + +/* Module errors */ + + "Failure loading module pattern", + "Failure loading module track", + "Failure loading module header", + "Failure loading sampleinfo", + "Module format not recognized", + "Module sample format not recognized", + "Synthsounds not supported in MED files", + "Compressed sample is invalid", + +/* Driver errors: */ + + "Sound device not detected", + "Device number out of range", + "Software mixer failure", + "Could not open sound device", + "This driver supports 8 bit linear output only", + "This driver supports 16 bit linear output only", + "This driver supports stereo output only", + "This driver supports uLaw output (8 bit mono, 8 kHz) only", + "Unable to set non-blocking mode for audio device", + +/* AudioFile driver errors */ + + "Cannot find suitable AudioFile audio port", + +/* AIX driver errors */ + + "Configuration (init step) of audio device failed", + "Configuration (control step) of audio device failed", + "Configuration (start step) of audio device failed", + +/* ALSA driver errors */ + +/* EsounD driver errors */ + +/* Ultrasound driver errors */ + + "Ultrasound driver only works in 16 bit stereo 44 KHz", + "Ultrasound card could not be reset", + "Could not start Ultrasound timer", + +/* HP driver errors */ + + "Unable to select 16bit-linear sample format", + "Could not select requested sample-rate", + "Could not select requested number of channels", + "Unable to select audio output", + "Unable to get audio description", + "Could not set transmission buffer size", + +/* Open Sound System driver errors */ + + "Could not set fragment size", + "Could not set sample size", + "Could not set mono/stereo setting", + "Could not set sample rate", + +/* SGI driver errors */ + + "Unsupported sample rate", + "Hardware does not support 16 bit sound", + "Hardware does not support 8 bit sound", + "Hardware does not support stereo sound", + "Hardware does not support mono sound", + +/* Sun driver errors */ + + "Sound device initialization failed", + +/* OS/2 drivers errors */ + + "Could not set mixing parameters", + "Could not create playback semaphores", + "Could not create playback timer", + "Could not create playback thread", + +/* DirectSound driver errors */ + + "Could not set playback priority", + "Could not create playback buffers", + "Could not set playback format", + "Could not register callback", + "Could not register event", + "Could not create playback thread", + "Could not initialize playback thread", + +/* Windows Multimedia API driver errors */ + + "Invalid device handle", + "The resource is already allocated", + "Invalid device identifier", + "Unsupported output format", + "Unknown error", + +/* Macintosh driver errors */ + + "Unsupported sample rate", + "Could not start playback", + +/* Invalid error */ + + "Invalid error code" +}; + +char *MikMod_strerror(int code) +{ + if ((code<0)||(code>MMERR_MAX)) code=MMERR_MAX+1; + return _mm_errmsg[code]; +} + +/* User installed error callback */ +MikMod_handler_t _mm_errorhandler = NULL; +int _mm_errno = 0; +BOOL _mm_critical = 0; + +MikMod_handler_t _mm_registererrorhandler(MikMod_handler_t proc) +{ + MikMod_handler_t oldproc=_mm_errorhandler; + + _mm_errorhandler = proc; + return oldproc; +} + +MikMod_handler_t MikMod_RegisterErrorHandler(MikMod_handler_t proc) +{ + MikMod_handler_t result; + + MUTEX_LOCK(vars); + result=_mm_registererrorhandler(proc); + MUTEX_UNLOCK(vars); + + return result; +} + +/* ex:set ts=4: */ diff --git a/sc2/src/sc2code/libs/sound/decoders/mikmod/mmio.c b/sc2/src/sc2code/libs/sound/decoders/mikmod/mmio.c new file mode 100644 index 000000000..c0d406d9f --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/mikmod/mmio.c @@ -0,0 +1,437 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Portable file I/O routines + +==============================================================================*/ + +/* + + The way this module works: + + - _mm_fopen will call the errorhandler [see mmerror.c] in addition to + setting _mm_errno on exit. + - _mm_iobase is for internal use. It is used by Player_LoadFP to + ensure that it works properly with wad files. + - _mm_read_I_* and _mm_read_M_* differ : the first is for reading data + written by a little endian (intel) machine, and the second is for reading + big endian (Mac, RISC, Alpha) machine data. + - _mm_write functions work the same as the _mm_read functions. + - _mm_read_string is for reading binary strings. It is basically the same + as an fread of bytes. + +*/ + +/* FIXME + the _mm_iobase variable ought to be MREADER-specific. It will eventually + become a private field of the MREADER structure, but this will require a + soname version bump. + + In the meantime, the drawback is that if you use the xxx_LoadFP functions, + you can't have several MREADER objects with different iobase values. +*/ + +#include +#include + +#include "mikmod_internals.h" + +#define COPY_BUFSIZE 1024 + +static long _mm_iobase=0,temp_iobase=0; + + +/* + + This section is added to use SDL_rwops for IO + + -Matt Campbell (matt@campbellhome.dhs.org) April 2000 + +*/ + +#ifdef USE_RWOPS + +typedef struct MRWOPSREADER { + MREADER core; + SDL_RWops* rw; + int end; +} MRWOPSREADER; + +static BOOL _mm_RWopsReader_Eof(MREADER* reader) +{ + if ( ((MRWOPSREADER*)reader)->end == + SDL_RWtell(((MRWOPSREADER*)reader)->rw) ) return 1; + else return 0; +} + +static BOOL _mm_RWopsReader_Read(MREADER* reader,void* ptr,size_t size) +{ + return SDL_RWread(((MRWOPSREADER*)reader)->rw, ptr, size, 1); +} + +static int _mm_RWopsReader_Get(MREADER* reader) +{ + char buf; + if ( SDL_RWread(((MRWOPSREADER*)reader)->rw, &buf, 1, 1) != 1 ) return EOF; + else return (int)buf; +} + +static BOOL _mm_RWopsReader_Seek(MREADER* reader,long offset,int whence) +{ + return SDL_RWseek(((MRWOPSREADER*)reader)->rw, + (whence==SEEK_SET)?offset+_mm_iobase:offset,whence); +} + +static long _mm_RWopsReader_Tell(MREADER* reader) +{ + return SDL_RWtell(((MRWOPSREADER*)reader)->rw) - _mm_iobase; +} + +MREADER *_mm_new_rwops_reader(SDL_RWops * rw) +{ + int here; + MRWOPSREADER* reader=(MRWOPSREADER*)_mm_malloc(sizeof(MRWOPSREADER)); + if (reader) { + reader->core.Eof =&_mm_RWopsReader_Eof; + reader->core.Read=&_mm_RWopsReader_Read; + reader->core.Get =&_mm_RWopsReader_Get; + reader->core.Seek=&_mm_RWopsReader_Seek; + reader->core.Tell=&_mm_RWopsReader_Tell; + reader->rw=rw; + + /* RWops does not explicitly support an eof check, so we shall find + the end manually - this requires seek support for the RWop */ + here = SDL_RWtell(rw); + reader->end = SDL_RWseek(rw, 0, SEEK_END); + SDL_RWseek(rw, here, SEEK_SET); /* Move back */ + } + return (MREADER*)reader; +} + +void _mm_delete_rwops_reader (MREADER* reader) +{ + if(reader) free(reader); +} + +#endif /* USE_RWOPS */ + +/* + + End SDL_rwops section + +*/ + +FILE* _mm_fopen(CHAR* fname,CHAR* attrib) +{ + FILE *fp; + + if(!(fp=fopen(fname,attrib))) { + _mm_errno = MMERR_OPENING_FILE; + if(_mm_errorhandler) _mm_errorhandler(); + } + return fp; +} + +BOOL _mm_FileExists(CHAR* fname) +{ + FILE *fp; + + if(!(fp=fopen(fname,"r"))) return 0; + fclose(fp); + + return 1; +} + +/* Sets the current file-position as the new _mm_iobase */ +void _mm_iobase_setcur(MREADER* reader) +{ + temp_iobase=_mm_iobase; /* store old value in case of revert */ + _mm_iobase=reader->Tell(reader); +} + +/* Reverts to the last known _mm_iobase value. */ +void _mm_iobase_revert(void) +{ + _mm_iobase=temp_iobase; +} + +/*========== File Reader */ + +typedef struct MFILEREADER { + MREADER core; + FILE* file; +} MFILEREADER; + +static BOOL _mm_FileReader_Eof(MREADER* reader) +{ + return feof(((MFILEREADER*)reader)->file); +} + +static BOOL _mm_FileReader_Read(MREADER* reader,void* ptr,size_t size) +{ + return fread(ptr,size,1,((MFILEREADER*)reader)->file); +} + +static int _mm_FileReader_Get(MREADER* reader) +{ + return fgetc(((MFILEREADER*)reader)->file); +} + +static BOOL _mm_FileReader_Seek(MREADER* reader,long offset,int whence) +{ + return fseek(((MFILEREADER*)reader)->file, + (whence==SEEK_SET)?offset+_mm_iobase:offset,whence); +} + +static long _mm_FileReader_Tell(MREADER* reader) +{ + return ftell(((MFILEREADER*)reader)->file)-_mm_iobase; +} + +MREADER *_mm_new_file_reader(FILE* fp) +{ + MFILEREADER* reader=(MFILEREADER*)_mm_malloc(sizeof(MFILEREADER)); + if (reader) { + reader->core.Eof =&_mm_FileReader_Eof; + reader->core.Read=&_mm_FileReader_Read; + reader->core.Get =&_mm_FileReader_Get; + reader->core.Seek=&_mm_FileReader_Seek; + reader->core.Tell=&_mm_FileReader_Tell; + reader->file=fp; + } + return (MREADER*)reader; +} + +void _mm_delete_file_reader (MREADER* reader) +{ + if(reader) free(reader); +} + +/*========== File Writer */ + +typedef struct MFILEWRITER { + MWRITER core; + FILE* file; +} MFILEWRITER; + +static BOOL _mm_FileWriter_Seek(MWRITER* writer,long offset,int whence) +{ + return fseek(((MFILEWRITER*)writer)->file,offset,whence); +} + +static long _mm_FileWriter_Tell(MWRITER* writer) +{ + return ftell(((MFILEWRITER*)writer)->file); +} + +static BOOL _mm_FileWriter_Write(MWRITER* writer,void* ptr,size_t size) +{ + return (fwrite(ptr,size,1,((MFILEWRITER*)writer)->file)==size); +} + +static BOOL _mm_FileWriter_Put(MWRITER* writer,int value) +{ + return fputc(value,((MFILEWRITER*)writer)->file); +} + +MWRITER *_mm_new_file_writer(FILE* fp) +{ + MFILEWRITER* writer=(MFILEWRITER*)_mm_malloc(sizeof(MFILEWRITER)); + if (writer) { + writer->core.Seek =&_mm_FileWriter_Seek; + writer->core.Tell =&_mm_FileWriter_Tell; + writer->core.Write=&_mm_FileWriter_Write; + writer->core.Put =&_mm_FileWriter_Put; + writer->file=fp; + } + return (MWRITER*) writer; +} + +void _mm_delete_file_writer (MWRITER* writer) +{ + if(writer) free (writer); +} + +/*========== Write functions */ + +void _mm_write_string(CHAR* data,MWRITER* writer) +{ + if(data) + _mm_write_UBYTES(data,strlen(data),writer); +} + +void _mm_write_M_UWORD(UWORD data,MWRITER* writer) +{ + _mm_write_UBYTE(data>>8,writer); + _mm_write_UBYTE(data&0xff,writer); +} + +void _mm_write_I_UWORD(UWORD data,MWRITER* writer) +{ + _mm_write_UBYTE(data&0xff,writer); + _mm_write_UBYTE(data>>8,writer); +} + +void _mm_write_M_ULONG(ULONG data,MWRITER* writer) +{ + _mm_write_M_UWORD(data>>16,writer); + _mm_write_M_UWORD(data&0xffff,writer); +} + +void _mm_write_I_ULONG(ULONG data,MWRITER* writer) +{ + _mm_write_I_UWORD(data&0xffff,writer); + _mm_write_I_UWORD(data>>16,writer); +} + +void _mm_write_M_SWORD(SWORD data,MWRITER* writer) +{ + _mm_write_M_UWORD((UWORD)data,writer); +} + +void _mm_write_I_SWORD(SWORD data,MWRITER* writer) +{ + _mm_write_I_UWORD((UWORD)data,writer); +} + +void _mm_write_M_SLONG(SLONG data,MWRITER* writer) +{ + _mm_write_M_ULONG((ULONG)data,writer); +} + +void _mm_write_I_SLONG(SLONG data,MWRITER* writer) +{ + _mm_write_I_ULONG((ULONG)data,writer); +} + +#ifdef __STDC__ +#define DEFINE_MULTIPLE_WRITE_FUNCTION(type_name,type) \ +void _mm_write_##type_name##S (type *buffer,int number,MWRITER* writer) \ +{ \ + while(number-->0) \ + _mm_write_##type_name(*(buffer++),writer); \ +} +#else +#define DEFINE_MULTIPLE_WRITE_FUNCTION(type_name,type) \ +void _mm_write_/**/type_name/**/S (type *buffer,int number,MWRITER* writer) \ +{ \ + while(number-->0) \ + _mm_write_/**/type_name(*(buffer++),writer); \ +} +#endif + +DEFINE_MULTIPLE_WRITE_FUNCTION(M_SWORD,SWORD) +DEFINE_MULTIPLE_WRITE_FUNCTION(M_UWORD,UWORD) +DEFINE_MULTIPLE_WRITE_FUNCTION(I_SWORD,SWORD) +DEFINE_MULTIPLE_WRITE_FUNCTION(I_UWORD,UWORD) + +DEFINE_MULTIPLE_WRITE_FUNCTION(M_SLONG,SLONG) +DEFINE_MULTIPLE_WRITE_FUNCTION(M_ULONG,ULONG) +DEFINE_MULTIPLE_WRITE_FUNCTION(I_SLONG,SLONG) +DEFINE_MULTIPLE_WRITE_FUNCTION(I_ULONG,ULONG) + +/*========== Read functions */ + +int _mm_read_string(CHAR* buffer,int number,MREADER* reader) +{ + return reader->Read(reader,buffer,number); +} + +UWORD _mm_read_M_UWORD(MREADER* reader) +{ + UWORD result=((UWORD)_mm_read_UBYTE(reader))<<8; + result|=_mm_read_UBYTE(reader); + return result; +} + +UWORD _mm_read_I_UWORD(MREADER* reader) +{ + UWORD result=_mm_read_UBYTE(reader); + result|=((UWORD)_mm_read_UBYTE(reader))<<8; + return result; +} + +ULONG _mm_read_M_ULONG(MREADER* reader) +{ + ULONG result=((ULONG)_mm_read_M_UWORD(reader))<<16; + result|=_mm_read_M_UWORD(reader); + return result; +} + +ULONG _mm_read_I_ULONG(MREADER* reader) +{ + ULONG result=_mm_read_I_UWORD(reader); + result|=((ULONG)_mm_read_I_UWORD(reader))<<16; + return result; +} + +SWORD _mm_read_M_SWORD(MREADER* reader) +{ + return((SWORD)_mm_read_M_UWORD(reader)); +} + +SWORD _mm_read_I_SWORD(MREADER* reader) +{ + return((SWORD)_mm_read_I_UWORD(reader)); +} + +SLONG _mm_read_M_SLONG(MREADER* reader) +{ + return((SLONG)_mm_read_M_ULONG(reader)); +} + +SLONG _mm_read_I_SLONG(MREADER* reader) +{ + return((SLONG)_mm_read_I_ULONG(reader)); +} + +#ifdef __STDC__ +#define DEFINE_MULTIPLE_READ_FUNCTION(type_name,type) \ +int _mm_read_##type_name##S (type *buffer,int number,MREADER* reader) \ +{ \ + while(number-->0) \ + *(buffer++)=_mm_read_##type_name(reader); \ + return !reader->Eof(reader); \ +} +#else +#define DEFINE_MULTIPLE_READ_FUNCTION(type_name,type) \ +int _mm_read_/**/type_name/**/S (type *buffer,int number,MREADER* reader) \ +{ \ + while(number-->0) \ + *(buffer++)=_mm_read_/**/type_name(reader); \ + return !reader->Eof(reader); \ +} +#endif + +DEFINE_MULTIPLE_READ_FUNCTION(M_SWORD,SWORD) +DEFINE_MULTIPLE_READ_FUNCTION(M_UWORD,UWORD) +DEFINE_MULTIPLE_READ_FUNCTION(I_SWORD,SWORD) +DEFINE_MULTIPLE_READ_FUNCTION(I_UWORD,UWORD) + +DEFINE_MULTIPLE_READ_FUNCTION(M_SLONG,SLONG) +DEFINE_MULTIPLE_READ_FUNCTION(M_ULONG,ULONG) +DEFINE_MULTIPLE_READ_FUNCTION(I_SLONG,SLONG) +DEFINE_MULTIPLE_READ_FUNCTION(I_ULONG,ULONG) + +/* ex:set ts=4: */ diff --git a/sc2/src/sc2code/libs/sound/decoders/mikmod/mplayer.c b/sc2/src/sc2code/libs/sound/decoders/mikmod/mplayer.c new file mode 100644 index 000000000..ace82d314 --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/mikmod/mplayer.c @@ -0,0 +1,2911 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + The Protracker Player Driver + + The protracker driver supports all base Protracker 3.x commands and features. + +==============================================================================*/ + +#include +#include +#ifdef SUNOS +extern long int random(void); +#endif +#ifdef SRANDOM_IN_MATH_H +#include +#else +#include +#endif + +#include "mikmod_internals.h" + +/* Set forbid to 1 when you want to modify any of the pf->sngpos, pf->patpos etc + variables and clear it when you're done. This prevents getting strange + results due to intermediate interrupts. */ + + MODULE *pf=NULL; /* modfile being played */ +static SWORD mp_channel; /* channel we're working on */ +static MP_CONTROL *a; /* current AUDTMP we're working on */ +static int explicitslides; + +static UWORD oldperiods[OCTAVE*2]={ + 1712*16,1664*16,1616*16,1570*16,1524*16,1480*16, + 1438*16,1396*16,1356*16,1318*16,1280*16,1244*16, + 1208*16,1174*16,1140*16,1108*16,1076*16,1046*16, + 1016*16, 988*16, 960*16, 932*16, 906*16, 880*16 +}; + +static UBYTE VibratoTable[32]={ + 0, 24, 49, 74, 97,120,141,161,180,197,212,224,235,244,250,253, + 255,253,250,244,235,224,212,197,180,161,141,120, 97, 74, 49, 24 +}; + +static UBYTE avibtab[128]={ + 0, 1, 3, 4, 6, 7, 9,10,12,14,15,17,18,20,21,23, + 24,25,27,28,30,31,32,34,35,36,38,39,40,41,42,44, + 45,46,47,48,49,50,51,52,53,54,54,55,56,57,57,58, + 59,59,60,60,61,61,62,62,62,63,63,63,63,63,63,63, + 64,63,63,63,63,63,63,63,62,62,62,61,61,60,60,59, + 59,58,57,57,56,55,54,54,53,52,51,50,49,48,47,46, + 45,44,42,41,40,39,38,36,35,34,32,31,30,28,27,25, + 24,23,21,20,18,17,15,14,12,10, 9, 7, 6, 4, 3, 1 +}; + +/* Triton's linear periods to frequency translation table (for XM modules) */ +static ULONG lintab[768]={ + 535232,534749,534266,533784,533303,532822,532341,531861, + 531381,530902,530423,529944,529466,528988,528511,528034, + 527558,527082,526607,526131,525657,525183,524709,524236, + 523763,523290,522818,522346,521875,521404,520934,520464, + 519994,519525,519057,518588,518121,517653,517186,516720, + 516253,515788,515322,514858,514393,513929,513465,513002, + 512539,512077,511615,511154,510692,510232,509771,509312, + 508852,508393,507934,507476,507018,506561,506104,505647, + 505191,504735,504280,503825,503371,502917,502463,502010, + 501557,501104,500652,500201,499749,499298,498848,498398, + 497948,497499,497050,496602,496154,495706,495259,494812, + 494366,493920,493474,493029,492585,492140,491696,491253, + 490809,490367,489924,489482,489041,488600,488159,487718, + 487278,486839,486400,485961,485522,485084,484647,484210, + 483773,483336,482900,482465,482029,481595,481160,480726, + 480292,479859,479426,478994,478562,478130,477699,477268, + 476837,476407,475977,475548,475119,474690,474262,473834, + 473407,472979,472553,472126,471701,471275,470850,470425, + 470001,469577,469153,468730,468307,467884,467462,467041, + 466619,466198,465778,465358,464938,464518,464099,463681, + 463262,462844,462427,462010,461593,461177,460760,460345, + 459930,459515,459100,458686,458272,457859,457446,457033, + 456621,456209,455797,455386,454975,454565,454155,453745, + 453336,452927,452518,452110,451702,451294,450887,450481, + 450074,449668,449262,448857,448452,448048,447644,447240, + 446836,446433,446030,445628,445226,444824,444423,444022, + 443622,443221,442821,442422,442023,441624,441226,440828, + 440430,440033,439636,439239,438843,438447,438051,437656, + 437261,436867,436473,436079,435686,435293,434900,434508, + 434116,433724,433333,432942,432551,432161,431771,431382, + 430992,430604,430215,429827,429439,429052,428665,428278, + 427892,427506,427120,426735,426350,425965,425581,425197, + 424813,424430,424047,423665,423283,422901,422519,422138, + 421757,421377,420997,420617,420237,419858,419479,419101, + 418723,418345,417968,417591,417214,416838,416462,416086, + 415711,415336,414961,414586,414212,413839,413465,413092, + 412720,412347,411975,411604,411232,410862,410491,410121, + 409751,409381,409012,408643,408274,407906,407538,407170, + 406803,406436,406069,405703,405337,404971,404606,404241, + 403876,403512,403148,402784,402421,402058,401695,401333, + 400970,400609,400247,399886,399525,399165,398805,398445, + 398086,397727,397368,397009,396651,396293,395936,395579, + 395222,394865,394509,394153,393798,393442,393087,392733, + 392378,392024,391671,391317,390964,390612,390259,389907, + 389556,389204,388853,388502,388152,387802,387452,387102, + 386753,386404,386056,385707,385359,385012,384664,384317, + 383971,383624,383278,382932,382587,382242,381897,381552, + 381208,380864,380521,380177,379834,379492,379149,378807, + 378466,378124,377783,377442,377102,376762,376422,376082, + 375743,375404,375065,374727,374389,374051,373714,373377, + 373040,372703,372367,372031,371695,371360,371025,370690, + 370356,370022,369688,369355,369021,368688,368356,368023, + 367691,367360,367028,366697,366366,366036,365706,365376, + 365046,364717,364388,364059,363731,363403,363075,362747, + 362420,362093,361766,361440,361114,360788,360463,360137, + 359813,359488,359164,358840,358516,358193,357869,357547, + 357224,356902,356580,356258,355937,355616,355295,354974, + 354654,354334,354014,353695,353376,353057,352739,352420, + 352103,351785,351468,351150,350834,350517,350201,349885, + 349569,349254,348939,348624,348310,347995,347682,347368, + 347055,346741,346429,346116,345804,345492,345180,344869, + 344558,344247,343936,343626,343316,343006,342697,342388, + 342079,341770,341462,341154,340846,340539,340231,339924, + 339618,339311,339005,338700,338394,338089,337784,337479, + 337175,336870,336566,336263,335959,335656,335354,335051, + 334749,334447,334145,333844,333542,333242,332941,332641, + 332341,332041,331741,331442,331143,330844,330546,330247, + 329950,329652,329355,329057,328761,328464,328168,327872, + 327576,327280,326985,326690,326395,326101,325807,325513, + 325219,324926,324633,324340,324047,323755,323463,323171, + 322879,322588,322297,322006,321716,321426,321136,320846, + 320557,320267,319978,319690,319401,319113,318825,318538, + 318250,317963,317676,317390,317103,316817,316532,316246, + 315961,315676,315391,315106,314822,314538,314254,313971, + 313688,313405,313122,312839,312557,312275,311994,311712, + 311431,311150,310869,310589,310309,310029,309749,309470, + 309190,308911,308633,308354,308076,307798,307521,307243, + 306966,306689,306412,306136,305860,305584,305308,305033, + 304758,304483,304208,303934,303659,303385,303112,302838, + 302565,302292,302019,301747,301475,301203,300931,300660, + 300388,300117,299847,299576,299306,299036,298766,298497, + 298227,297958,297689,297421,297153,296884,296617,296349, + 296082,295815,295548,295281,295015,294749,294483,294217, + 293952,293686,293421,293157,292892,292628,292364,292100, + 291837,291574,291311,291048,290785,290523,290261,289999, + 289737,289476,289215,288954,288693,288433,288173,287913, + 287653,287393,287134,286875,286616,286358,286099,285841, + 285583,285326,285068,284811,284554,284298,284041,283785, + 283529,283273,283017,282762,282507,282252,281998,281743, + 281489,281235,280981,280728,280475,280222,279969,279716, + 279464,279212,278960,278708,278457,278206,277955,277704, + 277453,277203,276953,276703,276453,276204,275955,275706, + 275457,275209,274960,274712,274465,274217,273970,273722, + 273476,273229,272982,272736,272490,272244,271999,271753, + 271508,271263,271018,270774,270530,270286,270042,269798, + 269555,269312,269069,268826,268583,268341,268099,267857 +}; + +#define LOGFAC 2*16 +static UWORD logtab[104]={ + LOGFAC*907,LOGFAC*900,LOGFAC*894,LOGFAC*887, + LOGFAC*881,LOGFAC*875,LOGFAC*868,LOGFAC*862, + LOGFAC*856,LOGFAC*850,LOGFAC*844,LOGFAC*838, + LOGFAC*832,LOGFAC*826,LOGFAC*820,LOGFAC*814, + LOGFAC*808,LOGFAC*802,LOGFAC*796,LOGFAC*791, + LOGFAC*785,LOGFAC*779,LOGFAC*774,LOGFAC*768, + LOGFAC*762,LOGFAC*757,LOGFAC*752,LOGFAC*746, + LOGFAC*741,LOGFAC*736,LOGFAC*730,LOGFAC*725, + LOGFAC*720,LOGFAC*715,LOGFAC*709,LOGFAC*704, + LOGFAC*699,LOGFAC*694,LOGFAC*689,LOGFAC*684, + LOGFAC*678,LOGFAC*675,LOGFAC*670,LOGFAC*665, + LOGFAC*660,LOGFAC*655,LOGFAC*651,LOGFAC*646, + LOGFAC*640,LOGFAC*636,LOGFAC*632,LOGFAC*628, + LOGFAC*623,LOGFAC*619,LOGFAC*614,LOGFAC*610, + LOGFAC*604,LOGFAC*601,LOGFAC*597,LOGFAC*592, + LOGFAC*588,LOGFAC*584,LOGFAC*580,LOGFAC*575, + LOGFAC*570,LOGFAC*567,LOGFAC*563,LOGFAC*559, + LOGFAC*555,LOGFAC*551,LOGFAC*547,LOGFAC*543, + LOGFAC*538,LOGFAC*535,LOGFAC*532,LOGFAC*528, + LOGFAC*524,LOGFAC*520,LOGFAC*516,LOGFAC*513, + LOGFAC*508,LOGFAC*505,LOGFAC*502,LOGFAC*498, + LOGFAC*494,LOGFAC*491,LOGFAC*487,LOGFAC*484, + LOGFAC*480,LOGFAC*477,LOGFAC*474,LOGFAC*470, + LOGFAC*467,LOGFAC*463,LOGFAC*460,LOGFAC*457, + LOGFAC*453,LOGFAC*450,LOGFAC*447,LOGFAC*443, + LOGFAC*440,LOGFAC*437,LOGFAC*434,LOGFAC*431 +}; + +static SBYTE PanbrelloTable[256]={ + 0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 19, 20, 22, 23, + 24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59, + 59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60, + 59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, + 45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26, + 24, 23, 22, 20, 19, 17, 16, 14, 12, 11, 9, 8, 6, 5, 3, 2, + 0,- 2,- 3,- 5,- 6,- 8,- 9,-11,-12,-14,-16,-17,-19,-20,-22,-23, + -24,-26,-27,-29,-30,-32,-33,-34,-36,-37,-38,-39,-41,-42,-43,-44, + -45,-46,-47,-48,-49,-50,-51,-52,-53,-54,-55,-56,-56,-57,-58,-59, + -59,-60,-60,-61,-61,-62,-62,-62,-63,-63,-63,-64,-64,-64,-64,-64, + -64,-64,-64,-64,-64,-64,-63,-63,-63,-62,-62,-62,-61,-61,-60,-60, + -59,-59,-58,-57,-56,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46, + -45,-44,-43,-42,-41,-39,-38,-37,-36,-34,-33,-32,-30,-29,-27,-26, + -24,-23,-22,-20,-19,-17,-16,-14,-12,-11,- 9,- 8,- 6,- 5,- 3,- 2 +}; + +/* returns a random value between 0 and ceil-1, ceil must be a power of two */ +static int getrandom(int ceil) +{ +#ifdef HAVE_SRANDOM + return random()&(ceil-1); +#else + return (int)((rand()*ceil)/(RAND_MAX+1.0)); +#endif +} + +/* New Note Action Scoring System : + -------------------------------- +1) total-volume (fadevol, chanvol, volume) is the main scorer. +2) a looping sample is a bonus x2 +3) a foreground channel is a bonus x4 +4) an active envelope with keyoff is a handicap -x2 */ +static int MP_FindEmptyChannel(void) +{ + MP_VOICE *a; + ULONG t,k,tvol,pp; + + for (t=0;tvoice[t].kick==KICK_ABSENT)||(pf->voice[t].kick==KICK_ENV))&& + Voice_Stopped_internal(t)) + return t; + + tvol=0xffffffUL;t=0;a=pf->voice; + for (k=0;kkick==KICK_ABSENT)||(a->kick==KICK_ENV)) { + pp=a->totalvol<<((a->s->flags&SF_LOOP)?1:0); + if ((a->master)&&(a==a->master->slave)) + pp<<=2; + + if (pp8000*7) return -1; + return t; +} + +static SWORD Interpolate(SWORD p,SWORD p1,SWORD p2,SWORD v1,SWORD v2) +{ + if ((p1==p2)||(p==p1)) return v1; + return v1+((SLONG)((p-p1)*(v2-v1))/(p2-p1)); +} + +UWORD getlinearperiod(UWORD note,ULONG fine) +{ + UWORD t; + + t=(20L*OCTAVE+2-note)*32L-(fine>>1); + return t; +} + +static UWORD getlogperiod(UWORD note,ULONG fine) +{ + UWORD n,o; + UWORD p1,p2; + ULONG i; + + n=note%(2*OCTAVE); + o=note/(2*OCTAVE); + i=(n<<2)+(fine>>4); /* n*8 + fine/16 */ + + p1=logtab[i]; + p2=logtab[i+1]; + + return (Interpolate(fine>>4,0,15,p1,p2)>>o); +} + +static UWORD getoldperiod(UWORD note,ULONG speed) +{ + UWORD n,o; + + if (!speed) { +#ifdef MIKMOD_DEBUG + fprintf(stderr,"\rmplayer: getoldperiod() called with note=%d, speed=0 !\n",note); +#endif + return 4242; /* <- prevent divide overflow.. (42 hehe) */ + } + + n=note%(2*OCTAVE); + o=note/(2*OCTAVE); + return ((8363L*(ULONG)oldperiods[n])>>o)/speed; +} + +static UWORD GetPeriod(UWORD note,ULONG speed) +{ + if (pf->flags & UF_XMPERIODS) + return (pf->flags&UF_LINEAR)?getlinearperiod(note,speed):getlogperiod(note,speed); + + return getoldperiod(note,speed); +} + +static SWORD InterpolateEnv(SWORD p,ENVPT *a,ENVPT *b) +{ + return (Interpolate(p,a->pos,b->pos,a->val,b->val)); +} + +static SWORD DoPan(SWORD envpan,SWORD pan) +{ + int newpan; + + newpan=pan+(((envpan-PAN_CENTER)*(128-abs(pan-PAN_CENTER)))/128); + + return (newpanPAN_RIGHT?PAN_RIGHT:newpan); +} + +static void StartEnvelope(ENVPR *t,UBYTE flg,UBYTE pts,UBYTE susbeg,UBYTE susend,UBYTE beg,UBYTE end,ENVPT *p,UBYTE keyoff) +{ + t->flg=flg; + t->pts=pts; + t->susbeg=susbeg; + t->susend=susend; + t->beg=beg; + t->end=end; + t->env=p; + t->p=0; + t->a=0; + t->b=((t->flg&EF_SUSTAIN)&&(!(keyoff&KEY_OFF)))?0:1; + + /* Imago Orpheus sometimes stores an extra initial point in the envelope */ + if ((t->pts>=2)&&(t->env[0].pos==t->env[1].pos)) { + t->a++;t->b++; + } + + if (t->b>=t->pts) t->b=t->pts-1; +} + +/* This procedure processes all envelope types, include volume, pitch, and + panning. Envelopes are defined by a set of points, each with a magnitude + [relating either to volume, panning position, or pitch modifier] and a tick + position. + + Envelopes work in the following manner: + + (a) Each tick the envelope is moved a point further in its progression. For + an accurate progression, magnitudes between two envelope points are + interpolated. + + (b) When progression reaches a defined point on the envelope, values are + shifted to interpolate between this point and the next, and checks for + loops or envelope end are done. + + Misc: + Sustain loops are loops that are only active as long as the keyoff flag is + clear. When a volume envelope terminates, so does the current fadeout. */ +static SWORD ProcessEnvelope(ENVPR *t,SWORD v,UBYTE keyoff) +{ + if (t->flg & EF_ON) { + UBYTE a,b; /* actual points in the envelope */ + UWORD p; /* the 'tick counter' - real point being played */ + + a=t->a; + b=t->b; + p=t->p; + + /* if sustain loop on one point (XM type), don't move and don't + interpolate when the point is reached */ + if ((t->flg & EF_SUSTAIN)&&(t->susbeg==t->susend)&& + (!(keyoff&KEY_OFF))&&(p==t->env[t->susbeg].pos)) + v=t->env[t->susbeg].val; + else { + /* compute the current envelope value between points a and b */ + if (a==b) + v=t->env[a].val; + else + v=InterpolateEnv(p,&t->env[a],&t->env[b]); + + p++; + /* pointer reached point b? */ + if (p>=t->env[b].pos) { + a=b++; /* shift points a and b */ + + /* Check for loops, sustain loops, or end of envelope. */ + if ((t->flg&EF_SUSTAIN)&&(!(keyoff&KEY_OFF))&&(b>t->susend)) { + a=t->susbeg; + b=(t->susbeg==t->susend)?a:a+1; + p=t->env[a].pos; + } else + if ((t->flg & EF_LOOP)&&(b>t->end)) { + a=t->beg; + b=(t->beg==t->end)?a:a+1; + p=t->env[a].pos; + } else { + if (b>=t->pts) { + if ((t->flg & EF_VOLENV)&&(mp_channel!=-1)) { + pf->voice[mp_channel].keyoff|=KEY_FADE; + if (!v) + pf->voice[mp_channel].fadevol=0; + } + b--;p--; + } + } + } + t->a=a; + t->b=b; + t->p=p; + } + } + return v; +} + +/* XM linear period to frequency conversion */ +ULONG getfrequency(UBYTE flags,ULONG period) +{ + if (flags & UF_LINEAR) + return lintab[period%768]>>(period/768); + else + return (8363L*1712L)/(period?period:1); +} + +/*========== Protracker effects */ + +static void DoEEffects(UBYTE dat) +{ + UBYTE nib=dat&0xf; + + switch (dat>>4) { + case 0x0: /* hardware filter toggle, not supported */ + break; + case 0x1: /* fineslide up */ + if (a->period) + if (!pf->vbtick) a->tmpperiod-=(nib<<2); + break; + case 0x2: /* fineslide dn */ + if (a->period) + if (!pf->vbtick) a->tmpperiod+=(nib<<2); + break; + case 0x3: /* glissando ctrl */ + a->glissando=nib; + break; + case 0x4: /* set vibrato waveform */ + a->wavecontrol&=0xf0; + a->wavecontrol|=nib; + break; + case 0x5: /* set finetune */ + if (a->period) { + if (pf->flags&UF_XMPERIODS) + a->speed=nib+128; + else + a->speed=finetune[nib]; + a->tmpperiod=GetPeriod((UWORD)a->note<<1,a->speed); + } + break; + case 0x6: /* set patternloop */ + if (pf->vbtick) break; + if (nib) { /* set reppos or repcnt ? */ + /* set repcnt, so check if repcnt already is set, which means we + are already looping */ + if (a->pat_repcnt) + a->pat_repcnt--; /* already looping, decrease counter */ + else { +#if 0 + /* this would make walker.xm, shipped with Xsoundtracker, + play correctly, but it's better to remain compatible + with FT2 */ + if ((!(pf->flags&UF_NOWRAP))||(a->pat_reppos!=POS_NONE)) +#endif + a->pat_repcnt=nib; /* not yet looping, so set repcnt */ + } + + if (a->pat_repcnt) { /* jump to reppos if repcnt>0 */ + if (a->pat_reppos==POS_NONE) + a->pat_reppos=pf->patpos-1; + if (a->pat_reppos==-1) { + pf->pat_repcrazy=1; + pf->patpos=0; + } else + pf->patpos=a->pat_reppos; + } else a->pat_reppos=POS_NONE; + } else + a->pat_reppos=pf->patpos-1; /* set reppos - can be (-1) */ + break; + case 0x7: /* set tremolo waveform */ + a->wavecontrol&=0x0f; + a->wavecontrol|=nib<<4; + break; + case 0x8: /* set panning */ + if (pf->panflag) { + if (nib<=8) nib<<=4; + else nib*=17; + a->panning=pf->panning[mp_channel]=nib; + } + break; + case 0x9: /* retrig note */ + /* only retrigger if data nibble > 0 */ + if (nib) { + if (!a->retrig) { + /* when retrig counter reaches 0, reset counter and restart + the sample */ + if (a->period) a->kick=KICK_NOTE; + a->retrig=nib; + } + a->retrig--; /* countdown */ + } + break; + case 0xa: /* fine volume slide up */ + if (pf->vbtick) break; + a->tmpvolume+=nib; + if (a->tmpvolume>64) a->tmpvolume=64; + break; + case 0xb: /* fine volume slide dn */ + if (pf->vbtick) break; + a->tmpvolume-=nib; + if (a->tmpvolume<0) a->tmpvolume=0; + break; + case 0xc: /* cut note */ + /* When pf->vbtick reaches the cut-note value, turn the volume to + zero ( Just like on the amiga) */ + if (pf->vbtick>=nib) + a->tmpvolume=0; /* just turn the volume down */ + break; + case 0xd: /* note delay */ + /* delay the start of the sample until pf->vbtick==nib */ + if (!pf->vbtick) + a->notedelay=nib; + else if (a->notedelay) + a->notedelay--; + break; + case 0xe: /* pattern delay */ + if (pf->vbtick) break; + if (!pf->patdly2) pf->patdly=nib+1; /* only once, when vbtick=0 */ + break; + case 0xf: /* invert loop, not supported */ + break; + } +} + +static void DoVibrato(void) +{ + UBYTE q; + UWORD temp=0; + + q=(a->vibpos>>2)&0x1f; + + switch (a->wavecontrol&3) { + case 0: /* sine */ + temp=VibratoTable[q]; + break; + case 1: /* ramp down */ + q<<=3; + if (a->vibpos<0) q=255-q; + temp=q; + break; + case 2: /* square wave */ + temp=255; + break; + case 3: /* random wave */ + temp=getrandom(256); + break; + } + + temp*=a->vibdepth; + temp>>=7;temp<<=2; + + if (a->vibpos>=0) + a->period=a->tmpperiod+temp; + else + a->period=a->tmpperiod-temp; + + if (pf->vbtick) a->vibpos+=a->vibspd; +} + +static void DoTremolo(void) +{ + UBYTE q; + UWORD temp=0; + + q=(a->trmpos>>2)&0x1f; + + switch ((a->wavecontrol>>4)&3) { + case 0: /* sine */ + temp=VibratoTable[q]; + break; + case 1: /* ramp down */ + q<<=3; + if (a->trmpos<0) q=255-q; + temp=q; + break; + case 2: /* square wave */ + temp=255; + break; + case 3: /* random wave */ + temp=getrandom(256); + break; + } + temp*=a->trmdepth; + temp>>=6; + + if (a->trmpos>=0) { + a->volume=a->tmpvolume+temp; + if (a->volume>64) a->volume=64; + } else { + a->volume=a->tmpvolume-temp; + if (a->volume<0) a->volume=0; + } + + if (pf->vbtick) a->trmpos+=a->trmspd; +} + +static void DoVolSlide(UBYTE dat) +{ + if (!pf->vbtick) return; + + if (dat&0xf) { + a->tmpvolume-=(dat&0x0f); + if (a->tmpvolume<0) a->tmpvolume=0; + } else { + a->tmpvolume+=(dat>>4); + if (a->tmpvolume>64) a->tmpvolume=64; + } +} + +static void DoToneSlide(void) +{ + if (pf->vbtick) { + int dist; + + /* We have to slide a->period towards a->wantedperiod, so compute the + difference between those two values */ + dist=a->period-a->wantedperiod; + + /* if they are equal or if portamentospeed is too big ...*/ + if ((!dist)||a->portspeed>abs(dist)) + /* ...make tmpperiod equal tperiod */ + a->tmpperiod=a->period=a->wantedperiod; + else if (dist>0) { + a->tmpperiod-=a->portspeed; + a->period-=a->portspeed; /* dist>0, slide up */ + } else { + a->tmpperiod+=a->portspeed; + a->period+=a->portspeed; /* dist<0, slide down */ + } + } else + a->tmpperiod=a->period; +} + +static void DoArpeggio(UBYTE dat) +{ + UBYTE note=a->note; + + if (dat) { + switch (pf->vbtick%3) { + case 1: + note+=(dat>>4); break; + case 2: + note+=(dat&0xf); break; + } + a->period=GetPeriod((UWORD)note<<1,a->speed); + a->ownper=1; + } +} + +/*========== Scream Tracker effects */ + +static void DoS3MVolSlide(UBYTE inf) +{ + UBYTE lo, hi; + + explicitslides=1; + + if (inf) a->s3mvolslide=inf; + else inf=a->s3mvolslide; + + lo=inf&0xf; + hi=inf>>4; + + if (!lo) { + if ((pf->vbtick)||(pf->flags&UF_S3MSLIDES)) a->tmpvolume+=hi; + } else + if (!hi) { + if ((pf->vbtick)||(pf->flags&UF_S3MSLIDES)) a->tmpvolume-=lo; + } else + if (lo==0xf) { + if (!pf->vbtick) a->tmpvolume+=(hi?hi:0xf); + } else + if (hi==0xf) { + if (!pf->vbtick) a->tmpvolume-=(lo?lo:0xf); + } else + return; + + if (a->tmpvolume<0) a->tmpvolume=0; + else if (a->tmpvolume>64) a->tmpvolume=64; +} + +static void DoS3MSlideDn(UBYTE inf) +{ + UBYTE hi,lo; + + if (inf) a->slidespeed=inf; + else inf=a->slidespeed; + + hi=inf>>4; + lo=inf&0xf; + + if (hi==0xf) { + if (!pf->vbtick) a->tmpperiod+=(UWORD)lo<<2; + } else + if (hi==0xe) { + if (!pf->vbtick) a->tmpperiod+=lo; + } else { + if (pf->vbtick) a->tmpperiod+=(UWORD)inf<<2; + } +} + +static void DoS3MSlideUp(UBYTE inf) +{ + UBYTE hi,lo; + + if (inf) a->slidespeed=inf; + else inf=a->slidespeed; + + hi=inf>>4; + lo=inf&0xf; + + if (hi==0xf) { + if (!pf->vbtick) a->tmpperiod-=(UWORD)lo<<2; + } else + if (hi==0xe) { + if (!pf->vbtick) a->tmpperiod-=lo; + } else { + if (pf->vbtick) a->tmpperiod-=(UWORD)inf<<2; + } +} + +static void DoS3MTremor(UBYTE inf) +{ + UBYTE on,off; + + if (inf) + a->s3mtronof=inf; + else { + inf=a->s3mtronof; + if (!inf) return; + } + + if (!pf->vbtick) return; + + on=(inf>>4)+1; + off=(inf&0xf)+1; + a->s3mtremor%=(on+off); + a->volume=(a->s3mtremortmpvolume:0; + a->s3mtremor++; +} + +static void DoS3MRetrig(UBYTE inf) +{ + if (inf) { + a->s3mrtgslide=inf>>4; + a->s3mrtgspeed=inf&0xf; + } + + /* only retrigger if low nibble > 0 */ + if ( a->s3mrtgspeed>0) { + if (!a->retrig) { + /* when retrig counter reaches 0, reset counter and restart the + sample */ + if (a->kick!=KICK_NOTE) a->kick=KICK_KEYOFF; + a->retrig=a->s3mrtgspeed; + + if ((pf->vbtick)||(pf->flags&UF_S3MSLIDES)) { + switch (a->s3mrtgslide) { + case 1: + case 2: + case 3: + case 4: + case 5: + a->tmpvolume-=(1<<(a->s3mrtgslide-1)); + break; + case 6: + a->tmpvolume=(2*a->tmpvolume)/3; + break; + case 7: + a->tmpvolume>>=1; + break; + case 9: + case 0xa: + case 0xb: + case 0xc: + case 0xd: + a->tmpvolume+=(1<<(a->s3mrtgslide-9)); + break; + case 0xe: + a->tmpvolume=(3*a->tmpvolume)>>1; + break; + case 0xf: + a->tmpvolume=a->tmpvolume<<1; + break; + } + if (a->tmpvolume<0) a->tmpvolume=0; + else if (a->tmpvolume>64) a->tmpvolume=64; + } + } + a->retrig--; /* countdown */ + } +} + +static void DoS3MSpeed(UBYTE speed) +{ + if (pf->vbtick||pf->patdly2) return; + + if (speed>128) speed-=128; + if (speed) { + pf->sngspd=speed; + pf->vbtick=0; + } +} + +static void DoS3MTempo(UBYTE tempo) +{ + if (pf->vbtick||pf->patdly2) return; + + pf->bpm=(tempo<32)?32:tempo; +} + +static void DoS3MFineVibrato(void) +{ + UBYTE q; + UWORD temp=0; + + q=(a->vibpos>>2)&0x1f; + + switch (a->wavecontrol&3) { + case 0: /* sine */ + temp=VibratoTable[q]; + break; + case 1: /* ramp down */ + q<<=3; + if (a->vibpos<0) q=255-q; + temp=q; + break; + case 2: /* square wave */ + temp=255; + break; + case 3: /* random */ + temp=getrandom(256); + break; + } + + temp*=a->vibdepth; + temp>>=8; + + if (a->vibpos>=0) + a->period=a->tmpperiod+temp; + else + a->period=a->tmpperiod-temp; + + a->vibpos+=a->vibspd; +} + +static void DoS3MTremolo(void) +{ + UBYTE q; + UWORD temp=0; + + q=(a->trmpos>>2)&0x1f; + + switch ((a->wavecontrol>>4)&3) { + case 0: /* sine */ + temp=VibratoTable[q]; + break; + case 1: /* ramp down */ + q<<=3; + if (a->trmpos<0) q=255-q; + temp=q; + break; + case 2: /* square wave */ + temp=255; + break; + case 3: /* random */ + temp=getrandom(256); + break; + } + + temp*=a->trmdepth; + temp>>=7; + + if (a->trmpos>=0) { + a->volume=a->tmpvolume+temp; + if (a->volume>64) a->volume=64; + } else { + a->volume=a->tmpvolume-temp; + if (a->volume<0) a->volume=0; + } + + if (pf->vbtick) a->trmpos+=a->trmspd; +} + +/*========== Fast Tracker effects */ + +static void DoXMVolSlide(UBYTE inf) +{ + UBYTE lo,hi; + + explicitslides=2; + + if (inf) a->s3mvolslide=inf; + else inf=a->s3mvolslide; + + if (!pf->vbtick) return; + + lo=inf&0xf; + hi=inf>>4; + + if (!hi) { + a->tmpvolume-=lo; + if (a->tmpvolume<0) a->tmpvolume=0; + } else { + a->tmpvolume+=hi; + if (a->tmpvolume>64) a->tmpvolume=64; + } +} + +static void DoXMGlobalSlide(UBYTE inf) +{ + if (pf->vbtick) { + if (inf) pf->globalslide=inf; + else inf=pf->globalslide; + if (inf & 0xf0) inf&=0xf0; + pf->volume=pf->volume+((inf>>4)-(inf&0xf))*2; + + if (pf->volume<0) pf->volume=0; + else if (pf->volume>128) pf->volume=128; + } +} + +static void DoXMPanSlide(UBYTE inf) +{ + UBYTE lo,hi; + SWORD pan; + + if (inf) a->pansspd=inf; + else inf=a->pansspd; + + if (!pf->vbtick) return; + + lo=inf&0xf; + hi=inf>>4; + + /* slide right has absolute priority */ + if (hi) lo=0; + + pan=((a->panning==PAN_SURROUND)?PAN_CENTER:a->panning)+hi-lo; + + a->panning=(panPAN_RIGHT?PAN_RIGHT:pan); +} + +static void DoXMExtraFineSlideUp(UBYTE inf) +{ + if (!pf->vbtick) { + a->period-=inf; + a->tmpperiod-=inf; + } +} + +static void DoXMExtraFineSlideDown(UBYTE inf) +{ + if (!pf->vbtick) { + a->period+=inf; + a->tmpperiod+=inf; + } +} + +/*========== Impulse Tracker effects */ + +static void DoITChanVolSlide(UBYTE inf) +{ + UBYTE lo, hi; + + if (inf) a->chanvolslide=inf; + inf=a->chanvolslide; + + lo=inf&0xf; + hi=inf>>4; + + if (!hi) + a->chanvol-=lo; + else + if (!lo) { + a->chanvol+=hi; + } else + if (hi==0xf) { + if (!pf->vbtick) a->chanvol-=lo; + } else + if (lo==0xf) { + if (!pf->vbtick) a->chanvol+=hi; + } + + if (a->chanvol<0) a->chanvol=0; + if (a->chanvol>64) a->chanvol=64; +} + +static void DoITGlobalSlide(UBYTE inf) +{ + UBYTE lo,hi; + + if (inf) pf->globalslide=inf; + inf=pf->globalslide; + + lo=inf&0xf; + hi=inf>>4; + + if (!lo) { + if (pf->vbtick) pf->volume+=hi; + } else + if (!hi) { + if (pf->vbtick) pf->volume-=lo; + } else + if (lo==0xf) { + if (!pf->vbtick) pf->volume+=hi; + } else + if (hi==0xf) { + if (!pf->vbtick) pf->volume-=lo; + } + + if (pf->volume<0) pf->volume=0; + if (pf->volume>128) pf->volume=128; +} + +static void DoITPanSlide(UBYTE inf) +{ + UBYTE lo,hi; + SWORD pan; + + if (inf) a->pansspd=inf; + else inf=a->pansspd; + + lo=inf&0xf; + hi=inf>>4; + + pan=(a->panning==PAN_SURROUND)?PAN_CENTER:a->panning; + + if (!hi) + pan+=lo<<2; + else + if (!lo) { + pan-=hi<<2; + } else + if (hi==0xf) { + if (!pf->vbtick) pan+=lo<<2; + } else + if (lo==0xf) { + if (!pf->vbtick) pan-=hi<<2; + } + a->panning=/*pf->panning[mp_channel]=*/ + (panPAN_RIGHT?PAN_RIGHT:pan); +} + +static void DoITTempo(UBYTE tempo) +{ + SWORD temp=pf->bpm; + + if (pf->vbtick||pf->patdly2) return; + + if (tempo&0x10) + temp+=(tempo&0x0f); + else + temp-=tempo; + + pf->bpm=(temp>255)?255:(temp<1?1:temp); +} + +static void DoITVibrato(void) +{ + UBYTE q; + UWORD temp=0; + + q=(a->vibpos>>2)&0x1f; + + switch (a->wavecontrol&3) { + case 0: /* sine */ + temp=VibratoTable[q]; + break; + case 1: /* square wave */ + temp=255; + break; + case 2: /* ramp down */ + q<<=3; + if (a->vibpos<0) q=255-q; + temp=q; + break; + case 3: /* random */ + temp=getrandom(256); + break; + } + + temp*=a->vibdepth; + temp>>=8; + temp<<=2; + + if (a->vibpos>=0) + a->period=a->tmpperiod+temp; + else + a->period=a->tmpperiod-temp; + + a->vibpos+=a->vibspd; +} + +static void DoITFineVibrato(void) +{ + UBYTE q; + UWORD temp=0; + + q=(a->vibpos>>2)&0x1f; + + switch (a->wavecontrol&3) { + case 0: /* sine */ + temp=VibratoTable[q]; + break; + case 1: /* square wave */ + temp=255; + break; + case 2: /* ramp down */ + q<<=3; + if (a->vibpos<0) q=255-q; + temp=q; + break; + case 3: /* random */ + temp=getrandom(256); + break; + } + + temp*=a->vibdepth; + temp>>=8; + + if (a->vibpos>=0) + a->period=a->tmpperiod+temp; + else + a->period=a->tmpperiod-temp; + + a->vibpos+=a->vibspd; +} + +static void DoITTremor(UBYTE inf) +{ + UBYTE on,off; + + if (inf) + a->s3mtronof=inf; + else { + inf=a->s3mtronof; + if (!inf) return; + } + + if (!pf->vbtick) return; + + on=(inf>>4); + off=(inf&0xf); + + a->s3mtremor%=(on+off); + a->volume=(a->s3mtremortmpvolume:0; + a->s3mtremor++; +} + +static void DoITPanbrello(void) +{ + UBYTE q; + SLONG temp=0; + + q=a->panbpos; + + switch (a->panbwave) { + case 0: /* sine */ + temp=PanbrelloTable[q]; + break; + case 1: /* square wave */ + temp=(q<0x80)?64:0; + break; + case 2: /* ramp down */ + q<<=3; + temp=q; + break; + case 3: /* random */ + if (a->panbpos>=a->panbspd) { + a->panbpos=0; + temp=getrandom(256); + } + } + + temp*=a->panbdepth; + temp=(temp/8)+pf->panning[mp_channel]; + + a->panning=(tempPAN_RIGHT?PAN_RIGHT:temp); + a->panbpos+=a->panbspd; +} + +static void DoITToneSlide(void) +{ + /* if we don't come from another note, ignore the slide and play the note + as is */ + if (!a->oldnote) return; + + if (pf->vbtick) { + int dist; + + /* We have to slide a->period towards a->wantedperiod, compute the + difference between those two values */ + dist=a->period-a->wantedperiod; + + /* if they are equal or if portamentospeed is too big... */ + if ((!dist)||((a->portspeed<<2)>abs(dist))) + /* ... make tmpperiod equal tperiod */ + a->tmpperiod=a->period=a->wantedperiod; + else + if (dist>0) { + a->tmpperiod-=a->portspeed<<2; + a->period-=a->portspeed<<2; /* dist>0 slide up */ + } else { + a->tmpperiod+=a->portspeed<<2; + a->period+=a->portspeed<<2; /* dist<0 slide down */ + } + } else + a->tmpperiod=a->period; +} + +static void DoNNAEffects(UBYTE dat); +/* Impulse/Scream Tracker Sxx effects. + All Sxx effects share the same memory space. */ +static void DoSSEffects(UBYTE dat) +{ + UBYTE inf,c; + + inf=dat&0xf; + c=dat>>4; + + if (!dat) { + c=a->sseffect; + inf=a->ssdata; + } else { + a->sseffect=c; + a->ssdata=inf; + } + + switch (c) { + case SS_GLISSANDO: /* S1x set glissando voice */ + DoEEffects(0x30|inf); + break; + case SS_FINETUNE: /* S2x set finetune */ + DoEEffects(0x50|inf); + break; + case SS_VIBWAVE: /* S3x set vibrato waveform */ + DoEEffects(0x40|inf); + break; + case SS_TREMWAVE: /* S4x set tremolo waveform */ + DoEEffects(0x70|inf); + break; + case SS_PANWAVE: /* S5x panbrello */ + a->panbwave=inf; + break; + case SS_FRAMEDELAY: /* S6x delay x number of frames (patdly) */ + DoEEffects(0xe0|inf); + break; + case SS_S7EFFECTS: /* S7x instrument / NNA commands */ + DoNNAEffects(inf); + break; + case SS_PANNING: /* S8x set panning position */ + DoEEffects(0x80 | inf); + break; + case SS_SURROUND: /* S9x set surround Sound */ + if (pf->panflag) + a->panning=pf->panning[mp_channel]=PAN_SURROUND; + break; + case SS_HIOFFSET: /* SAy set high order sample offset yxx00h */ + if (!pf->vbtick) { + a->hioffset=inf<<16; + a->start=a->hioffset|a->soffset; + + if ((a->s)&&(a->start>a->s->length)) + a->start=a->s->flags&(SF_LOOP|SF_BIDI)?a->s->loopstart:a->s->length; + } + break; + case SS_PATLOOP: /* SBx pattern loop */ + DoEEffects(0x60|inf); + break; + case SS_NOTECUT: /* SCx notecut */ + DoEEffects(0xC0|inf); + break; + case SS_NOTEDELAY: /* SDx notedelay */ + DoEEffects(0xD0|inf); + break; + case SS_PATDELAY: /* SEx patterndelay */ + DoEEffects(0xE0|inf); + break; + } +} + +/* Impulse Tracker Volume/Pan Column effects. + All volume/pan column effects share the same memory space. */ +static void DoVolEffects(UBYTE c) +{ + UBYTE inf=UniGetByte(); + + if ((!c)&&(!inf)) { + c=a->voleffect; + inf=a->voldata; + } else { + a->voleffect=c; + a->voldata=inf; + } + + if (c) + switch (c) { + case VOL_VOLUME: + if (pf->vbtick) break; + if (inf>64) inf=64; + a->tmpvolume=inf; + break; + case VOL_PANNING: + if (pf->panflag) + a->panning=/*pf->panning[mp_channel]=*/inf; + break; + case VOL_VOLSLIDE: + DoS3MVolSlide(inf); + break; + case VOL_PITCHSLIDEDN: + if (a->period) + DoS3MSlideDn(inf); + break; + case VOL_PITCHSLIDEUP: + if (a->period) + DoS3MSlideUp(inf); + break; + case VOL_PORTAMENTO: + if (inf) a->slidespeed=inf; + if (a->period) { + if ((!pf->vbtick)||(a->newsamp)){ + a->kick=KICK_NOTE; + a->start=-1; + } else + a->kick=(a->kick==KICK_NOTE)?KICK_ENV:KICK_ABSENT; + DoITToneSlide(); + a->ownper=1; + } + break; + case VOL_VIBRATO: + if (!pf->vbtick) { + if (inf&0x0f) a->vibdepth=inf&0xf; + if (inf&0xf0) a->vibspd=(inf&0xf0)>>2; + } + if (a->period) { + DoITVibrato(); + a->ownper=1; + } + break; + } +} + +/*========== UltraTracker effects */ + +static void DoULTSampleOffset(void) +{ + UWORD offset=UniGetWord(); + + if (offset) + a->ultoffset=offset; + + a->start=a->ultoffset<<2; + if ((a->s)&&(a->start>a->s->length)) + a->start=a->s->flags&(SF_LOOP|SF_BIDI)?a->s->loopstart:a->s->length; +} + +/*========== OctaMED effects */ + +static void DoMEDSpeed(void) +{ + UWORD speed=UniGetWord(); + + pf->bpm=speed; +} + +/*========== General player functions */ + +static void pt_playeffects(void) +{ + UBYTE dat,c; + + while((c=UniGetByte())) { + int oldsliding=a->sliding; + + a->sliding=0; + switch (c) { + case UNI_NOTE: + case UNI_INSTRUMENT: + a->sliding=oldsliding; + UniSkipOpcode(c); + break; + case UNI_PTEFFECT0: + dat=UniGetByte(); + if (!pf->vbtick) { + if ((!dat)&&(pf->flags&UF_ARPMEM)) dat=a->arpmem; + a->arpmem=dat; + } + if (a->period) + DoArpeggio(a->arpmem); + break; + case UNI_PTEFFECT1: + dat=UniGetByte(); + if ((!pf->vbtick)&&(dat)) a->slidespeed=(UWORD)dat<<2; + if (a->period) + if (pf->vbtick) a->tmpperiod-=a->slidespeed; + break; + case UNI_PTEFFECT2: + dat=UniGetByte(); + if ((!pf->vbtick)&&(dat)) a->slidespeed=(UWORD)dat<<2; + if (a->period) + if (pf->vbtick) a->tmpperiod+=a->slidespeed; + break; + case UNI_PTEFFECT3: + dat=UniGetByte(); + if ((!pf->vbtick)&&(dat)) a->portspeed=(UWORD)dat<<2; + if (a->period) { + if (!a->fadevol) + a->kick=(a->kick==KICK_NOTE)?KICK_NOTE:KICK_KEYOFF; + else + a->kick=(a->kick==KICK_NOTE)?KICK_ENV:KICK_ABSENT; + DoToneSlide(); + a->ownper=1; + } + break; + case UNI_PTEFFECT4: + case UNI_XMEFFECT4: + dat=UniGetByte(); + if (!pf->vbtick) { + if (dat&0x0f) a->vibdepth=dat&0xf; + if (dat&0xf0) a->vibspd=(dat&0xf0)>>2; + } else + if (a->period) { + DoVibrato(); + a->ownper=1; + } + break; + case UNI_PTEFFECT5: + dat=UniGetByte(); + if (a->period) { + if (!a->fadevol) + a->kick=(a->kick==KICK_NOTE)?KICK_NOTE:KICK_KEYOFF; + else + a->kick=(a->kick==KICK_NOTE)?KICK_ENV:KICK_ABSENT; + DoToneSlide(); + a->ownper=1; + } + DoVolSlide(dat); + break; + case UNI_PTEFFECT6: + dat=UniGetByte(); + if ((a->period)&&(pf->vbtick)) { + DoVibrato(); + a->ownper=1; + } + DoVolSlide(dat); + break; + case UNI_PTEFFECT7: + dat=UniGetByte(); + if (!pf->vbtick) { + if (dat&0x0f) a->trmdepth=dat&0xf; + if (dat&0xf0) a->trmspd=(dat&0xf0)>>2; + } + if (a->period) { + DoTremolo(); + a->ownvol=1; + } + break; + case UNI_PTEFFECT8: + dat=UniGetByte(); + if (pf->panflag) + a->panning=pf->panning[mp_channel]=dat; + break; + case UNI_PTEFFECT9: + dat=UniGetByte(); + if (!pf->vbtick) { + if (dat) a->soffset=(UWORD)dat<<8; + a->start=a->hioffset|a->soffset; + + if ((a->s)&&(a->start>a->s->length)) + a->start=a->s->flags&(SF_LOOP|SF_BIDI)?a->s->loopstart:a->s->length; + } + break; + case UNI_PTEFFECTA: + DoVolSlide(UniGetByte()); + break; + case UNI_PTEFFECTB: + dat=UniGetByte(); + if ((pf->vbtick)||(pf->patdly2)) break; + /* Vincent Voois uses a nasty trick in "Universal Bolero" */ + if (dat==pf->sngpos && pf->patbrk==pf->patpos) break; + if ((!pf->loop)&&(!pf->patbrk)&&((datsngpos)|| + ((pf->sngpos==pf->numpos-1)&&(!pf->patbrk))|| + ((dat==pf->sngpos)&&(pf->flags&UF_NOWRAP)))) { + /* if we don't loop, better not to skip the end of the + pattern, after all... so: + pf->patbrk=0; */ + pf->posjmp=3; + } else { + /* if we were fading, adjust... */ + if (pf->sngpos==(pf->numpos-1)) + pf->volume=pf->initvolume>128?128:pf->initvolume; + pf->sngpos=dat; + pf->posjmp=2; + pf->patpos=0; + } + break; + case UNI_PTEFFECTC: + dat=UniGetByte(); + if (pf->vbtick) break; + if (dat==(UBYTE)-1) a->anote=dat=0; /* note cut */ + else if (dat>64) dat=64; + a->tmpvolume=dat; + break; + case UNI_PTEFFECTD: + dat=UniGetByte(); + if ((pf->vbtick)||(pf->patdly2)) break; + if ((pf->positions[pf->sngpos]!=255)&& + (dat>pf->pattrows[pf->positions[pf->sngpos]])) + dat=pf->pattrows[pf->positions[pf->sngpos]]; + pf->patbrk=dat; + if (!pf->posjmp) { + /* don't ask me to explain this code - it makes + backwards.s3m and children.xm (heretic's version) play + correctly, among others. Take that for granted, or write + the page of comments yourself... you might need some + aspirin - Miod */ + if ((pf->sngpos==pf->numpos-1)&&(dat)&&((pf->loop)|| + (pf->positions[pf->sngpos]==(pf->numpat-1) + && !(pf->flags&UF_NOWRAP)))) { + pf->sngpos=0; + pf->posjmp=2; + } else + pf->posjmp=3; + } + break; + case UNI_PTEFFECTE: + DoEEffects(UniGetByte()); + break; + case UNI_PTEFFECTF: + dat=UniGetByte(); + if (pf->vbtick||pf->patdly2) break; + if (pf->extspd&&(dat>=0x20)) + pf->bpm=dat; + else + if (dat) { + pf->sngspd=(dat>32)?32:dat; + pf->vbtick=0; + } + break; + case UNI_S3MEFFECTA: + DoS3MSpeed(UniGetByte()); + break; + case UNI_S3MEFFECTD: + DoS3MVolSlide(UniGetByte()); + break; + case UNI_S3MEFFECTE: + dat=UniGetByte(); + if (a->period) + DoS3MSlideDn(dat); + break; + case UNI_S3MEFFECTF: + dat=UniGetByte(); + if (a->period) + DoS3MSlideUp(dat); + break; + case UNI_S3MEFFECTI: + DoS3MTremor(UniGetByte()); + a->ownvol=1; + break; + case UNI_S3MEFFECTQ: + dat=UniGetByte(); + if (a->period) + DoS3MRetrig(dat); + break; + case UNI_S3MEFFECTR: + dat=UniGetByte(); + if (!pf->vbtick) { + if (dat&0x0f) a->trmdepth=dat&0xf; + if (dat&0xf0) a->trmspd=(dat&0xf0)>>2; + } + DoS3MTremolo(); + a->ownvol=1; + break; + case UNI_S3MEFFECTT: + DoS3MTempo(UniGetByte()); + break; + case UNI_S3MEFFECTU: + dat=UniGetByte(); + if (!pf->vbtick) { + if (dat&0x0f) a->vibdepth=dat&0xf; + if (dat&0xf0) a->vibspd=(dat&0xf0)>>2; + } else + if (a->period) { + DoS3MFineVibrato(); + a->ownper=1; + } + break; + case UNI_KEYOFF: + a->keyoff|=KEY_OFF; + if ((!(a->volflg&EF_ON))||(a->volflg&EF_LOOP)) + a->keyoff=KEY_KILL; + break; + case UNI_KEYFADE: + dat=UniGetByte(); + if ((pf->vbtick>=dat)||(pf->vbtick==pf->sngspd-1)) { + a->keyoff=KEY_KILL; + if (!(a->volflg&EF_ON)) + a->fadevol=0; + } + break; + case UNI_VOLEFFECTS: + DoVolEffects(UniGetByte()); + break; + case UNI_XMEFFECTA: + DoXMVolSlide(UniGetByte()); + break; + case UNI_XMEFFECTE1: /* XM fineslide up */ + dat=UniGetByte(); + if (!pf->vbtick) { + if (dat) a->fportupspd=dat; + if (a->period) + a->tmpperiod-=(a->fportupspd<<2); + } + break; + case UNI_XMEFFECTE2: /* XM fineslide dn */ + dat=UniGetByte(); + if (!pf->vbtick) { + if (dat) a->fportdnspd=dat; + if (a->period) + a->tmpperiod+=(a->fportdnspd<<2); + } + break; + case UNI_XMEFFECTEA: /* fine volume slide up */ + dat=UniGetByte(); + if (!pf->vbtick) + if (dat) a->fslideupspd=dat; + a->tmpvolume+=a->fslideupspd; + if (a->tmpvolume>64) a->tmpvolume=64; + break; + case UNI_XMEFFECTEB: /* fine volume slide dn */ + dat=UniGetByte(); + if (!pf->vbtick) + if (dat) a->fslidednspd=dat; + a->tmpvolume-=a->fslidednspd; + if (a->tmpvolume<0) a->tmpvolume=0; + break; + case UNI_XMEFFECTG: + pf->volume=UniGetByte()<<1; + if (pf->volume>128) pf->volume=128; + break; + case UNI_XMEFFECTH: + DoXMGlobalSlide(UniGetByte()); + break; + case UNI_XMEFFECTL: + dat=UniGetByte(); + if ((!pf->vbtick)&&(a->i)) { + UWORD points; + INSTRUMENT *i=a->i; + MP_VOICE *aout; + + if ((aout=a->slave)) { + points=i->volenv[i->volpts-1].pos; + aout->venv.p=aout->venv.env[(dat>points)?points:dat].pos; + points=i->panenv[i->panpts-1].pos; + aout->penv.p=aout->penv.env[(dat>points)?points:dat].pos; + } + } + break; + case UNI_XMEFFECTP: + dat=UniGetByte(); + if (pf->panflag) + DoXMPanSlide(dat); + break; + case UNI_XMEFFECTX1: + dat=UniGetByte(); + if (dat) a->ffportupspd=dat; + else dat=a->ffportupspd; + if (a->period) { + DoXMExtraFineSlideUp(dat); + a->ownper=1; + } + break; + case UNI_XMEFFECTX2: + dat=UniGetByte(); + if (dat) a->ffportdnspd=dat; + else dat=a->ffportdnspd; + if (a->period) { + DoXMExtraFineSlideDown(dat); + a->ownper=1; + } + break; + case UNI_ITEFFECTG: + dat=UniGetByte(); + if (dat) { + a->portspeed=dat; + } + if (a->period) { + if ((!pf->vbtick)&&(a->newsamp)){ + a->kick=KICK_NOTE; + a->start=-1; + } else + a->kick=(a->kick==KICK_NOTE)?KICK_ENV:KICK_ABSENT; + DoITToneSlide(); + a->ownper=1; + } + break; + case UNI_ITEFFECTH: /* IT vibrato */ + dat=UniGetByte(); + if (!pf->vbtick) { + if (dat&0x0f) a->vibdepth=dat&0xf; + if (dat&0xf0) a->vibspd=(dat&0xf0)>>2; + } + if (a->period) { + DoITVibrato(); + a->ownper=1; + } + break; + case UNI_ITEFFECTI: /* IT tremor */ + DoITTremor(UniGetByte()); + a->ownvol=1; + break; + case UNI_ITEFFECTM: + a->chanvol=UniGetByte(); + if (a->chanvol>64) a->chanvol=64; + else if (a->chanvol<0) a->chanvol=0; + break; + case UNI_ITEFFECTN: /* slide / fineslide channel volume */ + DoITChanVolSlide(UniGetByte()); + break; + case UNI_ITEFFECTP: /* slide / fineslide channel panning */ + dat=UniGetByte(); + if (pf->panflag) + DoITPanSlide(dat); + break; + case UNI_ITEFFECTT: /* slide / fineslide tempo */ + DoITTempo(UniGetByte()); + break; + case UNI_ITEFFECTU: /* fine vibrato */ + dat=UniGetByte(); + if (!pf->vbtick) { + if (dat&0x0f) a->vibdepth=dat&0xf; + if (dat&0xf0) a->vibspd=(dat&0xf0)>>2; + } + if (a->period) { + DoITFineVibrato(); + a->ownper=1; + } + break; + case UNI_ITEFFECTW: /* slide / fineslide global volume */ + DoITGlobalSlide(UniGetByte()); + break; + case UNI_ITEFFECTY: /* panbrello */ + dat=UniGetByte(); + if (!pf->vbtick) { + if (dat&0x0f) a->panbdepth=(dat&0xf); + if (dat&0xf0) a->panbspd=(dat&0xf0)>>4; + } + if (pf->panflag) DoITPanbrello(); + break; + case UNI_ITEFFECTS0: + DoSSEffects(UniGetByte()); + break; + case UNI_ITEFFECTZ: + /* FIXME not yet implemented */ + UniSkipOpcode(UNI_ITEFFECTZ); + break; + case UNI_ULTEFFECT9: + DoULTSampleOffset(); + break; + case UNI_MEDSPEED: + DoMEDSpeed(); + break; + case UNI_MEDEFFECTF1: + DoEEffects(0x90|(pf->sngspd/2)); + break; + case UNI_MEDEFFECTF2: + DoEEffects(0xd0|(pf->sngspd/2)); + break; + case UNI_MEDEFFECTF3: + DoEEffects(0x90|(pf->sngspd/3)); + break; + case UNI_XMEFFECTZ: + Player_SetSynchroValue(UniGetByte()); + break; + default: + a->sliding=oldsliding; + UniSkipOpcode(c); + break; + } + } +} + +static void DoNNAEffects(UBYTE dat) +{ + int t; + MP_VOICE *aout; + + dat&=0xf; + aout=(a->slave)?a->slave:NULL; + + switch (dat) { + case 0x0: /* past note cut */ + for (t=0;tvoice[t].master==a) + pf->voice[t].fadevol=0; + break; + case 0x1: /* past note off */ + for (t=0;tvoice[t].master==a) { + pf->voice[t].keyoff|=KEY_OFF; + if ((!(pf->voice[t].venv.flg & EF_ON))|| + (pf->voice[t].venv.flg & EF_LOOP)) + pf->voice[t].keyoff=KEY_KILL; + } + break; + case 0x2: /* past note fade */ + for (t=0;tvoice[t].master==a) + pf->voice[t].keyoff|=KEY_FADE; + break; + case 0x3: /* set NNA note cut */ + a->nna=(a->nna&~NNA_MASK)|NNA_CUT; + break; + case 0x4: /* set NNA note continue */ + a->nna=(a->nna&~NNA_MASK)|NNA_CONTINUE; + break; + case 0x5: /* set NNA note off */ + a->nna=(a->nna&~NNA_MASK)|NNA_OFF; + break; + case 0x6: /* set NNA note fade */ + a->nna=(a->nna&~NNA_MASK)|NNA_FADE; + break; + case 0x7: /* disable volume envelope */ + if (aout) + aout->volflg&=~EF_ON; + break; + case 0x8: /* enable volume envelope */ + if (aout) + aout->volflg|=EF_ON; + break; + case 0x9: /* disable panning envelope */ + if (aout) + aout->panflg&=~EF_ON; + break; + case 0xa: /* enable panning envelope */ + if (aout) + aout->panflg|=EF_ON; + break; + case 0xb: /* disable pitch envelope */ + if (aout) + aout->pitflg&=~EF_ON; + break; + case 0xc: /* enable pitch envelope */ + if (aout) + aout->pitflg|=EF_ON; + break; + } +} + +void pt_UpdateVoices(int max_volume) +{ + SWORD envpan,envvol,envpit; + UWORD playperiod; + SLONG vibval,vibdpt; + ULONG tmpvol; + + MP_VOICE *aout; + INSTRUMENT *i; + SAMPLE *s; + + pf->totalchn=pf->realchn=0; + for (mp_channel=0;mp_channelvoice[mp_channel]; + i=aout->i; + s=aout->s; + + if ((!s)||(!s->length)) continue; + + if (aout->period<40) aout->period=40; + else if (aout->period>50000) aout->period=50000; + + if ((aout->kick==KICK_NOTE)||(aout->kick==KICK_KEYOFF)) { + Voice_Play_internal(mp_channel,s,(aout->start==-1)?((s->flags&SF_UST_LOOP)?s->loopstart:0):aout->start); + aout->fadevol=32768; + aout->aswppos=0; + } + + if (i && ((aout->kick==KICK_NOTE)||(aout->kick==KICK_ENV))) { + StartEnvelope(&aout->venv,aout->volflg,i->volpts,i->volsusbeg, + i->volsusend,i->volbeg,i->volend,i->volenv,aout->keyoff); + StartEnvelope(&aout->penv,aout->panflg,i->panpts,i->pansusbeg, + i->pansusend,i->panbeg,i->panend,i->panenv,aout->keyoff); + StartEnvelope(&aout->cenv,aout->pitflg,i->pitpts,i->pitsusbeg, + i->pitsusend,i->pitbeg,i->pitend,i->pitenv,aout->keyoff); + if (aout->cenv.flg&EF_ON) + aout->masterperiod=GetPeriod((UWORD)aout->note<<1,aout->master->speed); + } + aout->kick=KICK_ABSENT; + + envvol=(!(aout->volflg & EF_ON))?256: + ProcessEnvelope(&aout->venv,256,aout->keyoff); + envpan=(!(aout->panflg & EF_ON))?PAN_CENTER: + ProcessEnvelope(&aout->penv,PAN_CENTER,aout->keyoff); + envpit=(!(aout->pitflg & EF_ON))?32: + ProcessEnvelope(&aout->cenv,32,aout->keyoff); + + tmpvol=aout->fadevol; /* max 32768 */ + tmpvol*=aout->chanvol; /* * max 64 */ + tmpvol*=aout->volume; /* * max 256 */ + tmpvol/=16384L; /* tmpvol is max 32768 */ + aout->totalvol=tmpvol>>2; /* totalvolume used to determine samplevolume */ + tmpvol*=envvol; /* * max 256 */ + tmpvol*=pf->volume; /* * max 128 */ + tmpvol/=4194304UL; + + /* fade out */ + if (pf->sngpos>=pf->numpos) tmpvol=0; + else + tmpvol=(tmpvol*max_volume)/128; + + if ((aout->masterchn!=-1)&& pf->control[aout->masterchn].muted) + Voice_SetVolume_internal(mp_channel,0); + else { + Voice_SetVolume_internal(mp_channel,tmpvol); + if ((tmpvol)&&(aout->master)&&(aout->master->slave==aout)) + pf->realchn++; + pf->totalchn++; + } + + if (aout->panning==PAN_SURROUND) + Voice_SetPanning_internal(mp_channel,PAN_SURROUND); + else + if ((pf->panflag)&&(aout->penv.flg & EF_ON)) + Voice_SetPanning_internal(mp_channel,DoPan(envpan,aout->panning)); + else + Voice_SetPanning_internal(mp_channel,aout->panning); + + if (aout->period && s->vibdepth) + switch (s->vibtype) { + case 0: + vibval=avibtab[s->avibpos&127]; + if (s->avibpos & 0x80) vibval=-vibval; + break; + case 1: + vibval=64; + if (s->avibpos & 0x80) vibval=-vibval; + break; + case 2: + vibval=63-(((s->avibpos+128)&255)>>1); + break; + default: + vibval=(((s->avibpos+128)&255)>>1)-64; + break; + } + else + vibval=0; + + if (s->vibflags & AV_IT) { + if ((aout->aswppos>>8)vibdepth) { + aout->aswppos += s->vibsweep; + vibdpt=aout->aswppos; + } else + vibdpt=s->vibdepth<<8; + vibval=(vibval*vibdpt)>>16; + if (aout->mflag) { + if (!(pf->flags&UF_LINEAR)) vibval>>=1; + aout->period-=vibval; + } + } else { + /* do XM style auto-vibrato */ + if (!(aout->keyoff & KEY_OFF)) { + if (aout->aswpposvibsweep) { + vibdpt=(aout->aswppos*s->vibdepth)/s->vibsweep; + aout->aswppos++; + } else + vibdpt=s->vibdepth; + } else { + /* keyoff -> depth becomes 0 if final depth wasn't reached or + stays at final level if depth WAS reached */ + if (aout->aswppos>=s->vibsweep) + vibdpt=s->vibdepth; + else + vibdpt=0; + } + vibval=(vibval*vibdpt)>>8; + aout->period-=vibval; + } + + /* update vibrato position */ + s->avibpos=(s->avibpos+s->vibrate)&0xff; + + /* process pitch envelope */ + playperiod=aout->period; + + if ((aout->pitflg&EF_ON)&&(envpit!=32)) { + long p1; + + envpit-=32; + if ((aout->note<<1)+envpit<=0) envpit=-(aout->note<<1); + + p1=GetPeriod(((UWORD)aout->note<<1)+envpit,aout->master->speed)-aout->masterperiod; + if (p1>0) { + if ((UWORD)(playperiod+p1)<=playperiod) { + p1=0; + aout->keyoff|=KEY_OFF; + } + } else if (p1<0) { + if ((UWORD)(playperiod+p1)>=playperiod) { + p1=0; + aout->keyoff|=KEY_OFF; + } + } + playperiod+=p1; + } + + if (!aout->fadevol) { /* check for a dead note (fadevol=0) */ + Voice_Stop_internal(mp_channel); + pf->totalchn--; + if ((tmpvol)&&(aout->master)&&(aout->master->slave==aout)) + pf->realchn--; + } else { + Voice_SetFrequency_internal(mp_channel, + getfrequency(pf->flags,playperiod)); + + /* if keyfade, start substracting fadeoutspeed from fadevol: */ + if ((i)&&(aout->keyoff&KEY_FADE)) { + if (aout->fadevol>=i->volfade) + aout->fadevol-=i->volfade; + else + aout->fadevol=0; + } + } + + md_bpm=pf->bpm+pf->relspd; + if (md_bpm<32) md_bpm=32; + else if (md_bpm>255) md_bpm=255; + } +} + +/* Handles new notes or instruments */ +void pt_Notes(void) +{ + UBYTE c,inst; + int tr,funky; /* funky is set to indicate note or instrument change */ + + for (mp_channel=0;mp_channelnumchn;mp_channel++) { + a=&pf->control[mp_channel]; + + if (pf->sngpos>=pf->numpos) { + tr=pf->numtrk; + pf->numrow=0; + } else { + tr=pf->patterns[(pf->positions[pf->sngpos]*pf->numchn)+mp_channel]; + pf->numrow=pf->pattrows[pf->positions[pf->sngpos]]; + } + + a->row=(trnumtrk)?UniFindRow(pf->tracks[tr],pf->patpos):NULL; + a->newsamp=0; + if (!pf->vbtick) a->notedelay=0; + + if (!a->row) continue; + UniSetRow(a->row); + funky=0; + + while((c=UniGetByte())) + switch (c) { + case UNI_NOTE: + funky|=1; + a->oldnote=a->anote,a->anote=UniGetByte(); + a->kick =KICK_NOTE; + a->start=-1; + a->sliding=0; + + /* retrig tremolo and vibrato waves ? */ + if (!(a->wavecontrol & 0x80)) a->trmpos=0; + if (!(a->wavecontrol & 0x08)) a->vibpos=0; + if (!a->panbwave) a->panbpos=0; + break; + case UNI_INSTRUMENT: + inst=UniGetByte(); + if (inst>=pf->numins) break; /* safety valve */ + funky|=2; + a->i=(pf->flags & UF_INST)?&pf->instruments[inst]:NULL; + a->retrig=0; + a->s3mtremor=0; + a->sample=inst; + break; + default: + UniSkipOpcode(c); + break; + } + + if (funky) { + INSTRUMENT *i; + SAMPLE *s; + + i=a->i; + if (i) { + if (i->samplenumber[a->anote] >= pf->numsmp) continue; + s=&pf->samples[i->samplenumber[a->anote]]; + a->note=i->samplenote[a->anote]; + } else { + a->note=a->anote; + s=&pf->samples[a->sample]; + } + + if (a->s!=s) { + a->s=s; + a->newsamp=a->period; + } + + /* channel or instrument determined panning ? */ + a->panning=pf->panning[mp_channel]; + if (s->flags & SF_OWNPAN) + a->panning=s->panning; + else if ((i)&&(i->flags & IF_OWNPAN)) + a->panning=i->panning; + + a->handle=s->handle; + a->speed=s->speed; + + if (i) { + if ((pf->panflag)&&(i->flags & IF_PITCHPAN) + &&(a->panning!=PAN_SURROUND)){ + a->panning+=((a->anote-i->pitpancenter)*i->pitpansep)/8; + if (a->panningpanning=PAN_LEFT; + else if (a->panning>PAN_RIGHT) a->panning=PAN_RIGHT; + } + a->pitflg=i->pitflg; + a->volflg=i->volflg; + a->panflg=i->panflg; + a->nna=i->nnatype; + a->dca=i->dca; + a->dct=i->dct; + } else { + a->pitflg=0; + a->volflg=0; + a->panflg=0; + a->nna=0; + a->dca=0; + a->dct=0; + } + + if (funky&2) /* instrument change */ { + /* IT random volume variations: 0:8 bit fixed, and one bit for + sign. */ + a->volume=a->tmpvolume=s->volume; + if ((s)&&(i)) { + if (i->rvolvar) { + a->volume=a->tmpvolume=s->volume+ + ((s->volume*((SLONG)i->rvolvar*(SLONG)getrandom(512) + ))/25600); + if (a->volume<0) a->volume=a->tmpvolume=0; + else if (a->volume>64) a->volume=a->tmpvolume=64; + } + if ((pf->panflag)&&(a->panning!=PAN_SURROUND)) { + a->panning+=((a->panning*((SLONG)i->rpanvar* + (SLONG)getrandom(512)))/25600); + if (a->panningpanning=PAN_LEFT; + else if (a->panning>PAN_RIGHT) a->panning=PAN_RIGHT; + } + } + } + + a->wantedperiod=a->tmpperiod=GetPeriod((UWORD)a->note<<1,a->speed); + a->keyoff=KEY_KICK; + } + } +} + +/* Handles effects */ +void pt_EffectsPass1(void) +{ + MP_VOICE *aout; + + for (mp_channel=0;mp_channelnumchn;mp_channel++) { + a=&pf->control[mp_channel]; + + if ((aout=a->slave)) { + a->fadevol=aout->fadevol; + a->period=aout->period; + if (a->kick==KICK_KEYOFF) a->keyoff=aout->keyoff; + } + + if (!a->row) continue; + UniSetRow(a->row); + + a->ownper=a->ownvol=0; + explicitslides=0; + pt_playeffects(); + + /* continue volume slide if necessary for XM and IT */ + if (pf->flags&UF_BGSLIDES) { + if (!explicitslides) + switch (a->sliding) { + case 1: + DoS3MVolSlide(0); + break; + case 2: + DoXMVolSlide(0); + break; + } + else if (a->tmpvolume) a->sliding=explicitslides; + } + + if (!a->ownper) a->period=a->tmpperiod; + if (!a->ownvol) a->volume=a->tmpvolume; + + if (a->s) { + if (a->i) + a->outvolume=(a->volume*a->s->globvol*a->i->globvol)>>10; + else + a->outvolume=(a->volume*a->s->globvol)>>4; + if (a->outvolume>256)a->volume=256; + else if (a->outvolume<0) a->outvolume=0; + } + } +} + +/* NNA management */ +void pt_NNA(void) +{ + for (mp_channel=0;mp_channelnumchn;mp_channel++) { + a=&pf->control[mp_channel]; + + if (a->kick==KICK_NOTE) { + BOOL k=0; + + if (a->slave) { + MP_VOICE *aout; + + aout=a->slave; + if (aout->nna & NNA_MASK) { + /* Make sure the old MP_VOICE channel knows it has no + master now ! */ + a->slave=NULL; + /* assume the channel is taken by NNA */ + aout->mflag=0; + + switch (aout->nna) { + case NNA_CONTINUE: /* continue note, do nothing */ + break; + case NNA_OFF: /* note off */ + aout->keyoff|=KEY_OFF; + if ((!(aout->volflg & EF_ON))||(aout->volflg & EF_LOOP)) + aout->keyoff=KEY_KILL; + break; + case NNA_FADE: + aout->keyoff |= KEY_FADE; + break; + } + } + } + + if (a->dct!=DCT_OFF) { + int t; + + for (t=0;tvoice[t].masterchn==mp_channel)&& + (a->sample==pf->voice[t].sample)) { + k=0; + switch (a->dct) { + case DCT_NOTE: + if (a->note==pf->voice[t].note) + k=1; + break; + case DCT_SAMPLE: + if (a->handle==pf->voice[t].handle) + k=1; + break; + case DCT_INST: + k=1; + break; + } + if (k) + switch (a->dca) { + case DCA_CUT: + pf->voice[t].fadevol=0; + break; + case DCA_OFF: + pf->voice[t].keyoff|=KEY_OFF; + if ((!(pf->voice[t].volflg&EF_ON))|| + (pf->voice[t].volflg&EF_LOOP)) + pf->voice[t].keyoff=KEY_KILL; + break; + case DCA_FADE: + pf->voice[t].keyoff|=KEY_FADE; + break; + } + } + } + } /* if (a->kick==KICK_NOTE) */ + } +} + +/* Setup module and NNA voices */ +void pt_SetupVoices(void) +{ + MP_VOICE *aout; + + for (mp_channel=0;mp_channelnumchn;mp_channel++) { + a=&pf->control[mp_channel]; + + if (a->notedelay) continue; + if (a->kick==KICK_NOTE) { + /* if no channel was cut above, find an empty or quiet channel + here */ + if (pf->flags&UF_NNA) { + if (!a->slave) { + int newchn; + + if ((newchn=MP_FindEmptyChannel())!=-1) + a->slave=&pf->voice[a->slavechn=newchn]; + } + } else + a->slave=&pf->voice[a->slavechn=mp_channel]; + + /* assign parts of MP_VOICE only done for a KICK ! */ + if ((aout=a->slave)) { + if (aout->mflag && aout->master) aout->master->slave=NULL; + a->slave=aout; + aout->master=a; + aout->masterchn=mp_channel; + aout->mflag=1; + } + } else + aout=a->slave; + + if (aout) { + aout->kick=a->kick; + aout->i=a->i; + aout->s=a->s; + aout->sample=a->sample; + aout->handle=a->handle; + aout->period=a->period; + aout->panning=a->panning; + aout->chanvol=a->chanvol; + aout->fadevol=a->fadevol; + aout->start=a->start; + aout->volflg=a->volflg; + aout->panflg=a->panflg; + aout->pitflg=a->pitflg; + aout->volume=a->outvolume; + aout->keyoff=a->keyoff; + aout->note=a->note; + aout->nna=a->nna; + } + a->kick=KICK_ABSENT; + } +} + +/* second effect pass */ +void pt_EffectsPass2(void) +{ + UBYTE c; + + for (mp_channel=0;mp_channelnumchn;mp_channel++) { + a=&pf->control[mp_channel]; + + if (!a->row) continue; + UniSetRow(a->row); + + while((c=UniGetByte())) + if (c==UNI_ITEFFECTS0) { + c=UniGetByte(); + if ((c>>4)==SS_S7EFFECTS) + DoNNAEffects(c&0xf); + } else + UniSkipOpcode(c); + } +} + +void Player_HandleTick(void) +{ + int max_volume; + +#if 0 + /* don't handle the very first ticks, this allows the other hardware to + settle down so we don't loose any starting notes */ + if (isfirst) { + isfirst--; + return; + } +#endif + + if ((!pf)||(pf->forbid)||(pf->sngpos>=pf->numpos)) return; + + /* update time counter (sngtime is in milliseconds (in fact 2^-10)) */ + pf->sngremainder+=(1<<9)*5; /* thus 2.5*(1<<10), since fps=0.4xtempo */ + pf->sngtime+=pf->sngremainder/pf->bpm; + pf->sngremainder%=pf->bpm; + + if (++pf->vbtick>=pf->sngspd) { + if (pf->pat_repcrazy) + pf->pat_repcrazy=0; /* play 2 times row 0 */ + else + pf->patpos++; + pf->vbtick=0; + + /* process pattern-delay. pf->patdly2 is the counter and pf->patdly is + the command memory. */ + if (pf->patdly) + pf->patdly2=pf->patdly,pf->patdly=0; + if (pf->patdly2) { + /* patterndelay active */ + if (--pf->patdly2) + /* so turn back pf->patpos by 1 */ + if (pf->patpos) pf->patpos--; + } + + /* do we have to get a new patternpointer ? (when pf->patpos reaches the + pattern size, or when a patternbreak is active) */ + if (((pf->patpos>=pf->numrow)&&(pf->numrow>0))&&(!pf->posjmp)) + pf->posjmp=3; + + if (pf->posjmp) { + pf->patpos=pf->numrow?(pf->patbrk%pf->numrow):0; + pf->pat_repcrazy=0; + pf->sngpos+=(pf->posjmp-2); + for (mp_channel=0;mp_channelnumchn;mp_channel++) + pf->control[mp_channel].pat_reppos=-1; + + pf->patbrk=pf->posjmp=0; + /* handle the "---" (end of song) pattern since it can occur + *inside* the module in .IT and .S3M */ + if ((pf->sngpos>=pf->numpos)||(pf->positions[pf->sngpos]==255)) { + if (!pf->wrap) return; + if (!(pf->sngpos=pf->reppos)) { + pf->volume=pf->initvolume>128?128:pf->initvolume; + pf->sngspd=pf->initspeed?(pf->initspeed<32?pf->initspeed:32):6; + pf->bpm=pf->inittempo<32?32:pf->inittempo; + } + } + if (pf->sngpos<0) pf->sngpos=pf->numpos-1; + } + + if (!pf->patdly2) + pt_Notes(); + } + + if (((pf->sngpos==pf->numpos-1)||(pf->positions[pf->sngpos+1]==255))&& + (pf->fadeout)) + max_volume=pf->numrow?((pf->numrow-pf->patpos)*128)/pf->numrow:0; + else + max_volume=128; + + pt_EffectsPass1(); + if (pf->flags&UF_NNA) + pt_NNA(); + pt_SetupVoices(); + pt_EffectsPass2(); + + /* now set up the actual hardware channel playback information */ + pt_UpdateVoices(max_volume); +} + +static void Player_Init_internal(MODULE* mf) +{ + int t; + + for (t=0;tnumchn;t++) { + mf->control[t].chanvol=mf->chanvol[t]; + mf->control[t].panning=mf->panning[t]; + } + + mf->sngtime=0; + mf->sngremainder=0; + + mf->pat_repcrazy=0; + mf->sngpos=0; + mf->sngspd=mf->initspeed?(mf->initspeed<32?mf->initspeed:32):6; + mf->volume=mf->initvolume>128?128:mf->initvolume; + + mf->vbtick=mf->sngspd; + mf->patdly=0; + mf->patdly2=0; + mf->bpm=mf->inittempo<32?32:mf->inittempo; + mf->realchn=0; + + mf->patpos=0; + mf->posjmp=2; /* make sure the player fetches the first note */ + mf->numrow=-1; + mf->patbrk=0; +} + +BOOL Player_Init(MODULE* mf) +{ + mf->extspd=1; + mf->panflag=1; + mf->wrap=0; + mf->loop=1; + mf->fadeout=0; + + mf->relspd=0; + + /* make sure the player doesn't start with garbage */ + if (!(mf->control=(MP_CONTROL*)_mm_calloc(mf->numchn,sizeof(MP_CONTROL)))) + return 1; + if (!(mf->voice=(MP_VOICE*)_mm_calloc(md_sngchn,sizeof(MP_VOICE)))) + return 1; + + Player_Init_internal(mf); + return 0; +} + +void Player_Exit_internal(MODULE* mf) +{ + if (!mf) return; + if (mf==pf) { + Player_Stop_internal(); + pf=NULL; + } + if (mf->control) free(mf->control); + if (mf->voice) free(mf->voice); + mf->control=NULL; + mf->voice=NULL; +} + +void Player_Exit(MODULE* mf) +{ + MUTEX_LOCK(vars); + Player_Exit_internal(mf); + MUTEX_UNLOCK(vars); +} + +void Player_SetVolume(SWORD volume) +{ + MUTEX_LOCK(vars); + if (pf) + pf->volume=pf->initvolume=(volume<0)?0:(volume>128)?128:volume; + MUTEX_UNLOCK(vars); +} + +MODULE* Player_GetModule(void) +{ + MODULE* result; + + MUTEX_LOCK(vars); + result=pf; + MUTEX_UNLOCK(vars); + + return result; +} + +void Player_Start(MODULE *mf) +{ + int t; + + if (!mf) return; + + if (!MikMod_Active()) + MikMod_EnableOutput(); + + mf->forbid=0; + + MUTEX_LOCK(vars); + if (pf!=mf) { + /* new song is being started, so completely stop out the old one. */ + if (pf) pf->forbid=1; + for (t=0;tforbid=1; + pf=NULL; +} + +void Player_Stop(void) +{ + MUTEX_LOCK(vars); + Player_Stop_internal(); + MUTEX_UNLOCK(vars); +} + +BOOL Player_Active(void) +{ + BOOL result=0; + + MUTEX_LOCK(vars); + if (pf) + result=(!(pf->sngpos>=pf->numpos)); + MUTEX_UNLOCK(vars); + + return result; +} + +void Player_NextPosition(void) +{ + MUTEX_LOCK(vars); + if (pf) { + int t; + + pf->forbid=1; + pf->posjmp=3; + pf->patbrk=0; + pf->vbtick=pf->sngspd; + + for (t=0;tvoice[t].i=NULL; + pf->voice[t].s=NULL; + } + for (t=0;tnumchn;t++) { + pf->control[t].i=NULL; + pf->control[t].s=NULL; + } + pf->forbid=0; + } + MUTEX_UNLOCK(vars); +} + +void Player_PrevPosition(void) +{ + MUTEX_LOCK(vars); + if (pf) { + int t; + + pf->forbid=1; + pf->posjmp=1; + pf->patbrk=0; + pf->vbtick=pf->sngspd; + + for (t=0;tvoice[t].i=NULL; + pf->voice[t].s=NULL; + } + for (t=0;tnumchn;t++) { + pf->control[t].i=NULL; + pf->control[t].s=NULL; + } + pf->forbid=0; + } + MUTEX_UNLOCK(vars); +} + +void Player_SetPosition(UWORD pos) +{ + MUTEX_LOCK(vars); + if (pf) { + int t; + + pf->forbid=1; + if (pos>=pf->numpos) pos=pf->numpos; + pf->posjmp=2; + pf->patbrk=0; + pf->sngpos=pos; + pf->vbtick=pf->sngspd; + + for (t=0;tvoice[t].i=NULL; + pf->voice[t].s=NULL; + } + for (t=0;tnumchn;t++) { + pf->control[t].i=NULL; + pf->control[t].s=NULL; + } + pf->forbid=0; + + if (!pos) + Player_Init_internal(pf); + } + MUTEX_UNLOCK(vars); +} + +static void Player_Unmute_internal(SLONG arg1,va_list ap) +{ + SLONG t,arg2,arg3=0; + + if (pf) { + switch (arg1) { + case MUTE_INCLUSIVE: + if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))|| + (arg2>arg3)||(arg3>=pf->numchn)) + return; + for (;arg2numchn && arg2<=arg3;arg2++) + pf->control[arg2].muted=0; + break; + case MUTE_EXCLUSIVE: + if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))|| + (arg2>arg3)||(arg3>=pf->numchn)) + return; + for (t=0;tnumchn;t++) { + if ((t>=arg2) && (t<=arg3)) continue; + pf->control[t].muted=0; + } + break; + default: + if (arg1numchn) pf->control[arg1].muted=0; + break; + } + } +} + +void Player_Unmute(SLONG arg1, ...) +{ + va_list args; + + va_start(args,arg1); + MUTEX_LOCK(vars); + Player_Unmute_internal(arg1,args); + MUTEX_UNLOCK(vars); + va_end(args); +} + +static void Player_Mute_internal(SLONG arg1,va_list ap) +{ + SLONG t,arg2,arg3=0; + + if (pf) { + switch (arg1) { + case MUTE_INCLUSIVE: + if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))|| + (arg2>arg3)||(arg3>=pf->numchn)) + return; + for (;arg2numchn && arg2<=arg3;arg2++) + pf->control[arg2].muted=1; + break; + case MUTE_EXCLUSIVE: + if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))|| + (arg2>arg3)||(arg3>=pf->numchn)) + return; + for (t=0;tnumchn;t++) { + if ((t>=arg2) && (t<=arg3)) continue; + pf->control[t].muted=1; + } + break; + default: + if (arg1numchn) + pf->control[arg1].muted=1; + break; + } + } +} + +void Player_Mute(SLONG arg1,...) +{ + va_list args; + + va_start(args,arg1); + MUTEX_LOCK(vars); + Player_Mute_internal(arg1,args); + MUTEX_UNLOCK(vars); + va_end(args); +} + +static void Player_ToggleMute_internal(SLONG arg1,va_list ap) +{ + SLONG arg2,arg3=0; + ULONG t; + + if (pf) { + switch (arg1) { + case MUTE_INCLUSIVE: + if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))|| + (arg2>arg3)||(arg3>=pf->numchn)) + return; + for (;arg2numchn && arg2<=arg3;arg2++) + pf->control[arg2].muted=1-pf->control[arg2].muted; + break; + case MUTE_EXCLUSIVE: + if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))|| + (arg2>arg3)||(arg3>=pf->numchn)) + return; + for (t=0;tnumchn;t++) { + if ((t>=arg2) && (t<=arg3)) continue; + pf->control[t].muted=1-pf->control[t].muted; + } + break; + default: + if (arg1numchn) + pf->control[arg1].muted=1-pf->control[arg1].muted; + break; + } + } +} + +void Player_ToggleMute(SLONG arg1,...) +{ + va_list args; + + va_start(args,arg1); + MUTEX_LOCK(vars); + Player_ToggleMute_internal(arg1,args); + MUTEX_UNLOCK(vars); + va_end(args); +} + +BOOL Player_Muted(UBYTE chan) +{ + BOOL result=1; + + MUTEX_LOCK(vars); + if (pf) + result=(channumchn)?pf->control[chan].muted:1; + MUTEX_UNLOCK(vars); + + return result; +} + +int Player_GetChannelVoice(UBYTE chan) +{ + int result=0; + + MUTEX_LOCK(vars); + if (pf) + result=(channumchn)?pf->control[chan].slavechn:-1; + MUTEX_UNLOCK(vars); + + return result; +} + +UWORD Player_GetChannelPeriod(UBYTE chan) +{ + UWORD result=0; + + MUTEX_LOCK(vars); + if (pf) + result=(channumchn)?pf->control[chan].period:0; + MUTEX_UNLOCK(vars); + + return result; +} + +BOOL Player_Paused_internal(void) +{ + return pf?pf->forbid:1; +} + +BOOL Player_Paused(void) +{ + BOOL result; + + MUTEX_LOCK(vars); + result=Player_Paused_internal(); + MUTEX_UNLOCK(vars); + + return result; +} + +void Player_TogglePause(void) +{ + MUTEX_LOCK(vars); + if (pf) + pf->forbid=1-pf->forbid; + MUTEX_UNLOCK(vars); +} + +void Player_SetSpeed(UWORD speed) +{ + MUTEX_LOCK(vars); + if (pf) + pf->sngspd=speed?(speed<32?speed:32):1; + MUTEX_UNLOCK(vars); +} + +void Player_SetTempo(UWORD tempo) +{ + if (tempo<32) tempo=32; + + MUTEX_LOCK(vars); + if (pf) { + if ((!(pf->flags&UF_HIGHBPM))&&(tempo>255)) tempo=255; + pf->bpm=tempo; + } + MUTEX_UNLOCK(vars); +} + +static int _pl_synchro_value; +void Player_SetSynchroValue(int i) +{ + _pl_synchro_value = i; +} + +int Player_GetSynchroValue(void) +{ + return _pl_synchro_value; +} + + +/* ex:set ts=4: */ diff --git a/sc2/src/sc2code/libs/sound/decoders/mikmod/munitrk.c b/sc2/src/sc2code/libs/sound/decoders/mikmod/munitrk.c new file mode 100644 index 000000000..d999d6edd --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/mikmod/munitrk.c @@ -0,0 +1,297 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + All routines dealing with the manipulation of UNITRK streams + +==============================================================================*/ + +#include + +#include "mikmod_internals.h" + + +/* Unibuffer chunk size */ +#define BUFPAGE 128 + +UWORD unioperands[UNI_LAST]={ + 0, /* not used */ + 1, /* UNI_NOTE */ + 1, /* UNI_INSTRUMENT */ + 1, /* UNI_PTEFFECT0 */ + 1, /* UNI_PTEFFECT1 */ + 1, /* UNI_PTEFFECT2 */ + 1, /* UNI_PTEFFECT3 */ + 1, /* UNI_PTEFFECT4 */ + 1, /* UNI_PTEFFECT5 */ + 1, /* UNI_PTEFFECT6 */ + 1, /* UNI_PTEFFECT7 */ + 1, /* UNI_PTEFFECT8 */ + 1, /* UNI_PTEFFECT9 */ + 1, /* UNI_PTEFFECTA */ + 1, /* UNI_PTEFFECTB */ + 1, /* UNI_PTEFFECTC */ + 1, /* UNI_PTEFFECTD */ + 1, /* UNI_PTEFFECTE */ + 1, /* UNI_PTEFFECTF */ + 1, /* UNI_S3MEFFECTA */ + 1, /* UNI_S3MEFFECTD */ + 1, /* UNI_S3MEFFECTE */ + 1, /* UNI_S3MEFFECTF */ + 1, /* UNI_S3MEFFECTI */ + 1, /* UNI_S3MEFFECTQ */ + 1, /* UNI_S3MEFFECTR */ + 1, /* UNI_S3MEFFECTT */ + 1, /* UNI_S3MEFFECTU */ + 0, /* UNI_KEYOFF */ + 1, /* UNI_KEYFADE */ + 2, /* UNI_VOLEFFECTS */ + 1, /* UNI_XMEFFECT4 */ + 1, /* UNI_XMEFFECTA */ + 1, /* UNI_XMEFFECTE1 */ + 1, /* UNI_XMEFFECTE2 */ + 1, /* UNI_XMEFFECTEA */ + 1, /* UNI_XMEFFECTEB */ + 1, /* UNI_XMEFFECTG */ + 1, /* UNI_XMEFFECTH */ + 1, /* UNI_XMEFFECTL */ + 1, /* UNI_XMEFFECTP */ + 1, /* UNI_XMEFFECTX1 */ + 1, /* UNI_XMEFFECTX2 */ + 1, /* UNI_ITEFFECTG */ + 1, /* UNI_ITEFFECTH */ + 1, /* UNI_ITEFFECTI */ + 1, /* UNI_ITEFFECTM */ + 1, /* UNI_ITEFFECTN */ + 1, /* UNI_ITEFFECTP */ + 1, /* UNI_ITEFFECTT */ + 1, /* UNI_ITEFFECTU */ + 1, /* UNI_ITEFFECTW */ + 1, /* UNI_ITEFFECTY */ + 2, /* UNI_ITEFFECTZ */ + 1, /* UNI_ITEFFECTS0 */ + 2, /* UNI_ULTEFFECT9 */ + 2, /* UNI_MEDSPEED */ + 0, /* UNI_MEDEFFECTF1 */ + 0, /* UNI_MEDEFFECTF2 */ + 0, /* UNI_MEDEFFECTF3 */ + 1 /* UNI_XMEFFECTZ */ +}; + +/* Sparse description of the internal module format + ------------------------------------------------ + + A UNITRK stream is an array of bytes representing a single track of a pattern. +It's made up of 'repeat/length' bytes, opcodes and operands (sort of a assembly +language): + +rrrlllll +[REP/LEN][OPCODE][OPERAND][OPCODE][OPERAND] [REP/LEN][OPCODE][OPERAND].. +^ ^ ^ +|-------ROWS 0 - 0+REP of a track---------| |-------ROWS xx - xx+REP of a track... + + The rep/len byte contains the number of bytes in the current row, _including_ +the length byte itself (So the LENGTH byte of row 0 in the previous example +would have a value of 5). This makes it easy to search through a stream for a +particular row. A track is concluded by a 0-value length byte. + + The upper 3 bits of the rep/len byte contain the number of times -1 this row +is repeated for this track. (so a value of 7 means this row is repeated 8 times) + + Opcodes can range from 1 to 255 but currently only opcodes 1 to 52 are being +used. Each opcode can have a different number of operands. You can find the +number of operands to a particular opcode by using the opcode as an index into +the 'unioperands' table. + +*/ + +/*========== Reading routines */ + +static UBYTE *rowstart; /* startadress of a row */ +static UBYTE *rowend; /* endaddress of a row (exclusive) */ +static UBYTE *rowpc; /* current unimod(tm) programcounter */ + + +void UniSetRow(UBYTE* t) +{ + rowstart = t; + rowpc = rowstart; + rowend = t?rowstart+(*(rowpc++)&0x1f):t; +} + +UBYTE UniGetByte(void) +{ + return (rowpc end of track.. */ + l = (c>>5)+1; /* extract repeat value */ + if(l>row) break; /* reached wanted row? -> return pointer */ + row -= l; /* haven't reached row yet.. update row */ + t += c&0x1f; /* point t to the next row */ + } + return t; +} + +/*========== Writing routines */ + +static UBYTE *unibuf; /* pointer to the temporary unitrk buffer */ +static UWORD unimax; /* buffer size */ + +static UWORD unipc; /* buffer cursor */ +static UWORD unitt; /* current row index */ +static UWORD lastp; /* previous row index */ + +/* Resets index-pointers to create a new track. */ +void UniReset(void) +{ + unitt = 0; /* reset index to rep/len byte */ + unipc = 1; /* first opcode will be written to index 1 */ + lastp = 0; /* no previous row yet */ + unibuf[0] = 0; /* clear rep/len byte */ +} + +/* Expands the buffer */ +static BOOL UniExpand(int wanted) +{ + if ((unipc+wanted)>=unimax) { + UBYTE *newbuf; + + /* Expand the buffer by BUFPAGE bytes */ + newbuf=(UBYTE*)realloc(unibuf,(unimax+BUFPAGE)*sizeof(UBYTE)); + + /* Check if realloc succeeded */ + if(newbuf) { + unibuf = newbuf; + unimax+=BUFPAGE; + return 1; + } else + return 0; + } + return 1; +} + +/* Appends one byte of data to the current row of a track. */ +void UniWriteByte(UBYTE data) +{ + if (UniExpand(1)) + /* write byte to current position and update */ + unibuf[unipc++]=data; +} + +void UniWriteWord(UWORD data) +{ + if (UniExpand(2)) { + unibuf[unipc++]=data>>8; + unibuf[unipc++]=data&0xff; + } +} + +static BOOL MyCmp(UBYTE* a,UBYTE* b,UWORD l) +{ + UWORD t; + + for(t=0;t>5)+1; /* repeat of previous row */ + l = (unibuf[lastp]&0x1f); /* length of previous row */ + + len = unipc-unitt; /* length of current row */ + + /* Now, check if the previous and the current row are identical.. when they + are, just increase the repeat field of the previous row */ + if(n<8 && len==l && MyCmp(&unibuf[lastp+1],&unibuf[unitt+1],len-1)) { + unibuf[lastp]+=0x20; + unipc = unitt+1; + } else { + if (UniExpand(unitt-unipc)) { + /* current and previous row aren't equal... update the pointers */ + unibuf[unitt] = len; + lastp = unitt; + unitt = unipc++; + } + } +} + +/* Terminates the current unitrk stream and returns a pointer to a copy of the + stream. */ +UBYTE* UniDup(void) +{ + UBYTE *d; + + if (!UniExpand(unitt-unipc)) return NULL; + unibuf[unitt] = 0; + + if(!(d=(UBYTE *)_mm_malloc(unipc))) return NULL; + memcpy(d,unibuf,unipc); + + return d; +} + +BOOL UniInit(void) +{ + unimax = BUFPAGE; + + if(!(unibuf=(UBYTE*)_mm_malloc(unimax*sizeof(UBYTE)))) return 0; + return 1; +} + +void UniCleanup(void) +{ + if(unibuf) free(unibuf); + unibuf = NULL; +} + +/* ex:set ts=4: */ diff --git a/sc2/src/sc2code/libs/sound/decoders/mikmod/mwav.c b/sc2/src/sc2code/libs/sound/decoders/mikmod/mwav.c new file mode 100644 index 000000000..447329887 --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/mikmod/mwav.c @@ -0,0 +1,186 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + WAV sample loader + +==============================================================================*/ + +/* + FIXME: Stereo .WAV files are not yet supported as samples. +*/ + +#include + +#include "mikmod_internals.h" + +typedef struct WAV { + CHAR rID[4]; + ULONG rLen; + CHAR wID[4]; + CHAR fID[4]; + ULONG fLen; + UWORD wFormatTag; + UWORD nChannels; + ULONG nSamplesPerSec; + ULONG nAvgBytesPerSec; + UWORD nBlockAlign; + UWORD nFormatSpecific; +} WAV; + +SAMPLE* Sample_LoadGeneric_internal(MREADER* reader) +{ + SAMPLE *si=NULL; + WAV wh; + BOOL have_fmt=0; + + /* read wav header */ + _mm_read_string(wh.rID,4,reader); + wh.rLen = _mm_read_I_ULONG(reader); + _mm_read_string(wh.wID,4,reader); + + /* check for correct header */ + if(_mm_eof(reader)|| memcmp(wh.rID,"RIFF",4) || memcmp(wh.wID,"WAVE",4)) { + _mm_errno = MMERR_UNKNOWN_WAVE_TYPE; + return NULL; + } + + /* scan all RIFF blocks until we find the sample data */ + for(;;) { + CHAR dID[4]; + ULONG len,start; + + _mm_read_string(dID,4,reader); + len = _mm_read_I_ULONG(reader); + /* truncated file ? */ + if (_mm_eof(reader)) { + _mm_errno=MMERR_UNKNOWN_WAVE_TYPE; + return NULL; + } + start = _mm_ftell(reader); + + /* sample format block + should be present only once and before a data block */ + if(!memcmp(dID,"fmt ",4)) { + wh.wFormatTag = _mm_read_I_UWORD(reader); + wh.nChannels = _mm_read_I_UWORD(reader); + wh.nSamplesPerSec = _mm_read_I_ULONG(reader); + wh.nAvgBytesPerSec = _mm_read_I_ULONG(reader); + wh.nBlockAlign = _mm_read_I_UWORD(reader); + wh.nFormatSpecific = _mm_read_I_UWORD(reader); + +#ifdef MIKMOD_DEBUG + fprintf(stderr,"\rwavloader : wFormatTag=%04x blockalign=%04x nFormatSpc=%04x\n", + wh.wFormatTag,wh.nBlockAlign,wh.nFormatSpecific); +#endif + + if((have_fmt)||(wh.nChannels>1)) { + _mm_errno=MMERR_UNKNOWN_WAVE_TYPE; + return NULL; + } + have_fmt=1; + } else + /* sample data block + should be present only once and after a format block */ + if(!memcmp(dID,"data",4)) { + if(!have_fmt) { + _mm_errno=MMERR_UNKNOWN_WAVE_TYPE; + return NULL; + } + if(!(si=(SAMPLE*)_mm_malloc(sizeof(SAMPLE)))) return NULL; + si->speed = wh.nSamplesPerSec/wh.nChannels; + si->volume = 64; + si->length = len; + if(wh.nBlockAlign == 2) { + si->flags = SF_16BITS | SF_SIGNED; + si->length >>= 1; + } + si->inflags = si->flags; + SL_RegisterSample(si,MD_SNDFX,reader); + SL_LoadSamples(); + + /* skip any other remaining blocks - so in case of repeated sample + fragments, we'll return the first anyway instead of an error */ + break; + } + /* onto next block */ + _mm_fseek(reader,start+len,SEEK_SET); + if (_mm_eof(reader)) + break; + } + + return si; +} + +SAMPLE* Sample_LoadGeneric(MREADER* reader) +{ + SAMPLE* result; + + MUTEX_LOCK(vars); + result=Sample_LoadGeneric_internal(reader); + MUTEX_UNLOCK(vars); + + return result; +} + +SAMPLE* Sample_LoadFP(FILE *fp) +{ + SAMPLE* result=NULL; + MREADER* reader; + + if((reader=_mm_new_file_reader(fp))) { + result=Sample_LoadGeneric(reader); + _mm_delete_file_reader(reader); + } + return result; +} + +SAMPLE* Sample_Load(CHAR* filename) +{ + FILE *fp; + SAMPLE *si=NULL; + + if(!(md_mode & DMODE_SOFT_SNDFX)) return NULL; + if((fp=_mm_fopen(filename,"rb"))) { + si = Sample_LoadFP(fp); + fclose(fp); + } + return si; +} + +void Sample_Free(SAMPLE* si) +{ + if(si) { + MD_SampleUnload(si->handle); + free(si); + } +} + +void Sample_Free_internal(SAMPLE *si) +{ + MUTEX_LOCK(vars); + Sample_Free(si); + MUTEX_UNLOCK(vars); +} + +/* ex:set ts=4: */ diff --git a/sc2/src/sc2code/libs/sound/decoders/mikmod/npertab.c b/sc2/src/sc2code/libs/sound/decoders/mikmod/npertab.c new file mode 100644 index 000000000..db7ac52a6 --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/mikmod/npertab.c @@ -0,0 +1,43 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + MOD format period table. Used by both the MOD and M15 (15-inst mod) Loaders. + +==============================================================================*/ + +#include "mikmod_internals.h" + +UWORD npertab[7*OCTAVE]={ + /* -> Tuning 0 */ + 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016, 960, 906, + 856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453, + 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226, + 214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113, + 107, 101, 95, 90, 85, 80, 75, 71, 67, 63, 60, 56, + + 53, 50, 47, 45, 42, 40, 37, 35, 33, 31, 30, 28, + 27, 25, 24, 22, 21, 20, 19, 18, 17, 16, 15, 14 +}; + +/* ex:set ts=4: */ diff --git a/sc2/src/sc2code/libs/sound/decoders/mikmod/sloader.c b/sc2/src/sc2code/libs/sound/decoders/mikmod/sloader.c new file mode 100644 index 000000000..ba0247cbb --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/mikmod/sloader.c @@ -0,0 +1,511 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Routines for loading samples. The sample loader utilizes the routines + provided by the "registered" sample loader. + +==============================================================================*/ + +#include "mikmod_internals.h" + +static int sl_rlength; +static SWORD sl_old; +static SWORD *sl_buffer=NULL; +static SAMPLOAD *musiclist=NULL,*sndfxlist=NULL; + +/* size of the loader buffer in words */ +#define SLBUFSIZE 2048 + +/* IT-Compressed status structure */ +typedef struct ITPACK { + UWORD bits; /* current number of bits */ + UWORD bufbits; /* bits in buffer */ + SWORD last; /* last output */ + UBYTE buf; /* bit buffer */ +} ITPACK; + +BOOL SL_Init(SAMPLOAD* s) +{ + if(!sl_buffer) + if(!(sl_buffer=_mm_malloc(SLBUFSIZE*sizeof(SWORD)))) return 0; + + sl_rlength = s->length; + if(s->infmt & SF_16BITS) sl_rlength>>=1; + sl_old = 0; + + return 1; +} + +void SL_Exit(SAMPLOAD *s) +{ + if(sl_rlength>0) _mm_fseek(s->reader,sl_rlength,SEEK_CUR); + if(sl_buffer) { + free(sl_buffer); + sl_buffer=NULL; + } +} + +/* unpack a 8bit IT packed sample */ +static BOOL read_itcompr8(ITPACK* status,MREADER *reader,SWORD *sl_buffer,UWORD count,UWORD* incnt) +{ + SWORD *dest=sl_buffer,*end=sl_buffer+count; + UWORD x,y,needbits,havebits,new_count=0; + UWORD bits = status->bits; + UWORD bufbits = status->bufbits; + SBYTE last = status->last; + UBYTE buf = status->buf; + + while (dest>=y; + bufbits-=y; + needbits-=y; + havebits+=y; + } + if (new_count) { + new_count = 0; + if (++x >= bits) + x++; + bits = x; + continue; + } + if (bits<7) { + if (x==(1<<(bits-1))) { + new_count = 1; + continue; + } + } + else if (bits<9) { + y = (0xff >> (9-bits)) - 4; + if ((x>y)&&(x<=y+8)) { + if ((x-=y)>=bits) + x++; + bits = x; + continue; + } + } + else if (bits<10) { + if (x>=0x100) { + bits=x-0x100+1; + continue; + } + } else { + /* error in compressed data... */ + _mm_errno=MMERR_ITPACK_INVALID_DATA; + return 0; + } + + if (bits<8) /* extend sign */ + x = ((SBYTE)(x <<(8-bits))) >> (8-bits); + *(dest++)= (last+=x) << 8; /* convert to 16 bit */ + } + status->bits = bits; + status->bufbits = bufbits; + status->last = last; + status->buf = buf; + return dest-sl_buffer; +} + +/* unpack a 16bit IT packed sample */ +static BOOL read_itcompr16(ITPACK *status,MREADER *reader,SWORD *sl_buffer,UWORD count,UWORD* incnt) +{ + SWORD *dest=sl_buffer,*end=sl_buffer+count; + SLONG x,y,needbits,havebits,new_count=0; + UWORD bits = status->bits; + UWORD bufbits = status->bufbits; + SWORD last = status->last; + UBYTE buf = status->buf; + + while (dest>=y; + bufbits-=y; + needbits-=y; + havebits+=y; + } + if (new_count) { + new_count = 0; + if (++x >= bits) + x++; + bits = x; + continue; + } + if (bits<7) { + if (x==(1<<(bits-1))) { + new_count=1; + continue; + } + } + else if (bits<17) { + y=(0xffff>>(17-bits))-8; + if ((x>y)&&(x<=y+16)) { + if ((x-=y)>=bits) + x++; + bits = x; + continue; + } + } + else if (bits<18) { + if (x>=0x10000) { + bits=x-0x10000+1; + continue; + } + } else { + /* error in compressed data... */ + _mm_errno=MMERR_ITPACK_INVALID_DATA; + return 0; + } + + if (bits<16) /* extend sign */ + x = ((SWORD)(x<<(16-bits)))>>(16-bits); + *(dest++)=(last+=x); + } + status->bits = bits; + status->bufbits = bufbits; + status->last = last; + status->buf = buf; + return dest-sl_buffer; +} + +static BOOL SL_LoadInternal(void* buffer,UWORD infmt,UWORD outfmt,int scalefactor,ULONG length,MREADER* reader,BOOL dither) +{ + SBYTE *bptr = (SBYTE*)buffer; + SWORD *wptr = (SWORD*)buffer; + int stodo,t,u; + + int result,c_block=0; /* compression bytes until next block */ + ITPACK status; + UWORD incnt; + + while(length) { + stodo=(lengthRead(reader,sl_buffer,sizeof(SBYTE)*stodo); + src = (SBYTE*)sl_buffer; + dest = sl_buffer; + src += stodo;dest += stodo; + + for(t=0;t>1; + length-=2; + } + stodo = idx; + } + } + + if(outfmt & SF_16BITS) { + for(t=0;t>8; + } + } + return 0; +} + +BOOL SL_Load(void* buffer,SAMPLOAD *smp,ULONG length) +{ + return SL_LoadInternal(buffer,smp->infmt,smp->outfmt,smp->scalefactor, + length,smp->reader,0); +} + +/* Registers a sample for loading when SL_LoadSamples() is called. */ +SAMPLOAD* SL_RegisterSample(SAMPLE* s,int type,MREADER* reader) +{ + SAMPLOAD *news,**samplist,*cruise; + + if(type==MD_MUSIC) { + samplist = &musiclist; + cruise = musiclist; + } else + if (type==MD_SNDFX) { + samplist = &sndfxlist; + cruise = sndfxlist; + } else + return NULL; + + /* Allocate and add structure to the END of the list */ + if(!(news=(SAMPLOAD*)_mm_malloc(sizeof(SAMPLOAD)))) return NULL; + + if(cruise) { + while(cruise->next) cruise=cruise->next; + cruise->next = news; + } else + *samplist = news; + + news->infmt = s->flags & SF_FORMATMASK; + news->outfmt = news->infmt; + news->reader = reader; + news->sample = s; + news->length = s->length; + news->loopstart = s->loopstart; + news->loopend = s->loopend; + + return news; +} + +static void FreeSampleList(SAMPLOAD* s) +{ + SAMPLOAD *old; + + while(s) { + old = s; + s = s->next; + free(old); + } +} + +/* Returns the total amount of memory required by the samplelist queue. */ +static ULONG SampleTotal(SAMPLOAD* samplist,int type) +{ + int total = 0; + + while(samplist) { + samplist->sample->flags= + (samplist->sample->flags&~SF_FORMATMASK)|samplist->outfmt; + total += MD_SampleLength(type,samplist->sample); + samplist=samplist->next; + } + + return total; +} + +static ULONG RealSpeed(SAMPLOAD *s) +{ + return(s->sample->speed/(s->scalefactor?s->scalefactor:1)); +} + +static BOOL DitherSamples(SAMPLOAD* samplist,int type) +{ + SAMPLOAD *c2smp=NULL; + ULONG maxsize, speed; + SAMPLOAD *s; + + if(!samplist) return 0; + + if((maxsize=MD_SampleSpace(type)*1024)) + while(SampleTotal(samplist,type)>maxsize) { + /* First Pass - check for any 16 bit samples */ + s = samplist; + while(s) { + if(s->outfmt & SF_16BITS) { + SL_Sample16to8(s); + break; + } + s=s->next; + } + /* Second pass (if no 16bits found above) is to take the sample with + the highest speed and dither it by half. */ + if(!s) { + s = samplist; + speed = 0; + while(s) { + if((s->sample->length) && (RealSpeed(s)>speed)) { + speed=RealSpeed(s); + c2smp=s; + } + s=s->next; + } + if (c2smp) + SL_HalveSample(c2smp,2); + } + } + + /* Samples dithered, now load them ! */ + s = samplist; + while(s) { + /* sample has to be loaded ? -> increase number of samples, allocate + memory and load sample. */ + if(s->sample->length) { + if(s->sample->seekpos) + _mm_fseek(s->reader, s->sample->seekpos, SEEK_SET); + + /* Call the sample load routine of the driver module. It has to + return a 'handle' (>=0) that identifies the sample. */ + s->sample->handle = MD_SampleLoad(s, type); + s->sample->flags = (s->sample->flags & ~SF_FORMATMASK) | s->outfmt; + if(s->sample->handle<0) { + FreeSampleList(samplist); + if(_mm_errorhandler) _mm_errorhandler(); + return 1; + } + } + s = s->next; + } + + FreeSampleList(samplist); + return 0; +} + +BOOL SL_LoadSamples(void) +{ + BOOL ok; + + _mm_critical = 0; + + if((!musiclist)&&(!sndfxlist)) return 0; + ok=DitherSamples(musiclist,MD_MUSIC)||DitherSamples(sndfxlist,MD_SNDFX); + musiclist=sndfxlist=NULL; + + return ok; +} + +void SL_Sample16to8(SAMPLOAD* s) +{ + s->outfmt &= ~SF_16BITS; + s->sample->flags = (s->sample->flags&~SF_FORMATMASK) | s->outfmt; +} + +void SL_Sample8to16(SAMPLOAD* s) +{ + s->outfmt |= SF_16BITS; + s->sample->flags = (s->sample->flags&~SF_FORMATMASK) | s->outfmt; +} + +void SL_SampleSigned(SAMPLOAD* s) +{ + s->outfmt |= SF_SIGNED; + s->sample->flags = (s->sample->flags&~SF_FORMATMASK) | s->outfmt; +} + +void SL_SampleUnsigned(SAMPLOAD* s) +{ + s->outfmt &= ~SF_SIGNED; + s->sample->flags = (s->sample->flags&~SF_FORMATMASK) | s->outfmt; +} + +void SL_HalveSample(SAMPLOAD* s,int factor) +{ + s->scalefactor=factor>0?factor:2; + + s->sample->divfactor = s->scalefactor; + s->sample->length = s->length / s->scalefactor; + s->sample->loopstart = s->loopstart / s->scalefactor; + s->sample->loopend = s->loopend / s->scalefactor; +} + + +/* ex:set ts=4: */ diff --git a/sc2/src/sc2code/libs/sound/decoders/mikmod/virtch.c b/sc2/src/sc2code/libs/sound/decoders/mikmod/virtch.c new file mode 100644 index 000000000..0f2fae83b --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/mikmod/virtch.c @@ -0,0 +1,828 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Sample mixing routines, using a 32 bits mixing buffer. + +==============================================================================*/ + +/* + + Optional features include: + (a) 4-step reverb (for 16 bit output only) + (b) Interpolation of sample data during mixing + (c) Dolby Surround Sound +*/ + +#include +#include + +#include "mikmod_internals.h" + + +/* + Constant definitions + ==================== + + BITSHIFT + Controls the maximum volume of the sound output. All data is shifted + right by BITSHIFT after being mixed. Higher values result in quieter + sound and less chance of distortion. + + REVERBERATION + Controls the duration of the reverb. Larger values represent a shorter + reverb loop. Smaller values extend the reverb but can result in more of + an echo-ish sound. + +*/ + +#define BITSHIFT 9 +#define REVERBERATION 110000L + +#define FRACBITS 11 +#define FRACMASK ((1L< sample has to be restarted */ + UBYTE active; /* =1 -> sample is playing */ + UWORD flags; /* 16/8 bits looping/one-shot */ + SWORD handle; /* identifies the sample */ + ULONG start; /* start index */ + ULONG size; /* samplesize */ + ULONG reppos; /* loop start */ + ULONG repend; /* loop end */ + ULONG frq; /* current frequency */ + int vol; /* current volume */ + int pan; /* current panning position */ + + int rampvol; + int lvolsel,rvolsel; /* Volume factor in range 0-255 */ + int oldlvol,oldrvol; + + SLONGLONG current; /* current index in the sample */ + SLONGLONG increment; /* increment value */ +} VINFO; + +static SWORD **Samples; +static VINFO *vinf=NULL,*vnf; +static long tickleft,samplesthatfit,vc_memory=0; +static int vc_softchn; +static SLONGLONG idxsize,idxlpos,idxlend; +static SLONG *vc_tickbuf=NULL; +static UWORD vc_mode; + +/* Reverb control variables */ + +static int RVc1, RVc2, RVc3, RVc4, RVc5, RVc6, RVc7, RVc8; +static ULONG RVRindex; + +/* For Mono or Left Channel */ +static SLONG *RVbufL1=NULL,*RVbufL2=NULL,*RVbufL3=NULL,*RVbufL4=NULL, + *RVbufL5=NULL,*RVbufL6=NULL,*RVbufL7=NULL,*RVbufL8=NULL; + +/* For Stereo only (Right Channel) */ +static SLONG *RVbufR1=NULL,*RVbufR2=NULL,*RVbufR3=NULL,*RVbufR4=NULL, + *RVbufR5=NULL,*RVbufR6=NULL,*RVbufR7=NULL,*RVbufR8=NULL; + +#ifdef NATIVE_64BIT_INT +#define NATIVE SLONGLONG +#else +#define NATIVE SLONG +#endif + +/*========== 32 bit sample mixers - only for 32 bit platforms */ +#ifndef NATIVE_64BIT_INT + +static SLONG Mix32MonoNormal(SWORD* srce,SLONG* dest,SLONG index,SLONG increment,SLONG todo) +{ + SWORD sample; + + while(todo--) { + sample = srce[index >> FRACBITS]; + index += increment; + + *dest++ += vnf->lvolsel * sample; + } + return index; +} + +static SLONG Mix32StereoNormal(SWORD* srce,SLONG* dest,SLONG index,SLONG increment,SLONG todo) +{ + SWORD sample; + + while(todo--) { + sample=srce[index >> FRACBITS]; + index += increment; + + *dest++ += vnf->lvolsel * sample; + *dest++ += vnf->rvolsel * sample; + } + return index; +} + +static SLONG Mix32SurroundNormal(SWORD* srce,SLONG* dest,SLONG index,SLONG increment,SLONG todo) +{ + SWORD sample; + + while(todo--) { + sample = srce[index >> FRACBITS]; + index += increment; + + if(vnf->lvolsel>=vnf->rvolsel) { + *dest++ += vnf->lvolsel*sample; + *dest++ -= vnf->lvolsel*sample; + } else { + *dest++ -= vnf->rvolsel*sample; + *dest++ += vnf->rvolsel*sample; + } + } + return index; +} + +static SLONG Mix32MonoInterp(SWORD* srce,SLONG* dest,SLONG index,SLONG increment,SLONG todo) +{ + SLONG sample; + + while(todo--) { + sample=(SLONG)srce[index>>FRACBITS]+ + ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS]) + *(index&FRACMASK)>>FRACBITS); + index += increment; + + if(vnf->rampvol) { + *dest++ += (((SLONG)vnf->lvolsel<oldlvol-vnf->lvolsel)*vnf->rampvol + )*sample>>CLICK_SHIFT; + vnf->rampvol--; + } else + *dest++ += vnf->lvolsel * sample; + } + return index; +} + +static SLONG Mix32StereoInterp(SWORD* srce,SLONG* dest,SLONG index,SLONG increment,SLONG todo) +{ + SLONG sample; + + while(todo--) { + sample=(SLONG)srce[index>>FRACBITS]+ + ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS]) + *(index&FRACMASK)>>FRACBITS); + index += increment; + + if(vnf->rampvol) { + *dest++ += (((SLONG)vnf->lvolsel<oldlvol-vnf->lvolsel)*vnf->rampvol + )*sample>>CLICK_SHIFT; + *dest++ += (((SLONG)vnf->rvolsel<oldrvol-vnf->rvolsel)*vnf->rampvol + )*sample>>CLICK_SHIFT; + vnf->rampvol--; + } else { + *dest++ += vnf->lvolsel * sample; + *dest++ += vnf->rvolsel * sample; + } + } + return index; +} + +static SLONG Mix32SurroundInterp(SWORD* srce,SLONG* dest,SLONG index,SLONG increment,SLONG todo) +{ + SLONG sample; + + while(todo--) { + int oldvol,vol; + + sample=(SLONG)srce[index>>FRACBITS]+ + ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS]) + *(index&FRACMASK)>>FRACBITS); + index += increment; + + if(vnf->lvolsel>=vnf->rvolsel) { + vol=vnf->lvolsel;oldvol=vnf->oldlvol; + } else { + vol=vnf->rvolsel;oldvol=vnf->oldrvol; + } + + if(vnf->rampvol) { + sample=(((SLONG)vnf->lvolsel<oldlvol-vnf->lvolsel)*vnf->rampvol + )*sample>>CLICK_SHIFT; + *dest++ += sample; + *dest++ -= sample; + vnf->rampvol--; + } else { + *dest++ += vol*sample; + *dest++ -= vol*sample; + } + } + return index; +} +#endif + +/*========== 64 bit sample mixers - all platforms */ + +static SLONGLONG MixMonoNormal(SWORD* srce,SLONG* dest,SLONGLONG index,SLONGLONG increment,SLONG todo) +{ + SWORD sample; + + while(todo--) { + sample = srce[index >> FRACBITS]; + index += increment; + + *dest++ += vnf->lvolsel * sample; + } + return index; +} + +static SLONGLONG MixStereoNormal(SWORD* srce,SLONG* dest,SLONGLONG index,SLONGLONG increment,SLONG todo) +{ + SWORD sample; + + while(todo--) { + sample=srce[index >> FRACBITS]; + index += increment; + + *dest++ += vnf->lvolsel * sample; + *dest++ += vnf->rvolsel * sample; + } + return index; +} + +static SLONGLONG MixSurroundNormal(SWORD* srce,SLONG* dest,SLONGLONG index,SLONGLONG increment,SLONG todo) +{ + SWORD sample; + + while(todo--) { + sample = srce[index >> FRACBITS]; + index += increment; + + if(vnf->lvolsel>=vnf->rvolsel) { + *dest++ += vnf->lvolsel*sample; + *dest++ -= vnf->lvolsel*sample; + } else { + *dest++ -= vnf->rvolsel*sample; + *dest++ += vnf->rvolsel*sample; + } + } + return index; +} + +static SLONGLONG MixMonoInterp(SWORD* srce,SLONG* dest,SLONGLONG index,SLONGLONG increment,SLONG todo) +{ + SLONG sample; + + while(todo--) { + sample=(SLONG)srce[index>>FRACBITS]+ + ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS]) + *(index&FRACMASK)>>FRACBITS); + index += increment; + + if(vnf->rampvol) { + *dest++ += (((SLONG)vnf->lvolsel<oldlvol-vnf->lvolsel)*vnf->rampvol + )*sample>>CLICK_SHIFT; + vnf->rampvol--; + } else + *dest++ += vnf->lvolsel * sample; + } + return index; +} + +static SLONGLONG MixStereoInterp(SWORD* srce,SLONG* dest,SLONGLONG index,SLONGLONG increment,SLONG todo) +{ + SLONG sample; + + while(todo--) { + sample=(SLONG)srce[index>>FRACBITS]+ + ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS]) + *(index&FRACMASK)>>FRACBITS); + index += increment; + + if(vnf->rampvol) { + *dest++ +=(((SLONG)vnf->lvolsel<oldlvol-vnf->lvolsel)*vnf->rampvol + )*sample>>CLICK_SHIFT; + *dest++ +=(((SLONG)vnf->rvolsel<oldrvol-vnf->rvolsel)*vnf->rampvol + )*sample>>CLICK_SHIFT; + vnf->rampvol--; + } else { + *dest++ += vnf->lvolsel * sample; + *dest++ += vnf->rvolsel * sample; + } + } + return index; +} + +static SLONGLONG MixSurroundInterp(SWORD* srce,SLONG* dest,SLONGLONG index,SLONGLONG increment,SLONG todo) +{ + SLONG sample; + + while(todo--) { + int oldvol,vol; + + sample=(SLONG)srce[index>>FRACBITS]+ + ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS]) + *(index&FRACMASK)>>FRACBITS); + index += increment; + + if(vnf->lvolsel>=vnf->rvolsel) { + vol=vnf->lvolsel;oldvol=vnf->oldlvol; + } else { + vol=vnf->rvolsel;oldvol=vnf->oldrvol; + } + + if(vnf->rampvol) { + sample=(((SLONG)vnf->lvolsel<oldlvol-vnf->lvolsel)*vnf->rampvol + )*sample>>CLICK_SHIFT; + *dest++ += sample; + *dest++ -= sample; + vnf->rampvol--; + } else { + *dest++ += vol*sample; + *dest++ -= vol*sample; + } + } + return index; +} + +static void (*MixReverb)(SLONG* srce,NATIVE count); + +/* Reverb macros */ +#define COMPUTE_LOC(n) loc##n = RVRindex % RVc##n +#define COMPUTE_LECHO(n) RVbufL##n [loc##n ]=speedup+((ReverbPct*RVbufL##n [loc##n ])>>7) +#define COMPUTE_RECHO(n) RVbufR##n [loc##n ]=speedup+((ReverbPct*RVbufR##n [loc##n ])>>7) + +static void MixReverb_Normal(SLONG* srce,NATIVE count) +{ + unsigned int speedup; + int ReverbPct; + unsigned int loc1,loc2,loc3,loc4; + unsigned int loc5,loc6,loc7,loc8; + + ReverbPct=58+(md_reverb<<2); + + COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); + COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + + while(count--) { + /* Compute the left channel echo buffers */ + speedup = *srce >> 3; + + COMPUTE_LECHO(1); COMPUTE_LECHO(2); COMPUTE_LECHO(3); COMPUTE_LECHO(4); + COMPUTE_LECHO(5); COMPUTE_LECHO(6); COMPUTE_LECHO(7); COMPUTE_LECHO(8); + + /* Prepare to compute actual finalized data */ + RVRindex++; + + COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); + COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + + /* left channel */ + *srce++ +=RVbufL1[loc1]-RVbufL2[loc2]+RVbufL3[loc3]-RVbufL4[loc4]+ + RVbufL5[loc5]-RVbufL6[loc6]+RVbufL7[loc7]-RVbufL8[loc8]; + } +} + +static void MixReverb_Stereo(SLONG* srce,NATIVE count) +{ + unsigned int speedup; + int ReverbPct; + unsigned int loc1, loc2, loc3, loc4; + unsigned int loc5, loc6, loc7, loc8; + + ReverbPct = 92+(md_reverb<<1); + + COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); + COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + + while(count--) { + /* Compute the left channel echo buffers */ + speedup = *srce >> 3; + + COMPUTE_LECHO(1); COMPUTE_LECHO(2); COMPUTE_LECHO(3); COMPUTE_LECHO(4); + COMPUTE_LECHO(5); COMPUTE_LECHO(6); COMPUTE_LECHO(7); COMPUTE_LECHO(8); + + /* Compute the right channel echo buffers */ + speedup = srce[1] >> 3; + + COMPUTE_RECHO(1); COMPUTE_RECHO(2); COMPUTE_RECHO(3); COMPUTE_RECHO(4); + COMPUTE_RECHO(5); COMPUTE_RECHO(6); COMPUTE_RECHO(7); COMPUTE_RECHO(8); + + /* Prepare to compute actual finalized data */ + RVRindex++; + + COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); + COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + + /* left channel then right channel */ + *srce++ +=RVbufL1[loc1]-RVbufL2[loc2]+RVbufL3[loc3]-RVbufL4[loc4]+ + RVbufL5[loc5]-RVbufL6[loc6]+RVbufL7[loc7]-RVbufL8[loc8]; + + *srce++ +=RVbufR1[loc1]-RVbufR2[loc2]+RVbufR3[loc3]-RVbufR4[loc4]+ + RVbufR5[loc5]-RVbufR6[loc6]+RVbufR7[loc7]-RVbufR8[loc8]; + } +} + +/* Mixing macros */ +#define EXTRACT_SAMPLE(var,size) var=*srce++>>(BITSHIFT+16-size) +#define CHECK_SAMPLE(var,bound) var=(var>=bound)?bound-1:(var<-bound)?-bound:var +#define PUT_SAMPLE(var) *dste++=var + +static void Mix32To16(SWORD* dste,SLONG* srce,NATIVE count) +{ + SLONG x1,x2,x3,x4; + int remain; + + remain=count&3; + for(count>>=2;count;count--) { + EXTRACT_SAMPLE(x1,16); EXTRACT_SAMPLE(x2,16); + EXTRACT_SAMPLE(x3,16); EXTRACT_SAMPLE(x4,16); + + CHECK_SAMPLE(x1,32768); CHECK_SAMPLE(x2,32768); + CHECK_SAMPLE(x3,32768); CHECK_SAMPLE(x4,32768); + + PUT_SAMPLE(x1); PUT_SAMPLE(x2); PUT_SAMPLE(x3); PUT_SAMPLE(x4); + } + while(remain--) { + EXTRACT_SAMPLE(x1,16); + CHECK_SAMPLE(x1,32768); + PUT_SAMPLE(x1); + } +} + +static void Mix32To8(SBYTE* dste,SLONG* srce,NATIVE count) +{ + SWORD x1,x2,x3,x4; + int remain; + + remain=count&3; + for(count>>=2;count;count--) { + EXTRACT_SAMPLE(x1,8); EXTRACT_SAMPLE(x2,8); + EXTRACT_SAMPLE(x3,8); EXTRACT_SAMPLE(x4,8); + + CHECK_SAMPLE(x1,128); CHECK_SAMPLE(x2,128); + CHECK_SAMPLE(x3,128); CHECK_SAMPLE(x4,128); + + PUT_SAMPLE(x1+128); PUT_SAMPLE(x2+128); + PUT_SAMPLE(x3+128); PUT_SAMPLE(x4+128); + } + while(remain--) { + EXTRACT_SAMPLE(x1,8); + CHECK_SAMPLE(x1,128); + PUT_SAMPLE(x1+128); + } +} + +static void AddChannel(SLONG* ptr,NATIVE todo) +{ + SLONGLONG end,done; + SWORD *s; + + if(!(s=Samples[vnf->handle])) { + vnf->current = vnf->active = 0; + return; + } + + /* update the 'current' index so the sample loops, or stops playing if it + reached the end of the sample */ + while(todo>0) { + SLONGLONG endpos; + if(vnf->flags & SF_REVERSE) { + /* The sample is playing in reverse */ + if((vnf->flags&SF_LOOP)&&(vnf->currentflags & SF_BIDI) { + /* sample is doing bidirectional loops, so 'bounce' the + current index against the idxlpos */ + vnf->current = idxlpos+(idxlpos-vnf->current); + vnf->flags &= ~SF_REVERSE; + vnf->increment = -vnf->increment; + } else + /* normal backwards looping, so set the current position to + loopend index */ + vnf->current=idxlend-(idxlpos-vnf->current); + } else { + /* the sample is not looping, so check if it reached index 0 */ + if(vnf->current < 0) { + /* playing index reached 0, so stop playing this sample */ + vnf->current = vnf->active = 0; + break; + } + } + } else { + /* The sample is playing forward */ + if((vnf->flags & SF_LOOP) && (vnf->current >= idxlend)) { + /* the sample is looping, check the loopend index */ + if(vnf->flags & SF_BIDI) { + /* sample is doing bidirectional loops, so 'bounce' the + current index against the idxlend */ + vnf->flags |= SF_REVERSE; + vnf->increment = -vnf->increment; + vnf->current = idxlend-(vnf->current-idxlend); + } else + /* normal backwards looping, so set the current position + to loopend index */ + vnf->current=idxlpos+(vnf->current-idxlend); + } else { + /* sample is not looping, so check if it reached the last + position */ + if(vnf->current >= idxsize) { + /* yes, so stop playing this sample */ + vnf->current = vnf->active = 0; + break; + } + } + } + + end=(vnf->flags&SF_REVERSE)?(vnf->flags&SF_LOOP)?idxlpos:0: + (vnf->flags&SF_LOOP)?idxlend:idxsize; + + /* if the sample is not blocked... */ + if((end==vnf->current)||(!vnf->increment)) + done=0; + else { + done=MIN((end-vnf->current)/vnf->increment+1,todo); + if(done<0) done=0; + } + + if(!done) { + vnf->active = 0; + break; + } + + endpos=vnf->current+done*vnf->increment; + + if(vnf->vol) { +#ifndef NATIVE_64BIT_INT + /* use the 32 bit mixers as often as we can (they're much faster) */ + if((vnf->current<0x7fffffff)&&(endpos<0x7fffffff)) { + if((md_mode & DMODE_INTERP)) { + if(vc_mode & DMODE_STEREO) { + if((vnf->pan==PAN_SURROUND)&&(vc_mode&DMODE_SURROUND)) + vnf->current=Mix32SurroundInterp + (s,ptr,vnf->current,vnf->increment,done); + else + vnf->current=Mix32StereoInterp + (s,ptr,vnf->current,vnf->increment,done); + } else + vnf->current=Mix32MonoInterp + (s,ptr,vnf->current,vnf->increment,done); + } else if(vc_mode & DMODE_STEREO) { + if((vnf->pan==PAN_SURROUND)&&(vc_mode&DMODE_SURROUND)) + vnf->current=Mix32SurroundNormal + (s,ptr,vnf->current,vnf->increment,done); + else + vnf->current=Mix32StereoNormal + (s,ptr,vnf->current,vnf->increment,done); + } else + vnf->current=Mix32MonoNormal + (s,ptr,vnf->current,vnf->increment,done); + } else +#endif + { + if((md_mode & DMODE_INTERP)) { + if(vc_mode & DMODE_STEREO) { + if((vnf->pan==PAN_SURROUND)&&(vc_mode&DMODE_SURROUND)) + vnf->current=MixSurroundInterp + (s,ptr,vnf->current,vnf->increment,done); + else + vnf->current=MixStereoInterp + (s,ptr,vnf->current,vnf->increment,done); + } else + vnf->current=MixMonoInterp + (s,ptr,vnf->current,vnf->increment,done); + } else if(vc_mode & DMODE_STEREO) { + if((vnf->pan==PAN_SURROUND)&&(vc_mode&DMODE_SURROUND)) + vnf->current=MixSurroundNormal + (s,ptr,vnf->current,vnf->increment,done); + else + vnf->current=MixStereoNormal + (s,ptr,vnf->current,vnf->increment,done); + } else + vnf->current=MixMonoNormal + (s,ptr,vnf->current,vnf->increment,done); + } + + } else + /* update sample position */ + vnf->current=endpos; + + todo-=done; +#if 1 + if ( vc_mode & DMODE_STEREO ) + ptr += done*2; + else + ptr += done; +#else + ptr +=(vc_mode & DMODE_STEREO)?(done<<1):done; +#endif + } +} + +#define _IN_VIRTCH_ +#include "virtch_common.c" +#undef _IN_VIRTCH_ + +void VC1_WriteSamples(SBYTE* buf,ULONG todo) +{ + int left,portion=0,count; + SBYTE *buffer; + int t, pan, vol; + + while(todo) { + if(!tickleft) { + if(vc_mode & DMODE_SOFT_MUSIC) md_player(); + tickleft=(md_mixfreq*125L)/(md_bpm*50L); + } + left = MIN(tickleft, todo); + buffer = buf; + tickleft -= left; + todo -= left; + buf += samples2bytes(left); + + while(left) { + portion = MIN(left, samplesthatfit); + count = (vc_mode & DMODE_STEREO)?(portion<<1):portion; + memset(vc_tickbuf, 0, count<<2); + for(t=0;tkick) { + vnf->current=((SLONGLONG)vnf->start)<kick =0; + vnf->active =1; + } + + if(!vnf->frq) vnf->active = 0; + + if(vnf->active) { + vnf->increment=((SLONGLONG)(vnf->frq<flags&SF_REVERSE) vnf->increment=-vnf->increment; + vol = vnf->vol; pan = vnf->pan; + + vnf->oldlvol=vnf->lvolsel;vnf->oldrvol=vnf->rvolsel; + if(vc_mode & DMODE_STEREO) { + if(pan != PAN_SURROUND) { + vnf->lvolsel=(vol*(PAN_RIGHT-pan))>>8; + vnf->rvolsel=(vol*pan)>>8; + } else + vnf->lvolsel=vnf->rvolsel=vol/2; + } else + vnf->lvolsel=vol; + + idxsize = (vnf->size)? ((SLONGLONG)vnf->size << FRACBITS)-1 : 0; + idxlend = (vnf->repend)? ((SLONGLONG)vnf->repend << FRACBITS)-1 : 0; + idxlpos = (SLONGLONG)vnf->reppos << FRACBITS; + AddChannel(vc_tickbuf, portion); + } + } + + if(md_reverb) { + if(md_reverb>15) md_reverb=15; + MixReverb(vc_tickbuf, portion); + } + + if(vc_mode & DMODE_16BITS) + Mix32To16((SWORD*) buffer, vc_tickbuf, count); + else + Mix32To8((SBYTE*) buffer, vc_tickbuf, count); + + buffer += samples2bytes(portion); + left -= portion; + } + } +} + +BOOL VC1_Init(void) +{ + VC_SetupPointers(); + + if (md_mode&DMODE_HQMIXER) + return VC2_Init(); + + if(!(Samples=(SWORD**)_mm_calloc(MAXSAMPLEHANDLES,sizeof(SWORD*)))) { + _mm_errno = MMERR_INITIALIZING_MIXER; + return 1; + } + if(!vc_tickbuf) + if(!(vc_tickbuf=(SLONG*)_mm_malloc((TICKLSIZE+32)*sizeof(SLONG)))) { + _mm_errno = MMERR_INITIALIZING_MIXER; + return 1; + } + + MixReverb=(md_mode&DMODE_STEREO)?MixReverb_Stereo:MixReverb_Normal; + vc_mode = md_mode; + return 0; +} + +BOOL VC1_PlayStart(void) +{ + samplesthatfit=TICKLSIZE; + if(vc_mode & DMODE_STEREO) samplesthatfit >>= 1; + tickleft = 0; + + RVc1 = (5000L * md_mixfreq) / REVERBERATION; + RVc2 = (5078L * md_mixfreq) / REVERBERATION; + RVc3 = (5313L * md_mixfreq) / REVERBERATION; + RVc4 = (5703L * md_mixfreq) / REVERBERATION; + RVc5 = (6250L * md_mixfreq) / REVERBERATION; + RVc6 = (6953L * md_mixfreq) / REVERBERATION; + RVc7 = (7813L * md_mixfreq) / REVERBERATION; + RVc8 = (8828L * md_mixfreq) / REVERBERATION; + + if(!(RVbufL1=(SLONG*)_mm_calloc((RVc1+1),sizeof(SLONG)))) return 1; + if(!(RVbufL2=(SLONG*)_mm_calloc((RVc2+1),sizeof(SLONG)))) return 1; + if(!(RVbufL3=(SLONG*)_mm_calloc((RVc3+1),sizeof(SLONG)))) return 1; + if(!(RVbufL4=(SLONG*)_mm_calloc((RVc4+1),sizeof(SLONG)))) return 1; + if(!(RVbufL5=(SLONG*)_mm_calloc((RVc5+1),sizeof(SLONG)))) return 1; + if(!(RVbufL6=(SLONG*)_mm_calloc((RVc6+1),sizeof(SLONG)))) return 1; + if(!(RVbufL7=(SLONG*)_mm_calloc((RVc7+1),sizeof(SLONG)))) return 1; + if(!(RVbufL8=(SLONG*)_mm_calloc((RVc8+1),sizeof(SLONG)))) return 1; + + if(!(RVbufR1=(SLONG*)_mm_calloc((RVc1+1),sizeof(SLONG)))) return 1; + if(!(RVbufR2=(SLONG*)_mm_calloc((RVc2+1),sizeof(SLONG)))) return 1; + if(!(RVbufR3=(SLONG*)_mm_calloc((RVc3+1),sizeof(SLONG)))) return 1; + if(!(RVbufR4=(SLONG*)_mm_calloc((RVc4+1),sizeof(SLONG)))) return 1; + if(!(RVbufR5=(SLONG*)_mm_calloc((RVc5+1),sizeof(SLONG)))) return 1; + if(!(RVbufR6=(SLONG*)_mm_calloc((RVc6+1),sizeof(SLONG)))) return 1; + if(!(RVbufR7=(SLONG*)_mm_calloc((RVc7+1),sizeof(SLONG)))) return 1; + if(!(RVbufR8=(SLONG*)_mm_calloc((RVc8+1),sizeof(SLONG)))) return 1; + + RVRindex = 0; + return 0; +} + +void VC1_PlayStop(void) +{ + if(RVbufL1) free(RVbufL1); + if(RVbufL2) free(RVbufL2); + if(RVbufL3) free(RVbufL3); + if(RVbufL4) free(RVbufL4); + if(RVbufL5) free(RVbufL5); + if(RVbufL6) free(RVbufL6); + if(RVbufL7) free(RVbufL7); + if(RVbufL8) free(RVbufL8); + RVbufL1=RVbufL2=RVbufL3=RVbufL4=RVbufL5=RVbufL6=RVbufL7=RVbufL8=NULL; + if(RVbufR1) free(RVbufR1); + if(RVbufR2) free(RVbufR2); + if(RVbufR3) free(RVbufR3); + if(RVbufR4) free(RVbufR4); + if(RVbufR5) free(RVbufR5); + if(RVbufR6) free(RVbufR6); + if(RVbufR7) free(RVbufR7); + if(RVbufR8) free(RVbufR8); + RVbufR1=RVbufR2=RVbufR3=RVbufR4=RVbufR5=RVbufR6=RVbufR7=RVbufR8=NULL; +} + +BOOL VC1_SetNumVoices(void) +{ + int t; + + if(!(vc_softchn=md_softchn)) return 0; + + if(vinf) free(vinf); + if(!(vinf= _mm_calloc(sizeof(VINFO),vc_softchn))) return 1; + + for(t=0;t +#include + +#include "mikmod_internals.h" + +#if defined(macintosh) || defined(__APPLE__) +#define NO_64BIT_MIXER +#endif +/* + Constant Definitions + ==================== + + MAXVOL_FACTOR (was BITSHIFT in virtch.c) + Controls the maximum volume of the output data. All mixed data is + divided by this number after mixing, so larger numbers result in + quieter mixing. Smaller numbers will increase the likeliness of + distortion on loud modules. + + REVERBERATION + Larger numbers result in shorter reverb duration. Longer reverb + durations can cause unwanted static and make the reverb sound more + like a crappy echo. + + SAMPLING_SHIFT + Specified the shift multiplier which controls by how much the mixing + rate is multiplied while mixing. Higher values can improve quality by + smoothing the sound and reducing pops and clicks. Note, this is a shift + value, so a value of 2 becomes a mixing-rate multiplier of 4, and a + value of 3 = 8, etc. + + FRACBITS + The number of bits per integer devoted to the fractional part of the + number. Generally, this number should not be changed for any reason. + + !!! IMPORTANT !!! All values below MUST ALWAYS be greater than 0 + +*/ + +#define MAXVOL_FACTOR (1<<9) +#define REVERBERATION 11000L + +#define SAMPLING_SHIFT 2 +#define SAMPLING_FACTOR (1UL< sample has to be restarted */ + UBYTE active; /* =1 -> sample is playing */ + UWORD flags; /* 16/8 bits looping/one-shot */ + SWORD handle; /* identifies the sample */ + ULONG start; /* start index */ + ULONG size; /* samplesize */ + ULONG reppos; /* loop start */ + ULONG repend; /* loop end */ + ULONG frq; /* current frequency */ + int vol; /* current volume */ + int pan; /* current panning position */ + + int click; + int rampvol; + SLONG lastvalL,lastvalR; + int lvolsel,rvolsel; /* Volume factor in range 0-255 */ + int oldlvol,oldrvol; + + SLONGLONG current; /* current index in the sample */ + SLONGLONG increment; /* increment value */ +} VINFO; + +static SWORD **Samples; +static VINFO *vinf=NULL,*vnf; +static long tickleft,samplesthatfit,vc_memory=0; +static int vc_softchn; +static SLONGLONG idxsize,idxlpos,idxlend; +static SLONG *vc_tickbuf=NULL; +static UWORD vc_mode; + +/* Reverb control variables */ + +static int RVc1, RVc2, RVc3, RVc4, RVc5, RVc6, RVc7, RVc8; +static ULONG RVRindex; + +/* For Mono or Left Channel */ +static SLONG *RVbufL1=NULL,*RVbufL2=NULL,*RVbufL3=NULL,*RVbufL4=NULL, + *RVbufL5=NULL,*RVbufL6=NULL,*RVbufL7=NULL,*RVbufL8=NULL; + +/* For Stereo only (Right Channel) */ +static SLONG *RVbufR1=NULL,*RVbufR2=NULL,*RVbufR3=NULL,*RVbufR4=NULL, + *RVbufR5=NULL,*RVbufR6=NULL,*RVbufR7=NULL,*RVbufR8=NULL; + +#ifdef NATIVE_64BIT_INT +#define NATIVE SLONGLONG +#else +#define NATIVE SLONG +#endif + +/*========== 32 bit sample mixers - only for 32 bit platforms */ +#ifndef NATIVE_64BIT_INT + +static SLONG Mix32MonoNormal(SWORD* srce,SLONG* dest,SLONG index,SLONG increment,SLONG todo) +{ + SWORD sample=0; + SLONG i,f; + + while(todo--) { + i=index>>FRACBITS,f=index&FRACMASK; + sample=(((SLONG)(srce[i]*(FRACMASK+1L-f)) + + ((SLONG)srce[i+1]*f)) >> FRACBITS); + index+=increment; + + if(vnf->rampvol) { + *dest++ += (long)( + ( ( (SLONG)(vnf->oldlvol*vnf->rampvol) + + (vnf->lvolsel*(CLICK_BUFFER-vnf->rampvol)) ) * + (SLONG)sample ) >> CLICK_SHIFT ); + vnf->rampvol--; + } else + if(vnf->click) { + *dest++ += (long)( + ( ( ((SLONG)vnf->lvolsel*(CLICK_BUFFER-vnf->click)) * + (SLONG)sample ) + + (vnf->lastvalL*vnf->click) ) >> CLICK_SHIFT ); + vnf->click--; + } else + *dest++ +=vnf->lvolsel*sample; + } + vnf->lastvalL=vnf->lvolsel * sample; + + return index; +} + +static SLONG Mix32StereoNormal(SWORD* srce,SLONG* dest,SLONG index,SLONG increment,ULONG todo) +{ + SWORD sample=0; + SLONG i,f; + + while(todo--) { + i=index>>FRACBITS,f=index&FRACMASK; + sample=((((SLONG)srce[i]*(FRACMASK+1L-f)) + + ((SLONG)srce[i+1] * f)) >> FRACBITS); + index += increment; + + if(vnf->rampvol) { + *dest++ += (long)( + ( ( ((SLONG)vnf->oldlvol*vnf->rampvol) + + (vnf->lvolsel*(CLICK_BUFFER-vnf->rampvol)) + ) * (SLONG)sample ) >> CLICK_SHIFT ); + *dest++ += (long)( + ( ( ((SLONG)vnf->oldrvol*vnf->rampvol) + + (vnf->rvolsel*(CLICK_BUFFER-vnf->rampvol)) + ) * (SLONG)sample ) >> CLICK_SHIFT ); + vnf->rampvol--; + } else + if(vnf->click) { + *dest++ += (long)( + ( ( (SLONG)(vnf->lvolsel*(CLICK_BUFFER-vnf->click)) * + (SLONG)sample ) + (vnf->lastvalL * vnf->click) ) + >> CLICK_SHIFT ); + *dest++ += (long)( + ( ( ((SLONG)vnf->rvolsel*(CLICK_BUFFER-vnf->click)) * + (SLONG)sample ) + (vnf->lastvalR * vnf->click) ) + >> CLICK_SHIFT ); + vnf->click--; + } else { + *dest++ +=vnf->lvolsel*sample; + *dest++ +=vnf->rvolsel*sample; + } + } + vnf->lastvalL=vnf->lvolsel*sample; + vnf->lastvalR=vnf->rvolsel*sample; + + return index; +} + +static SLONG Mix32StereoSurround(SWORD* srce,SLONG* dest,SLONG index,SLONG increment,ULONG todo) +{ + SWORD sample=0; + long whoop; + SLONG i, f; + + while(todo--) { + i=index>>FRACBITS,f=index&FRACMASK; + sample=((((SLONG)srce[i]*(FRACMASK+1L-f)) + + ((SLONG)srce[i+1]*f)) >> FRACBITS); + index+=increment; + + if(vnf->rampvol) { + whoop=(long)( + ( ( (SLONG)(vnf->oldlvol*vnf->rampvol) + + (vnf->lvolsel*(CLICK_BUFFER-vnf->rampvol)) ) * + (SLONG)sample) >> CLICK_SHIFT ); + *dest++ +=whoop; + *dest++ -=whoop; + vnf->rampvol--; + } else + if(vnf->click) { + whoop = (long)( + ( ( ((SLONG)vnf->lvolsel*(CLICK_BUFFER-vnf->click)) * + (SLONG)sample) + + (vnf->lastvalL * vnf->click) ) >> CLICK_SHIFT ); + *dest++ +=whoop; + *dest++ -=whoop; + vnf->click--; + } else { + *dest++ +=vnf->lvolsel*sample; + *dest++ -=vnf->lvolsel*sample; + } + } + vnf->lastvalL=vnf->lvolsel*sample; + vnf->lastvalR=vnf->lvolsel*sample; + + return index; +} +#endif + +/*========== 64 bit mixers */ + +#ifndef NO_64BIT_MIXER + +static SLONGLONG MixMonoNormal(SWORD* srce,SLONG* dest,SLONGLONG index,SLONGLONG increment,SLONG todo) +{ + SWORD sample=0; + SLONGLONG i,f; + + while(todo--) { + i=index>>FRACBITS,f=index&FRACMASK; + sample=(((SLONGLONG)(srce[i]*(FRACMASK+1L-f)) + + ((SLONGLONG)srce[i+1]*f)) >> FRACBITS); + index+=increment; + + if(vnf->rampvol) { + *dest++ += (long)( + ( ( (SLONGLONG)(vnf->oldlvol*vnf->rampvol) + + (vnf->lvolsel*(CLICK_BUFFER-vnf->rampvol)) ) * + (SLONGLONG)sample ) >> CLICK_SHIFT ); + vnf->rampvol--; + } else + if(vnf->click) { + *dest++ += (long)( + ( ( ((SLONGLONG)vnf->lvolsel*(CLICK_BUFFER-vnf->click)) * + (SLONGLONG)sample ) + + (vnf->lastvalL*vnf->click) ) >> CLICK_SHIFT ); + vnf->click--; + } else + *dest++ +=vnf->lvolsel*sample; + } + vnf->lastvalL=vnf->lvolsel * sample; + + return index; +} + +static SLONGLONG MixStereoNormal(SWORD* srce,SLONG* dest,SLONGLONG index,SLONGLONG increment,ULONG todo) +{ + SWORD sample=0; + SLONGLONG i,f; + + while(todo--) { + i=index>>FRACBITS,f=index&FRACMASK; + sample=((((SLONGLONG)srce[i]*(FRACMASK+1L-f)) + + ((SLONGLONG)srce[i+1] * f)) >> FRACBITS); + index += increment; + + if(vnf->rampvol) { + *dest++ += (long)( + ( ( ((SLONGLONG)vnf->oldlvol*vnf->rampvol) + + (vnf->lvolsel*(CLICK_BUFFER-vnf->rampvol)) + ) * (SLONGLONG)sample ) >> CLICK_SHIFT ); + *dest++ += (long)( + ( ( ((SLONGLONG)vnf->oldrvol*vnf->rampvol) + + (vnf->rvolsel*(CLICK_BUFFER-vnf->rampvol)) + ) * (SLONGLONG)sample ) >> CLICK_SHIFT ); + vnf->rampvol--; + } else + if(vnf->click) { + *dest++ += (long)( + ( ( (SLONGLONG)(vnf->lvolsel*(CLICK_BUFFER-vnf->click)) * + (SLONGLONG)sample ) + (vnf->lastvalL * vnf->click) ) + >> CLICK_SHIFT ); + *dest++ += (long)( + ( ( ((SLONGLONG)vnf->rvolsel*(CLICK_BUFFER-vnf->click)) * + (SLONGLONG)sample ) + (vnf->lastvalR * vnf->click) ) + >> CLICK_SHIFT ); + vnf->click--; + } else { + *dest++ +=vnf->lvolsel*sample; + *dest++ +=vnf->rvolsel*sample; + } + } + vnf->lastvalL=vnf->lvolsel*sample; + vnf->lastvalR=vnf->rvolsel*sample; + + return index; +} + +static SLONGLONG MixStereoSurround(SWORD* srce,SLONG* dest,SLONGLONG index,SLONGLONG increment,ULONG todo) +{ + SWORD sample=0; + long whoop; + SLONGLONG i, f; + + while(todo--) { + i=index>>FRACBITS,f=index&FRACMASK; + sample=((((SLONGLONG)srce[i]*(FRACMASK+1L-f)) + + ((SLONGLONG)srce[i+1]*f)) >> FRACBITS); + index+=increment; + + if(vnf->rampvol) { + whoop=(long)( + ( ( (SLONGLONG)(vnf->oldlvol*vnf->rampvol) + + (vnf->lvolsel*(CLICK_BUFFER-vnf->rampvol)) ) * + (SLONGLONG)sample) >> CLICK_SHIFT ); + *dest++ +=whoop; + *dest++ -=whoop; + vnf->rampvol--; + } else + if(vnf->click) { + whoop = (long)( + ( ( ((SLONGLONG)vnf->lvolsel*(CLICK_BUFFER-vnf->click)) * + (SLONGLONG)sample) + + (vnf->lastvalL * vnf->click) ) >> CLICK_SHIFT ); + *dest++ +=whoop; + *dest++ -=whoop; + vnf->click--; + } else { + *dest++ +=vnf->lvolsel*sample; + *dest++ -=vnf->lvolsel*sample; + } + } + vnf->lastvalL=vnf->lvolsel*sample; + vnf->lastvalR=vnf->lvolsel*sample; + + return index; +} + +#endif /* NO_64BIT_MIXER */ + +static void(*Mix32to16)(SWORD* dste,SLONG* srce,NATIVE count); +static void(*Mix32to8)(SBYTE* dste,SLONG* srce,NATIVE count); +static void(*MixReverb)(SLONG* srce,NATIVE count); + +/* Reverb macros */ +#define COMPUTE_LOC(n) loc##n = RVRindex % RVc##n +#define COMPUTE_LECHO(n) RVbufL##n [loc##n ]=speedup+((ReverbPct*RVbufL##n [loc##n ])>>7) +#define COMPUTE_RECHO(n) RVbufR##n [loc##n ]=speedup+((ReverbPct*RVbufR##n [loc##n ])>>7) + +static void MixReverb_Normal(SLONG* srce,NATIVE count) +{ + NATIVE speedup; + int ReverbPct; + unsigned int loc1,loc2,loc3,loc4,loc5,loc6,loc7,loc8; + + ReverbPct=58+(md_reverb*4); + + COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); + COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + + while(count--) { + /* Compute the left channel echo buffers */ + speedup = *srce >> 3; + + COMPUTE_LECHO(1); COMPUTE_LECHO(2); COMPUTE_LECHO(3); COMPUTE_LECHO(4); + COMPUTE_LECHO(5); COMPUTE_LECHO(6); COMPUTE_LECHO(7); COMPUTE_LECHO(8); + + /* Prepare to compute actual finalized data */ + RVRindex++; + + COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); + COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + + /* left channel */ + *srce++ +=RVbufL1[loc1]-RVbufL2[loc2]+RVbufL3[loc3]-RVbufL4[loc4]+ + RVbufL5[loc5]-RVbufL6[loc6]+RVbufL7[loc7]-RVbufL8[loc8]; + } +} + +static void MixReverb_Stereo(SLONG *srce,NATIVE count) +{ + NATIVE speedup; + int ReverbPct; + unsigned int loc1,loc2,loc3,loc4,loc5,loc6,loc7,loc8; + + ReverbPct=58+(md_reverb*4); + + COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); + COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + + while(count--) { + /* Compute the left channel echo buffers */ + speedup = *srce >> 3; + + COMPUTE_LECHO(1); COMPUTE_LECHO(2); COMPUTE_LECHO(3); COMPUTE_LECHO(4); + COMPUTE_LECHO(5); COMPUTE_LECHO(6); COMPUTE_LECHO(7); COMPUTE_LECHO(8); + + /* Compute the right channel echo buffers */ + speedup = srce[1] >> 3; + + COMPUTE_RECHO(1); COMPUTE_RECHO(2); COMPUTE_RECHO(3); COMPUTE_RECHO(4); + COMPUTE_RECHO(5); COMPUTE_RECHO(6); COMPUTE_RECHO(7); COMPUTE_RECHO(8); + + /* Prepare to compute actual finalized data */ + RVRindex++; + + COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); + COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + + /* left channel */ + *srce++ +=RVbufL1[loc1]-RVbufL2[loc2]+RVbufL3[loc3]-RVbufL4[loc4]+ + RVbufL5[loc5]-RVbufL6[loc6]+RVbufL7[loc7]-RVbufL8[loc8]; + + /* right channel */ + *srce++ +=RVbufR1[loc1]-RVbufR2[loc2]+RVbufR3[loc3]-RVbufR4[loc4]+ + RVbufR5[loc5]-RVbufR6[loc6]+RVbufR7[loc7]-RVbufR8[loc8]; + } +} + +/* Mixing macros */ +#define EXTRACT_SAMPLE(var,attenuation) var=*srce++/(MAXVOL_FACTOR*attenuation) +#define CHECK_SAMPLE(var,bound) var=(var>=bound)?bound-1:(var<-bound)?-bound:var + +static void Mix32To16_Normal(SWORD* dste,SLONG* srce,NATIVE count) +{ + NATIVE x1,x2,tmpx; + int i; + + for(count/=SAMPLING_FACTOR;count;count--) { + tmpx=0; + + for(i=SAMPLING_FACTOR/2;i;i--) { + EXTRACT_SAMPLE(x1,1); EXTRACT_SAMPLE(x2,1); + + CHECK_SAMPLE(x1,32768); CHECK_SAMPLE(x2,32768); + + tmpx+=x1+x2; + } + *dste++ =tmpx/SAMPLING_FACTOR; + } +} + +static void Mix32To16_Stereo(SWORD* dste,SLONG* srce,NATIVE count) +{ + NATIVE x1,x2,x3,x4,tmpx,tmpy; + int i; + + for(count/=SAMPLING_FACTOR;count;count--) { + tmpx=tmpy=0; + + for(i=SAMPLING_FACTOR/2;i;i--) { + EXTRACT_SAMPLE(x1,1); EXTRACT_SAMPLE(x2,1); + EXTRACT_SAMPLE(x3,1); EXTRACT_SAMPLE(x4,1); + + CHECK_SAMPLE(x1,32768); CHECK_SAMPLE(x2,32768); + CHECK_SAMPLE(x3,32768); CHECK_SAMPLE(x4,32768); + + tmpx+=x1+x3; + tmpy+=x2+x4; + } + *dste++ =tmpx/SAMPLING_FACTOR; + *dste++ =tmpy/SAMPLING_FACTOR; + } +} + +static void Mix32To8_Normal(SBYTE* dste,SLONG* srce,NATIVE count) +{ + NATIVE x1,x2,tmpx; + int i; + + for(count/=SAMPLING_FACTOR;count;count--) { + tmpx = 0; + + for(i=SAMPLING_FACTOR/2;i;i--) { + EXTRACT_SAMPLE(x1,256); EXTRACT_SAMPLE(x2,256); + + CHECK_SAMPLE(x1,128); CHECK_SAMPLE(x2,128); + + tmpx+=x1+x2; + } + *dste++ =(tmpx/SAMPLING_FACTOR)+128; + } +} + +static void Mix32To8_Stereo(SBYTE* dste,SLONG* srce,NATIVE count) +{ + NATIVE x1,x2,x3,x4,tmpx,tmpy; + int i; + + for(count/=SAMPLING_FACTOR;count;count--) { + tmpx=tmpy=0; + + for(i=SAMPLING_FACTOR/2;i;i--) { + EXTRACT_SAMPLE(x1,256); EXTRACT_SAMPLE(x2,256); + EXTRACT_SAMPLE(x3,256); EXTRACT_SAMPLE(x4,256); + + CHECK_SAMPLE(x1,128); CHECK_SAMPLE(x2,128); + CHECK_SAMPLE(x3,128); CHECK_SAMPLE(x4,128); + + tmpx+=x1+x3; + tmpy+=x2+x4; + } + *dste++ =(tmpx/SAMPLING_FACTOR)+128; + *dste++ =(tmpy/SAMPLING_FACTOR)+128; + } +} + +static void AddChannel(SLONG* ptr,NATIVE todo) +{ + SLONGLONG end,done; + SWORD *s; + + if(!(s=Samples[vnf->handle])) { + vnf->current = vnf->active = 0; + vnf->lastvalL = vnf->lastvalR = 0; + return; + } + + /* update the 'current' index so the sample loops, or stops playing if it + reached the end of the sample */ + while(todo>0) { + SLONGLONG endpos; + + if(vnf->flags & SF_REVERSE) { + /* The sample is playing in reverse */ + if((vnf->flags&SF_LOOP)&&(vnf->currentflags & SF_BIDI) { + /* sample is doing bidirectional loops, so 'bounce' the + current index against the idxlpos */ + vnf->current = idxlpos+(idxlpos-vnf->current); + vnf->flags &= ~SF_REVERSE; + vnf->increment = -vnf->increment; + } else + /* normal backwards looping, so set the current position to + loopend index */ + vnf->current=idxlend-(idxlpos-vnf->current); + } else { + /* the sample is not looping, so check if it reached index 0 */ + if(vnf->current < 0) { + /* playing index reached 0, so stop playing this sample */ + vnf->current = vnf->active = 0; + break; + } + } + } else { + /* The sample is playing forward */ + if((vnf->flags & SF_LOOP) && (vnf->current >= idxlend)) { + /* the sample is looping, check the loopend index */ + if(vnf->flags & SF_BIDI) { + /* sample is doing bidirectional loops, so 'bounce' the + current index against the idxlend */ + vnf->flags |= SF_REVERSE; + vnf->increment = -vnf->increment; + vnf->current = idxlend-(vnf->current-idxlend); + } else + /* normal backwards looping, so set the current position + to loopend index */ + vnf->current=idxlpos+(vnf->current-idxlend); + } else { + /* sample is not looping, so check if it reached the last + position */ + if(vnf->current >= idxsize) { + /* yes, so stop playing this sample */ + vnf->current = vnf->active = 0; + break; + } + } + } + + end=(vnf->flags&SF_REVERSE)?(vnf->flags&SF_LOOP)?idxlpos:0: + (vnf->flags&SF_LOOP)?idxlend:idxsize; + + /* if the sample is not blocked... */ + if((end==vnf->current)||(!vnf->increment)) + done=0; + else { + done=MIN((end-vnf->current)/vnf->increment+1,todo); + if(done<0) done=0; + } + + if(!done) { + vnf->active = 0; + break; + } + + endpos=vnf->current+done*vnf->increment; + + if(vnf->vol || vnf->rampvol) { +#ifndef NATIVE_64BIT_INT + /* use the 32 bit mixers as often as we can (they're much faster) */ + if((vnf->current<0x7fffffff)&&(endpos<0x7fffffff)) { + if(vc_mode & DMODE_STEREO) { + if((vnf->pan==PAN_SURROUND)&&(vc_mode&DMODE_SURROUND)) + vnf->current=Mix32StereoSurround + (s,ptr,vnf->current,vnf->increment,done); + else + vnf->current=Mix32StereoNormal + (s,ptr,vnf->current,vnf->increment,done); + } else + vnf->current=Mix32MonoNormal + (s,ptr,vnf->current,vnf->increment,done); + } else +#endif +#ifdef NO_64BIT_MIXER + /* Uh oh, the 64-bit mixers don't compile... */; +#else + { + if(vc_mode & DMODE_STEREO) { + if((vnf->pan==PAN_SURROUND)&&(vc_mode&DMODE_SURROUND)) + vnf->current=MixStereoSurround + (s,ptr,vnf->current,vnf->increment,done); + else + vnf->current=MixStereoNormal + (s,ptr,vnf->current,vnf->increment,done); + } else + vnf->current=MixMonoNormal + (s,ptr,vnf->current,vnf->increment,done); + } +#endif + } else { + vnf->lastvalL = vnf->lastvalR = 0; + /* update sample position */ + vnf->current=endpos; + } + + todo -= done; +#if 1 + if ( vc_mode & DMODE_STEREO ) { + ptr += done*2; + } else { + ptr += done; + } +#else + ptr +=(vc_mode & DMODE_STEREO)?(done<<1):done; +#endif + } +} + +#define _IN_VIRTCH_ + +#define VC1_SilenceBytes VC2_SilenceBytes +#define VC1_WriteSamples VC2_WriteSamples +#define VC1_WriteBytes VC2_WriteBytes +#define VC1_Exit VC2_Exit +#define VC1_VoiceSetVolume VC2_VoiceSetVolume +#define VC1_VoiceGetVolume VC2_VoiceGetVolume +#define VC1_VoiceSetPanning VC2_VoiceSetPanning +#define VC1_VoiceGetPanning VC2_VoiceGetPanning +#define VC1_VoiceSetFrequency VC2_VoiceSetFrequency +#define VC1_VoiceGetFrequency VC2_VoiceGetFrequency +#define VC1_VoicePlay VC2_VoicePlay +#define VC1_VoiceStop VC2_VoiceStop +#define VC1_VoiceStopped VC2_VoiceStopped +#define VC1_VoiceGetPosition VC2_VoiceGetPosition +#define VC1_SampleUnload VC2_SampleUnload +#define VC1_SampleLoad VC2_SampleLoad +#define VC1_SampleSpace VC2_SampleSpace +#define VC1_SampleLength VC2_SampleLength +#define VC1_VoiceRealVolume VC2_VoiceRealVolume + +#include "virtch_common.c" +#undef _IN_VIRTCH_ + +void VC2_WriteSamples(SBYTE* buf,ULONG todo) +{ + int left,portion=0; + SBYTE *buffer; + int t,pan,vol; + + todo*=SAMPLING_FACTOR; + + while(todo) { + if(!tickleft) { + if(vc_mode & DMODE_SOFT_MUSIC) md_player(); + tickleft=(md_mixfreq*125L*SAMPLING_FACTOR)/(md_bpm*50L); + tickleft&=~(SAMPLING_FACTOR-1); + } + left = MIN(tickleft, todo); + buffer = buf; + tickleft -= left; + todo -= left; + buf += samples2bytes(left)/SAMPLING_FACTOR; + + while(left) { + portion = MIN(left, samplesthatfit); + memset(vc_tickbuf,0,portion<<((vc_mode&DMODE_STEREO)?3:2)); + for(t=0;tkick) { + vnf->current=((SLONGLONG)(vnf->start))<kick = 0; + vnf->active = 1; + vnf->click = CLICK_BUFFER; + vnf->rampvol = 0; + } + + if(!vnf->frq) vnf->active = 0; + + if(vnf->active) { + vnf->increment=((SLONGLONG)(vnf->frq)<<(FRACBITS-SAMPLING_SHIFT)) + /md_mixfreq; + if(vnf->flags&SF_REVERSE) vnf->increment=-vnf->increment; + vol = vnf->vol; pan = vnf->pan; + + vnf->oldlvol=vnf->lvolsel;vnf->oldrvol=vnf->rvolsel; + if(vc_mode & DMODE_STEREO) { + if(pan!=PAN_SURROUND) { + vnf->lvolsel=(vol*(PAN_RIGHT-pan))>>8; + vnf->rvolsel=(vol*pan)>>8; + } else { + vnf->lvolsel=vnf->rvolsel=(vol * 256L) / 480; + } + } else + vnf->lvolsel=vol; + + idxsize=(vnf->size)?((SLONGLONG)(vnf->size)<repend)?((SLONGLONG)(vnf->repend)<reppos)<15) md_reverb=15; + MixReverb(vc_tickbuf,portion); + } + + if(vc_mode & DMODE_16BITS) + Mix32to16((SWORD*)buffer,vc_tickbuf,portion); + else + Mix32to8((SBYTE*)buffer,vc_tickbuf,portion); + + buffer += samples2bytes(portion) / SAMPLING_FACTOR; + left -= portion; + } + } +} + +BOOL VC2_Init(void) +{ + VC_SetupPointers(); + + if (!(md_mode&DMODE_HQMIXER)) + return VC1_Init(); + + if(!(Samples=(SWORD**)_mm_calloc(MAXSAMPLEHANDLES,sizeof(SWORD*)))) { + _mm_errno = MMERR_INITIALIZING_MIXER; + return 1; + } + if(!vc_tickbuf) + if(!(vc_tickbuf=(SLONG*)_mm_malloc((TICKLSIZE+32)*sizeof(SLONG)))) { + _mm_errno = MMERR_INITIALIZING_MIXER; + return 1; + } + + if(md_mode & DMODE_STEREO) { + Mix32to16 = Mix32To16_Stereo; + Mix32to8 = Mix32To8_Stereo; + MixReverb = MixReverb_Stereo; + } else { + Mix32to16 = Mix32To16_Normal; + Mix32to8 = Mix32To8_Normal; + MixReverb = MixReverb_Normal; + } + md_mode |= DMODE_INTERP; + vc_mode = md_mode; + return 0; +} + +BOOL VC2_PlayStart(void) +{ + md_mode|=DMODE_INTERP; + + samplesthatfit = TICKLSIZE; + if(vc_mode & DMODE_STEREO) samplesthatfit >>= 1; + tickleft = 0; + + RVc1 = (5000L * md_mixfreq) / (REVERBERATION * 10); + RVc2 = (5078L * md_mixfreq) / (REVERBERATION * 10); + RVc3 = (5313L * md_mixfreq) / (REVERBERATION * 10); + RVc4 = (5703L * md_mixfreq) / (REVERBERATION * 10); + RVc5 = (6250L * md_mixfreq) / (REVERBERATION * 10); + RVc6 = (6953L * md_mixfreq) / (REVERBERATION * 10); + RVc7 = (7813L * md_mixfreq) / (REVERBERATION * 10); + RVc8 = (8828L * md_mixfreq) / (REVERBERATION * 10); + + if(!(RVbufL1=(SLONG*)_mm_calloc((RVc1+1),sizeof(SLONG)))) return 1; + if(!(RVbufL2=(SLONG*)_mm_calloc((RVc2+1),sizeof(SLONG)))) return 1; + if(!(RVbufL3=(SLONG*)_mm_calloc((RVc3+1),sizeof(SLONG)))) return 1; + if(!(RVbufL4=(SLONG*)_mm_calloc((RVc4+1),sizeof(SLONG)))) return 1; + if(!(RVbufL5=(SLONG*)_mm_calloc((RVc5+1),sizeof(SLONG)))) return 1; + if(!(RVbufL6=(SLONG*)_mm_calloc((RVc6+1),sizeof(SLONG)))) return 1; + if(!(RVbufL7=(SLONG*)_mm_calloc((RVc7+1),sizeof(SLONG)))) return 1; + if(!(RVbufL8=(SLONG*)_mm_calloc((RVc8+1),sizeof(SLONG)))) return 1; + + if(!(RVbufR1=(SLONG*)_mm_calloc((RVc1+1),sizeof(SLONG)))) return 1; + if(!(RVbufR2=(SLONG*)_mm_calloc((RVc2+1),sizeof(SLONG)))) return 1; + if(!(RVbufR3=(SLONG*)_mm_calloc((RVc3+1),sizeof(SLONG)))) return 1; + if(!(RVbufR4=(SLONG*)_mm_calloc((RVc4+1),sizeof(SLONG)))) return 1; + if(!(RVbufR5=(SLONG*)_mm_calloc((RVc5+1),sizeof(SLONG)))) return 1; + if(!(RVbufR6=(SLONG*)_mm_calloc((RVc6+1),sizeof(SLONG)))) return 1; + if(!(RVbufR7=(SLONG*)_mm_calloc((RVc7+1),sizeof(SLONG)))) return 1; + if(!(RVbufR8=(SLONG*)_mm_calloc((RVc8+1),sizeof(SLONG)))) return 1; + + RVRindex = 0; + return 0; +} + +void VC2_PlayStop(void) +{ + if(RVbufL1) free(RVbufL1); + if(RVbufL2) free(RVbufL2); + if(RVbufL3) free(RVbufL3); + if(RVbufL4) free(RVbufL4); + if(RVbufL5) free(RVbufL5); + if(RVbufL6) free(RVbufL6); + if(RVbufL7) free(RVbufL7); + if(RVbufL8) free(RVbufL8); + if(RVbufR1) free(RVbufR1); + if(RVbufR2) free(RVbufR2); + if(RVbufR3) free(RVbufR3); + if(RVbufR4) free(RVbufR4); + if(RVbufR5) free(RVbufR5); + if(RVbufR6) free(RVbufR6); + if(RVbufR7) free(RVbufR7); + if(RVbufR8) free(RVbufR8); + + RVbufL1=RVbufL2=RVbufL3=RVbufL4=RVbufL5=RVbufL6=RVbufL7=RVbufL8=NULL; + RVbufR1=RVbufR2=RVbufR3=RVbufR4=RVbufR5=RVbufR6=RVbufR7=RVbufR8=NULL; +} + +BOOL VC2_SetNumVoices(void) +{ + int t; + + md_mode|=DMODE_INTERP; + + if(!(vc_softchn=md_softchn)) return 0; + + if(vinf) free(vinf); + if(!(vinf=_mm_calloc(sizeof(VINFO),vc_softchn))) return 1; + + for(t=0;t>= 1; + if(vc_mode & DMODE_STEREO) bytes >>= 1; + return bytes; +} + +/* Fill the buffer with 'todo' bytes of silence (it depends on the mixing mode + how the buffer is filled) */ +ULONG VC1_SilenceBytes(SBYTE* buf,ULONG todo) +{ + todo=samples2bytes(bytes2samples(todo)); + + /* clear the buffer to zero (16 bits signed) or 0x80 (8 bits unsigned) */ + if(vc_mode & DMODE_16BITS) + memset(buf,0,todo); + else + memset(buf,0x80,todo); + + return todo; +} + +void VC1_WriteSamples(SBYTE*,ULONG); + +/* Writes 'todo' mixed SBYTES (!!) to 'buf'. It returns the number of SBYTES + actually written to 'buf' (which is rounded to number of samples that fit + into 'todo' bytes). */ +ULONG VC1_WriteBytes(SBYTE* buf,ULONG todo) +{ + if(!vc_softchn) + return VC1_SilenceBytes(buf,todo); + + todo = bytes2samples(todo); + VC1_WriteSamples(buf,todo); + + return samples2bytes(todo); +} + +void VC1_Exit(void) +{ + if(vc_tickbuf) free(vc_tickbuf); + if(vinf) free(vinf); + if(Samples) free(Samples); + + vc_tickbuf = NULL; + vinf = NULL; + Samples = NULL; + + VC_SetupPointers(); +} + +UWORD VC1_VoiceGetVolume(UBYTE voice) +{ + return vinf[voice].vol; +} + +ULONG VC1_VoiceGetPanning(UBYTE voice) +{ + return vinf[voice].pan; +} + +void VC1_VoiceSetFrequency(UBYTE voice,ULONG frq) +{ + vinf[voice].frq=frq; +} + +ULONG VC1_VoiceGetFrequency(UBYTE voice) +{ + return vinf[voice].frq; +} + +void VC1_VoicePlay(UBYTE voice,SWORD handle,ULONG start,ULONG size,ULONG reppos,ULONG repend,UWORD flags) +{ + vinf[voice].flags = flags; + vinf[voice].handle = handle; + vinf[voice].start = start; + vinf[voice].size = size; + vinf[voice].reppos = reppos; + vinf[voice].repend = repend; + vinf[voice].kick = 1; +} + +void VC1_VoiceStop(UBYTE voice) +{ + vinf[voice].active = 0; +} + +BOOL VC1_VoiceStopped(UBYTE voice) +{ + return(vinf[voice].active==0); +} + +SLONG VC1_VoiceGetPosition(UBYTE voice) +{ + return(vinf[voice].current>>FRACBITS); +} + +void VC1_VoiceSetVolume(UBYTE voice,UWORD vol) +{ + /* protect against clicks if volume variation is too high */ + if(abs((int)vinf[voice].vol-(int)vol)>32) + vinf[voice].rampvol=CLICK_BUFFER; + vinf[voice].vol=vol; +} + +void VC1_VoiceSetPanning(UBYTE voice,ULONG pan) +{ + /* protect against clicks if panning variation is too high */ + if(abs((int)vinf[voice].pan-(int)pan)>48) + vinf[voice].rampvol=CLICK_BUFFER; + vinf[voice].pan=pan; +} + +/*========== External mixer interface */ + +void VC1_SampleUnload(SWORD handle) +{ + if (Samples && handlesample; + int handle; + ULONG t, length,loopstart,loopend; + + if(type==MD_HARDWARE) return -1; + + /* Find empty slot to put sample address in */ + for(handle=0;handlelength; + loopstart = s->loopstart; + loopend = s->loopend; + + SL_SampleSigned(sload); + SL_Sample8to16(sload); + + if(!(Samples[handle]=(SWORD*)_mm_malloc((length+20)<<1))) { + _mm_errno = MMERR_SAMPLE_TOO_BIG; + return -1; + } + + /* read sample into buffer */ + if (SL_Load(Samples[handle],sload,length)) + return -1; + + /* Unclick sample */ + if(s->flags & SF_LOOP) { + if(s->flags & SF_BIDI) + for(t=0;t<16;t++) + Samples[handle][loopend+t]=Samples[handle][(loopend-t)-1]; + else + for(t=0;t<16;t++) + Samples[handle][loopend+t]=Samples[handle][t+loopstart]; + } else + for(t=0;t<16;t++) + Samples[handle][t+length]=0; + + return handle; +} + +ULONG VC1_SampleSpace(int type) +{ + return vc_memory; +} + +ULONG VC1_SampleLength(int type,SAMPLE* s) +{ + if (!s) return 0; + + return (s->length*((s->flags&SF_16BITS)?2:1))+16; +} + +ULONG VC1_VoiceRealVolume(UBYTE voice) +{ + ULONG i,s,size; + int k,j; + SWORD *smp; + SLONG t; + + t = vinf[voice].current>>FRACBITS; + if(!vinf[voice].active) return 0; + + s = vinf[voice].handle; + size = vinf[voice].size; + + i=64; t-=64; k=0; j=0; + if(i>size) i = size; + if(t<0) t = 0; + if(t+i > size) t = size-i; + + i &= ~1; /* make sure it's EVEN. */ + + smp = &Samples[s][t]; + for(;i;i--,smp++) { + if(k<*smp) k = *smp; + if(j>*smp) j = *smp; + } + return abs(k-j); +} + +#endif + +#endif + +/* ex:set ts=4: */ diff --git a/sc2/src/sc2code/libs/sound/decoders/wav.c b/sc2/src/sc2code/libs/sound/decoders/wav.c new file mode 100644 index 000000000..81b16bf5d --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/wav.c @@ -0,0 +1,184 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2000 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +/* NOTE by Mika: Wav loader, (c) Creative, taken from their OpenAL + * implementation and modified. + */ + +#include +#include +#include +#include "libs/misc.h" +#include "wav.h" +#include "decoder.h" + +#if defined _MSC_VER + #pragma pack (push,1) /* Turn off alignment */ +#elif defined __GNUC__ + #define PADOFF_VAR __attribute__((packed)) +#endif + +#ifndef PADOFF_VAR + #define PADOFF_VAR +#endif + +typedef struct /* WAV File-header */ +{ + uint8 Id[4] PADOFF_VAR; + sint32 Size PADOFF_VAR; + uint8 Type[4] PADOFF_VAR; +} WAVFileHdr_Struct; + +typedef struct /* WAV Fmt-header */ +{ + uint16 Format PADOFF_VAR; + uint16 Channels PADOFF_VAR; + uint32 SamplesPerSec PADOFF_VAR; + uint32 BytesPerSec PADOFF_VAR; + uint16 BlockAlign PADOFF_VAR; + uint16 BitsPerSample PADOFF_VAR; +} WAVFmtHdr_Struct; + +typedef struct /* WAV FmtEx-header */ +{ + uint16 Size PADOFF_VAR; + uint16 SamplesPerBlock PADOFF_VAR; +} WAVFmtExHdr_Struct; + +typedef struct /* WAV Smpl-header */ +{ + uint32 Manufacturer PADOFF_VAR; + uint32 Product PADOFF_VAR; + uint32 SamplePeriod PADOFF_VAR; + uint32 Note PADOFF_VAR; + uint32 FineTune PADOFF_VAR; + uint32 SMPTEFormat PADOFF_VAR; + uint32 SMPTEOffest PADOFF_VAR; + uint32 Loops PADOFF_VAR; + uint32 SamplerData PADOFF_VAR; + struct + { + uint32 Identifier PADOFF_VAR; + uint32 Type PADOFF_VAR; + uint32 Start PADOFF_VAR; + uint32 End PADOFF_VAR; + uint32 Fraction PADOFF_VAR; + uint32 Count PADOFF_VAR; + } Loop[1] PADOFF_VAR; +} WAVSmplHdr_Struct; + +typedef struct /* WAV Chunk-header */ +{ + uint8 Id[4] PADOFF_VAR; + uint32 Size PADOFF_VAR; +} WAVChunkHdr_Struct; + + +#ifdef PADOFF_VAR /* Default alignment */ + #undef PADOFF_VAR +#endif + +#if defined _MSC_VER + #pragma pack (pop) +#endif + +/* loads the WAV file in little endian byte order */ +void LoadWAVFile (char *file, uint32 *format, void **data, uint32 *size, uint32 *freq, bool *loop) +{ + WAVChunkHdr_Struct ChunkHdr; + WAVFmtExHdr_Struct FmtExHdr; + WAVFileHdr_Struct FileHdr; + WAVSmplHdr_Struct SmplHdr; + WAVFmtHdr_Struct FmtHdr; + FILE *Stream; + + *format=decoder_formats.mono16; + *data=NULL; + *size=0; + *freq=22050; + *loop=false; + if (file) + { + Stream=fopen(file,"rb"); + if (Stream) + { + fread(&FileHdr,1,sizeof(WAVFileHdr_Struct),Stream); + FileHdr.Size=((FileHdr.Size+1)&~1)-4; + while ((FileHdr.Size!=0)&&(fread(&ChunkHdr,1,sizeof(WAVChunkHdr_Struct),Stream))) + { + if (!memcmp(ChunkHdr.Id,"fmt ",4)) + { + fread(&FmtHdr,1,sizeof(WAVFmtHdr_Struct),Stream); + if (FmtHdr.Format==0x0001) + { + *format=(FmtHdr.Channels == 1 ? + (FmtHdr.BitsPerSample == 8 ? + decoder_formats.mono8 + : decoder_formats.mono16) + : (FmtHdr.BitsPerSample == 8 ? + decoder_formats.stereo8 + : decoder_formats.stereo16) + ); + *freq=FmtHdr.SamplesPerSec; + fseek(Stream,ChunkHdr.Size-sizeof(WAVFmtHdr_Struct),SEEK_CUR); + } + else + { + fread(&FmtExHdr,1,sizeof(WAVFmtExHdr_Struct),Stream); + fseek(Stream,ChunkHdr.Size-sizeof(WAVFmtHdr_Struct)- + sizeof(WAVFmtExHdr_Struct),SEEK_CUR); + } + } + else if (!memcmp(ChunkHdr.Id,"data",4)) + { + if (FmtHdr.Format==0x0001) + { + *size=ChunkHdr.Size; + *data=HMalloc(ChunkHdr.Size+31); + if (*data) + fread(*data,FmtHdr.BlockAlign, + ChunkHdr.Size/FmtHdr.BlockAlign,Stream); + memset(((char *)*data)+ChunkHdr.Size,0,31); + } + else if (FmtHdr.Format==0x0011) + { + //IMA ADPCM + } + else if (FmtHdr.Format==0x0055) + { + //MP3 WAVE + } + } + else if (!memcmp(ChunkHdr.Id,"smpl",4)) + { + fread(&SmplHdr,1,sizeof(WAVSmplHdr_Struct),Stream); + *loop = (SmplHdr.Loops ? true : false); + fseek(Stream,ChunkHdr.Size-sizeof(WAVSmplHdr_Struct),SEEK_CUR); + } + else + fseek(Stream,ChunkHdr.Size,SEEK_CUR); + + fseek(Stream,ChunkHdr.Size&1,SEEK_CUR); + FileHdr.Size-=(((ChunkHdr.Size+1)&~1)+8); + } + fclose(Stream); + } + } +} diff --git a/sc2/src/sc2code/libs/sound/decoders/wav.h b/sc2/src/sc2code/libs/sound/decoders/wav.h new file mode 100644 index 000000000..b9b85a6fc --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/wav.h @@ -0,0 +1,33 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2000 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +/* NOTE by Mika: Wav loader, (c) Creative, taken from their OpenAL + * implementation and modified. + */ + +#ifndef WAV_H +#define WAV_H + +#include "types.h" + +/* loads the WAV file in little endian byte order */ +void LoadWAVFile (char *file, uint32 *format, void **data, uint32 *size, uint32 *freq, bool *loop); + +#endif diff --git a/sc2/src/sc2code/libs/sound/openal/mixer.h b/sc2/src/sc2code/libs/sound/openal/mixer.h new file mode 100644 index 000000000..1ac25ec44 --- /dev/null +++ b/sc2/src/sc2code/libs/sound/openal/mixer.h @@ -0,0 +1,38 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* Adaptation layer + */ + +#ifndef MIXER_H +#define MIXER_H + +#include "types.h" +#include "SDL_byteorder.h" + +/** + * This is just a simple endianness setup for the mixer + */ + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +# define MIX_IS_BIG_ENDIAN true +# define MIX_WANT_BIG_ENDIAN false +#else +# define MIX_IS_BIG_ENDIAN false +# define MIX_WANT_BIG_ENDIAN false +#endif + +#endif /* MIXER_H */