mirror of
https://github.com/Shpoike/Quakespasm.git
synced 2024-11-14 00:50:38 +00:00
81adf62374
- New console command music_jump: Jump to given order in music, like Unreal's music change. Only for module (tracker) music. - stream layer: Store the loop setting. - umx reader: Replaced questionable byte-swap. - libxmp backend: Handled the loop setting through libxmp apis. - mikmod backend: Enabled in-module loops. Respect global loop setting. More compatible reader callback structure.
221 lines
6 KiB
C
221 lines
6 KiB
C
/*
|
|
* tracker music (module file) decoding support using libmikmod
|
|
* Copyright (C) 2013 O.Sezer <sezero@users.sourceforge.net>
|
|
*
|
|
* 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.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "quakedef.h"
|
|
|
|
#if defined(USE_CODEC_MIKMOD)
|
|
#include "snd_codec.h"
|
|
#include "snd_codeci.h"
|
|
#include "snd_mikmod.h"
|
|
#include <mikmod.h>
|
|
|
|
#if ((LIBMIKMOD_VERSION+0) < 0x030105)
|
|
#error libmikmod version is way too old and unusable.
|
|
#endif
|
|
#if (LIBMIKMOD_VERSION < 0x030107) /* ancient libmikmod */
|
|
#define S_MIKMOD_initlib(c) MikMod_Init()
|
|
#else
|
|
#define S_MIKMOD_initlib(c) MikMod_Init(c)
|
|
#endif
|
|
|
|
#ifndef DMODE_NOISEREDUCTION
|
|
#define DMODE_NOISEREDUCTION 0x1000 /* Low pass filtering */
|
|
#endif
|
|
#ifndef DMODE_SIMDMIXER
|
|
#define DMODE_SIMDMIXER 0x0800 /* enable SIMD mixing */
|
|
#endif
|
|
|
|
typedef struct _mik_priv {
|
|
/* MREADER core members in libmikmod2/3: */
|
|
int (*Seek)(struct MREADER*, long, int);
|
|
long (*Tell)(struct MREADER*);
|
|
BOOL (*Read)(struct MREADER*, void*, size_t);
|
|
int (*Get)(struct MREADER*);
|
|
BOOL (*Eof)(struct MREADER*);
|
|
/* no iobase members in libmikmod <= 3.2.0-beta2 */
|
|
long iobase, prev_iobase;
|
|
|
|
fshandle_t *fh;
|
|
MODULE *module;
|
|
} mik_priv_t;
|
|
|
|
static int MIK_Seek (MREADER *r, long ofs, int whence)
|
|
{
|
|
return FS_fseek(((mik_priv_t *)r)->fh, ofs, whence);
|
|
}
|
|
|
|
static long MIK_Tell (MREADER *r)
|
|
{
|
|
return FS_ftell(((mik_priv_t *)r)->fh);
|
|
}
|
|
|
|
static BOOL MIK_Read (MREADER *r, void *ptr, size_t siz)
|
|
{
|
|
return !!FS_fread(ptr, siz, 1, ((mik_priv_t *)r)->fh);
|
|
}
|
|
|
|
static int MIK_Get (MREADER *r)
|
|
{
|
|
return FS_fgetc(((mik_priv_t *)r)->fh);
|
|
}
|
|
|
|
static BOOL MIK_Eof (MREADER *r)
|
|
{
|
|
return FS_feof(((mik_priv_t *)r)->fh);
|
|
}
|
|
|
|
static qboolean S_MIKMOD_CodecInitialize (void)
|
|
{
|
|
if (mikmod_codec.initialized)
|
|
return true;
|
|
|
|
/* set mode flags to only we like: */
|
|
md_mode = 0;
|
|
if ((shm->samplebits / 8) == 2)
|
|
md_mode |= DMODE_16BITS;
|
|
if (shm->channels == 2)
|
|
md_mode |= DMODE_STEREO;
|
|
md_mode |= DMODE_SOFT_MUSIC; /* this is a software-only mixer */
|
|
|
|
/* md_mixfreq is UWORD, so something like 96000 isn't OK */
|
|
md_mixfreq = (shm->speed < 65536)? shm->speed : 48000;
|
|
|
|
/* keeping md_device as 0 which is default (auto-detect: we
|
|
* only register drv_nos, and it will be the only one found.)
|
|
* md_pansep (stereo channels separation) default 128 is OK.
|
|
* no reverbation (md_reverb 0 (up to 15)) is OK.
|
|
* md_musicvolume and md_sndfxvolume defaults are 128: OK. */
|
|
/* just tone down overall volume md_volume from 128 to 96? */
|
|
md_volume = 96;
|
|
|
|
MikMod_RegisterDriver(&drv_nos); /* only need the "nosound" driver, none else */
|
|
MikMod_RegisterAllLoaders();
|
|
if (S_MIKMOD_initlib(NULL))
|
|
{
|
|
Con_DPrintf("Could not initialize MikMod: %s\n", MikMod_strerror(MikMod_errno));
|
|
return false;
|
|
}
|
|
|
|
/* this can't get set with drv_nos, but whatever, be safe: */
|
|
md_mode &= ~DMODE_SIMDMIXER; /* SIMD mixer is buggy when combined with HQMIXER */
|
|
|
|
mikmod_codec.initialized = true;
|
|
return true;
|
|
}
|
|
|
|
static void S_MIKMOD_CodecShutdown (void)
|
|
{
|
|
if (mikmod_codec.initialized)
|
|
{
|
|
mikmod_codec.initialized = false;
|
|
MikMod_Exit();
|
|
}
|
|
}
|
|
|
|
static qboolean S_MIKMOD_CodecOpenStream (snd_stream_t *stream)
|
|
{
|
|
mik_priv_t *priv;
|
|
|
|
stream->priv = Z_Malloc(sizeof(mik_priv_t));
|
|
priv = (mik_priv_t *) stream->priv;
|
|
priv->Seek = MIK_Seek;
|
|
priv->Tell = MIK_Tell;
|
|
priv->Read = MIK_Read;
|
|
priv->Get = MIK_Get;
|
|
priv->Eof = MIK_Eof;
|
|
priv->fh = &stream->fh;
|
|
|
|
priv->module = Player_LoadGeneric((MREADER *)stream->priv, 64, 0);
|
|
if (!priv->module)
|
|
{
|
|
Con_DPrintf("Could not load module: %s\n", MikMod_strerror(MikMod_errno));
|
|
Z_Free(stream->priv);
|
|
return false;
|
|
}
|
|
|
|
/* default values of module options set by Player_Init():
|
|
* fadeout (0): don't fade out volume during when last position of the
|
|
* module is being played,
|
|
* extspd (1): process Protracker extended speed effect,
|
|
* panflag (1): process panning effects,
|
|
* wrap (0): don't wrap to restart position when module is finished,
|
|
* loop (1): process all in-module loops -- possible backward loops
|
|
* would make the module to loop endlessly.
|
|
*/
|
|
priv->module->wrap = stream->loop;
|
|
Player_Start(priv->module);
|
|
|
|
stream->info.rate = md_mixfreq;
|
|
stream->info.bits = (md_mode & DMODE_16BITS)? 16: 8;
|
|
stream->info.width = stream->info.bits / 8;
|
|
stream->info.channels = (md_mode & DMODE_STEREO)? 2 : 1;
|
|
/* Con_DPrintf("Playing %s (%d chn)\n", priv->module->songname, priv->module->numchn);*/
|
|
|
|
return true;
|
|
}
|
|
|
|
static int S_MIKMOD_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer)
|
|
{
|
|
if (!Player_Active())
|
|
return 0;
|
|
|
|
/* handle possible loop setting change: */
|
|
((mik_priv_t *)stream->priv)->module->wrap = stream->loop;
|
|
|
|
return (int) VC_WriteBytes((SBYTE *)buffer, bytes);
|
|
}
|
|
|
|
static void S_MIKMOD_CodecCloseStream (snd_stream_t *stream)
|
|
{
|
|
Player_Stop();
|
|
Player_Free(((mik_priv_t *)stream->priv)->module);
|
|
Z_Free(stream->priv);
|
|
S_CodecUtilClose(&stream);
|
|
}
|
|
|
|
static int S_MIKMOD_CodecJumpToOrder (snd_stream_t *stream, int to)
|
|
{
|
|
Player_SetPosition ((UWORD)to);
|
|
return 0;
|
|
}
|
|
|
|
static int S_MIKMOD_CodecRewindStream (snd_stream_t *stream)
|
|
{
|
|
Player_SetPosition (0); /* FIXME: WRONG: THIS IS NOT A TIME SEEK */
|
|
return 0;
|
|
}
|
|
|
|
snd_codec_t mikmod_codec =
|
|
{
|
|
CODECTYPE_MOD,
|
|
false,
|
|
"s3m",
|
|
S_MIKMOD_CodecInitialize,
|
|
S_MIKMOD_CodecShutdown,
|
|
S_MIKMOD_CodecOpenStream,
|
|
S_MIKMOD_CodecReadStream,
|
|
S_MIKMOD_CodecRewindStream,
|
|
S_MIKMOD_CodecJumpToOrder,
|
|
S_MIKMOD_CodecCloseStream,
|
|
NULL
|
|
};
|
|
|
|
#endif /* USE_CODEC_MIKMOD */
|
|
|