From 7f6002caba3f0a6749820c2772161caf55b8d267 Mon Sep 17 00:00:00 2001 From: neonloop Date: Fri, 7 May 2021 20:00:12 +0000 Subject: Initial commit (uqm-0.8.0) --- src/libs/sound/decoders/modaud.c | 430 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 430 insertions(+) create mode 100644 src/libs/sound/decoders/modaud.c (limited to 'src/libs/sound/decoders/modaud.c') diff --git a/src/libs/sound/decoders/modaud.c b/src/libs/sound/decoders/modaud.c new file mode 100644 index 0000000..18c29a2 --- /dev/null +++ b/src/libs/sound/decoders/modaud.c @@ -0,0 +1,430 @@ +/* + * 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. + */ + +/* MikMod decoder (.mod adapter) + */ + +#include +#include +#include +#include "libs/memlib.h" +#include "port.h" +#include "types.h" +#include "endian_uqm.h" +#include "libs/uio.h" +#include "decoder.h" +#include "libs/sound/audiocore.h" +#include "libs/log.h" +#include "modaud.h" + +#ifdef USE_INTERNAL_MIKMOD +# include "libs/mikmod/mikmod.h" +#else +# include +#endif + +#define THIS_PTR TFB_SoundDecoder* This + +static const char* moda_GetName (void); +static bool moda_InitModule (int flags, const TFB_DecoderFormats*); +static void moda_TermModule (void); +static uint32 moda_GetStructSize (void); +static int moda_GetError (THIS_PTR); +static bool moda_Init (THIS_PTR); +static void moda_Term (THIS_PTR); +static bool moda_Open (THIS_PTR, uio_DirHandle *dir, const char *filename); +static void moda_Close (THIS_PTR); +static int moda_Decode (THIS_PTR, void* buf, sint32 bufsize); +static uint32 moda_Seek (THIS_PTR, uint32 pcm_pos); +static uint32 moda_GetFrame (THIS_PTR); + +TFB_SoundDecoderFuncs moda_DecoderVtbl = +{ + moda_GetName, + moda_InitModule, + moda_TermModule, + moda_GetStructSize, + moda_GetError, + moda_Init, + moda_Term, + moda_Open, + moda_Close, + moda_Decode, + moda_Seek, + moda_GetFrame, +}; + +typedef struct tfb_modsounddecoder +{ + // always the first member + TFB_SoundDecoder decoder; + + // private + sint32 last_error; + MODULE* module; + +} TFB_ModSoundDecoder; + + + +// MikMod Output driver +// we provide our own so that we can use MikMod as +// generic decoder + +static void* buffer; +static ULONG bufsize; +static ULONG written; + +static ULONG* +moda_mmout_SetOutputBuffer (void* buf, ULONG size) +{ + buffer = buf; + bufsize = size; + written = 0; + return &written; +} + +static BOOL +moda_mmout_IsThere (void) +{ + return 1; +} + +static BOOL +moda_mmout_Init (void) +{ + md_mode |= DMODE_SOFT_MUSIC | DMODE_SOFT_SNDFX; + return VC_Init (); +} + +static void +moda_mmout_Exit (void) +{ + VC_Exit (); +} + +static void +moda_mmout_Update (void) +{ + written = 0; + if (!buffer || bufsize == 0) + return; + + written = VC_WriteBytes (buffer, bufsize); +} + +static BOOL +moda_mmout_Reset (void) +{ + return 0; +} + +static char MDRIVER_name[] = "Mem Buffer"; +static char MDRIVER_version[] = "Mem Buffer driver v1.1"; +static char MDRIVER_alias[] = "membuf"; + +static MDRIVER moda_mmout_drv = +{ + NULL, + //xxx libmikmod does not declare these fields const; it probably should. + MDRIVER_name, // Name + MDRIVER_version, // Version + 0, 255, // Voice limits + MDRIVER_alias, // Alias + +// The minimum mikmod version we support is 3.1.8 +#if (LIBMIKMOD_VERSION_MAJOR > 3) || \ + ((LIBMIKMOD_VERSION_MAJOR == 3) && (LIBMIKMOD_VERSION_MINOR >= 2)) + NULL, // Cmdline help +#endif + + NULL, + moda_mmout_IsThere, + VC_SampleLoad, + VC_SampleUnload, + VC_SampleSpace, + VC_SampleLength, + moda_mmout_Init, + moda_mmout_Exit, + moda_mmout_Reset, + VC_SetNumVoices, + VC_PlayStart, + VC_PlayStop, + moda_mmout_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 +}; + + +static const TFB_DecoderFormats* moda_formats = NULL; + +// MikMod READER interface +// we provide our own so that we can do loading via uio +// +typedef struct MUIOREADER +{ + MREADER core; + uio_Stream* file; + +} MUIOREADER; + +static BOOL +moda_uioReader_Eof (MREADER* reader) +{ + return uio_feof (((MUIOREADER*)reader)->file); +} + +static BOOL +moda_uioReader_Read (MREADER* reader, void* ptr, size_t size) +{ + return uio_fread (ptr, size, 1, ((MUIOREADER*)reader)->file); +} + +static int +moda_uioReader_Get (MREADER* reader) +{ + return uio_fgetc (((MUIOREADER*)reader)->file); +} + +static BOOL +moda_uioReader_Seek (MREADER* reader, long offset, int whence) +{ + return uio_fseek (((MUIOREADER*)reader)->file, offset, whence); +} + +static long +moda_uioReader_Tell (MREADER* reader) +{ + return uio_ftell (((MUIOREADER*)reader)->file); +} + +static MREADER* +moda_new_uioReader (uio_Stream* fp) +{ + MUIOREADER* reader = (MUIOREADER*) HMalloc (sizeof(MUIOREADER)); + if (reader) + { + reader->core.Eof = &moda_uioReader_Eof; + reader->core.Read = &moda_uioReader_Read; + reader->core.Get = &moda_uioReader_Get; + reader->core.Seek = &moda_uioReader_Seek; + reader->core.Tell = &moda_uioReader_Tell; + reader->file = fp; + } + return (MREADER*)reader; +} + +static void +moda_delete_uioReader (MREADER* reader) +{ + if (reader) + HFree (reader); +} + + +static const char* +moda_GetName (void) +{ + return "MikMod"; +} + +static bool +moda_InitModule (int flags, const TFB_DecoderFormats* fmts) +{ + MikMod_RegisterDriver (&moda_mmout_drv); + MikMod_RegisterAllLoaders (); + + if (flags & audio_QUALITY_HIGH) + { + md_mode = DMODE_HQMIXER|DMODE_STEREO|DMODE_16BITS|DMODE_INTERP|DMODE_SURROUND; + md_mixfreq = 44100; + md_reverb = 1; + } + else if (flags & audio_QUALITY_LOW) + { + md_mode = DMODE_SOFT_MUSIC|DMODE_STEREO|DMODE_16BITS; +#ifdef __SYMBIAN32__ + md_mixfreq = 11025; +#else + md_mixfreq = 22050; +#endif + 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 (NULL)) + { + log_add (log_Error, "MikMod_Init() failed, %s", + MikMod_strerror (MikMod_errno)); + return false; + } + + moda_formats = fmts; + + return true; +} + +static void +moda_TermModule (void) +{ + MikMod_Exit (); +} + +static uint32 +moda_GetStructSize (void) +{ + return sizeof (TFB_ModSoundDecoder); +} + +static int +moda_GetError (THIS_PTR) +{ + TFB_ModSoundDecoder* moda = (TFB_ModSoundDecoder*) This; + int ret = moda->last_error; + moda->last_error = 0; + return ret; +} + +static bool +moda_Init (THIS_PTR) +{ + //TFB_ModSoundDecoder* moda = (TFB_ModSoundDecoder*) This; + This->need_swap = + moda_formats->big_endian != moda_formats->want_big_endian; + return true; +} + +static void +moda_Term (THIS_PTR) +{ + //TFB_ModSoundDecoder* moda = (TFB_ModSoundDecoder*) This; + moda_Close (This); // ensure cleanup +} + +static bool +moda_Open (THIS_PTR, uio_DirHandle *dir, const char *filename) +{ + TFB_ModSoundDecoder* moda = (TFB_ModSoundDecoder*) This; + uio_Stream *fp; + MREADER* reader; + MODULE* mod; + + fp = uio_fopen (dir, filename, "rb"); + if (!fp) + { + moda->last_error = errno; + return false; + } + + reader = moda_new_uioReader (fp); + if (!reader) + { + moda->last_error = -1; + uio_fclose (fp); + return false; + } + + mod = Player_LoadGeneric (reader, 8, 0); + + // can already dispose of reader and fileh + moda_delete_uioReader (reader); + uio_fclose (fp); + if (!mod) + { + log_add (log_Warning, "moda_Open(): could not load %s", filename); + return false; + } + + moda->module = mod; + mod->extspd = 1; + mod->panflag = 1; + mod->wrap = 0; + mod->loop = 1; + + This->format = moda_formats->stereo16; + This->frequency = md_mixfreq; + This->length = 0; // FIXME way to obtain this from mikmod? + + moda->last_error = 0; + + return true; +} + +static void +moda_Close (THIS_PTR) +{ + TFB_ModSoundDecoder* moda = (TFB_ModSoundDecoder*) This; + + if (moda->module) + { + Player_Free (moda->module); + moda->module = NULL; + } +} + +static int +moda_Decode (THIS_PTR, void* buf, sint32 bufsize) +{ + TFB_ModSoundDecoder* moda = (TFB_ModSoundDecoder*) This; + volatile ULONG* poutsize; + + Player_Start (moda->module); + if (!Player_Active()) + return 0; + + poutsize = moda_mmout_SetOutputBuffer (buf, bufsize); + MikMod_Update (); + + return *poutsize; +} + +static uint32 +moda_Seek (THIS_PTR, uint32 pcm_pos) +{ + TFB_ModSoundDecoder* moda = (TFB_ModSoundDecoder*) This; + + Player_Start (moda->module); + if (pcm_pos) + log_add (log_Debug, "moda_Seek(): " + "non-zero seek positions not supported for mod"); + Player_SetPosition (0); + + return 0; +} + +static uint32 +moda_GetFrame (THIS_PTR) +{ + TFB_ModSoundDecoder* moda = (TFB_ModSoundDecoder*) This; + return moda->module->sngpos; +} -- cgit v1.2.3