Updates for tracker music:

- 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.
This commit is contained in:
Ozkan Sezer 2021-02-04 23:28:00 +03:00
parent b60dae8acd
commit 81adf62374
14 changed files with 122 additions and 79 deletions

View file

@ -76,14 +76,11 @@ static snd_stream_t *bgmstream = NULL;
static void BGM_Play_f (void)
{
if (Cmd_Argc() == 2)
{
if (Cmd_Argc() == 2) {
BGM_Play (Cmd_Argv(1));
}
else
{
else {
Con_Printf ("music <musicfile>\n");
return;
}
}
@ -99,16 +96,17 @@ static void BGM_Resume_f (void)
static void BGM_Loop_f (void)
{
if (Cmd_Argc() == 2)
{
if (Cmd_Argc() == 2) {
if (q_strcasecmp(Cmd_Argv(1), "0") == 0 ||
q_strcasecmp(Cmd_Argv(1),"off") == 0)
bgmloop = false;
else if (q_strcasecmp(Cmd_Argv(1), "1") == 0 ||
q_strcasecmp(Cmd_Argv(1),"on") == 0)
q_strcasecmp(Cmd_Argv(1),"on") == 0)
bgmloop = true;
else if (q_strcasecmp(Cmd_Argv(1),"toggle") == 0)
bgmloop = !bgmloop;
if (bgmstream) bgmstream->loop = bgmloop;
}
if (bgmloop)
@ -122,6 +120,16 @@ static void BGM_Stop_f (void)
BGM_Stop();
}
static void BGM_Jump_f (void)
{
if (Cmd_Argc() != 2) {
Con_Printf ("music_jump <ordernum>\n");
}
else if (bgmstream) {
S_CodecJumpToOrder(bgmstream, atoi(Cmd_Argv(1)));
}
}
qboolean BGM_Init (void)
{
music_handler_t *handlers = NULL;
@ -133,6 +141,7 @@ qboolean BGM_Init (void)
Cmd_AddCommand("music_resume", BGM_Resume_f);
Cmd_AddCommand("music_loop", BGM_Loop_f);
Cmd_AddCommand("music_stop", BGM_Stop_f);
Cmd_AddCommand("music_jump", BGM_Jump_f);
if (COM_CheckParm("-noextmusic") != 0)
no_extmusic = true;
@ -206,7 +215,7 @@ static void BGM_Play_noext (const char *filename, unsigned int allowed_types)
/* not supported in quake */
break;
case BGM_STREAMER:
bgmstream = S_CodecOpenStreamType(tmp, handler->type);
bgmstream = S_CodecOpenStreamType(tmp, handler->type, bgmloop);
if (bgmstream)
return; /* success */
break;
@ -264,7 +273,7 @@ void BGM_Play (const char *filename)
/* not supported in quake */
break;
case BGM_STREAMER:
bgmstream = S_CodecOpenStreamType(tmp, handler->type);
bgmstream = S_CodecOpenStreamType(tmp, handler->type, bgmloop);
if (bgmstream)
return; /* success */
break;
@ -329,7 +338,7 @@ void BGM_PlayCDtrack (byte track, qboolean looping)
{
q_snprintf(tmp, sizeof(tmp), "%s/track%02d.%s",
MUSIC_DIRNAME, (int)track, ext);
bgmstream = S_CodecOpenStreamType(tmp, type);
bgmstream = S_CodecOpenStreamType(tmp, type, bgmloop);
if (! bgmstream)
Con_Printf("Couldn't handle music file %s\n", tmp);
}

View file

@ -117,7 +117,7 @@ void S_CodecShutdown (void)
S_CodecOpenStream
=================
*/
snd_stream_t *S_CodecOpenStreamType (const char *filename, unsigned int type)
snd_stream_t *S_CodecOpenStreamType (const char *filename, unsigned int type, qboolean loop)
{
snd_codec_t *codec;
snd_stream_t *stream;
@ -140,7 +140,7 @@ snd_stream_t *S_CodecOpenStreamType (const char *filename, unsigned int type)
Con_Printf("Unknown type for %s\n", filename);
return NULL;
}
stream = S_CodecUtilOpen(filename, codec);
stream = S_CodecUtilOpen(filename, codec, loop);
if (stream) {
if (codec->codec_open(stream))
stream->status = STREAM_PLAY;
@ -149,7 +149,7 @@ snd_stream_t *S_CodecOpenStreamType (const char *filename, unsigned int type)
return stream;
}
snd_stream_t *S_CodecOpenStreamExt (const char *filename)
snd_stream_t *S_CodecOpenStreamExt (const char *filename, qboolean loop)
{
snd_codec_t *codec;
snd_stream_t *stream;
@ -174,7 +174,7 @@ snd_stream_t *S_CodecOpenStreamExt (const char *filename)
Con_Printf("Unknown extension for %s\n", filename);
return NULL;
}
stream = S_CodecUtilOpen(filename, codec);
stream = S_CodecUtilOpen(filename, codec, loop);
if (stream) {
if (codec->codec_open(stream))
stream->status = STREAM_PLAY;
@ -183,7 +183,7 @@ snd_stream_t *S_CodecOpenStreamExt (const char *filename)
return stream;
}
snd_stream_t *S_CodecOpenStreamAny (const char *filename)
snd_stream_t *S_CodecOpenStreamAny (const char *filename, qboolean loop)
{
snd_codec_t *codec;
snd_stream_t *stream;
@ -198,7 +198,7 @@ snd_stream_t *S_CodecOpenStreamAny (const char *filename)
while (codec)
{
q_snprintf(tmp, sizeof(tmp), "%s.%s", filename, codec->ext);
stream = S_CodecUtilOpen(tmp, codec);
stream = S_CodecUtilOpen(tmp, codec, loop);
if (stream) {
if (codec->codec_open(stream)) {
stream->status = STREAM_PLAY;
@ -225,7 +225,7 @@ snd_stream_t *S_CodecOpenStreamAny (const char *filename)
Con_Printf("Unknown extension for %s\n", filename);
return NULL;
}
stream = S_CodecUtilOpen(filename, codec);
stream = S_CodecUtilOpen(filename, codec, loop);
if (stream) {
if (codec->codec_open(stream))
stream->status = STREAM_PLAY;
@ -261,6 +261,14 @@ int S_CodecRewindStream (snd_stream_t *stream)
return stream->codec->codec_rewind(stream);
}
int S_CodecJumpToOrder (snd_stream_t *stream, int to)
{
if (stream->codec->codec_jump) {
return stream->codec->codec_jump(stream, to);
}
return -1;
}
int S_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer)
{
return stream->codec->codec_read(stream, bytes, buffer);
@ -268,7 +276,7 @@ int S_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer)
/* Util functions (used by codecs) */
snd_stream_t *S_CodecUtilOpen(const char *filename, snd_codec_t *codec)
snd_stream_t *S_CodecUtilOpen(const char *filename, snd_codec_t *codec, qboolean loop)
{
snd_stream_t *stream;
FILE *handle;
@ -287,6 +295,7 @@ snd_stream_t *S_CodecUtilOpen(const char *filename, snd_codec_t *codec)
/* Allocate a stream, Z_Malloc zeroes its content */
stream = (snd_stream_t *) Z_Malloc(sizeof(snd_stream_t));
stream->codec = codec;
stream->loop = loop;
stream->fh.file = handle;
stream->fh.start = ftell(handle);
stream->fh.pos = 0;

View file

@ -54,6 +54,7 @@ typedef struct snd_stream_s
snd_info_t info;
stream_status_t status;
snd_codec_t *codec; /* codec handling this stream */
qboolean loop;
void *priv; /* data private to the codec. */
} snd_stream_t;
@ -64,22 +65,24 @@ void S_CodecShutdown (void);
/* Callers of the following S_CodecOpenStream* functions
* are reponsible for attaching any path to the filename */
snd_stream_t *S_CodecOpenStreamType (const char *filename, unsigned int type);
snd_stream_t *S_CodecOpenStreamType (const char *filename, unsigned int type,
qboolean loop);
/* Decides according to the required type. */
snd_stream_t *S_CodecOpenStreamAny (const char *filename);
snd_stream_t *S_CodecOpenStreamAny (const char *filename, qboolean loop);
/* Decides according to file extension. if the
* name has no extension, try all available. */
snd_stream_t *S_CodecOpenStreamExt (const char *filename);
snd_stream_t *S_CodecOpenStreamExt (const char *filename, qboolean loop);
/* Decides according to file extension. the name
* MUST have an extension. */
void S_CodecCloseStream (snd_stream_t *stream);
int S_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer);
int S_CodecRewindStream (snd_stream_t *stream);
int S_CodecJumpToOrder (snd_stream_t *stream, int to);
snd_stream_t *S_CodecUtilOpen(const char *filename, snd_codec_t *codec);
snd_stream_t *S_CodecUtilOpen(const char *filename, snd_codec_t *codec, qboolean loop);
void S_CodecUtilClose(snd_stream_t **stream);

View file

@ -32,6 +32,7 @@ typedef void (*CODEC_SHUTDOWN)(void);
typedef qboolean (*CODEC_OPEN)(snd_stream_t *stream);
typedef int (*CODEC_READ)(snd_stream_t *stream, int bytes, void *buffer);
typedef int (*CODEC_REWIND)(snd_stream_t *stream);
typedef int (*CODEC_JUMP)(snd_stream_t *stream, int order);
typedef void (*CODEC_CLOSE)(snd_stream_t *stream);
struct snd_codec_s
@ -44,6 +45,7 @@ struct snd_codec_s
CODEC_OPEN codec_open;
CODEC_READ codec_read;
CODEC_REWIND codec_rewind;
CODEC_JUMP codec_jump;
CODEC_CLOSE codec_close;
snd_codec_t *next;
};

View file

@ -356,8 +356,7 @@ static void S_FLAC_CodecCloseStream (snd_stream_t *stream)
FLAC__stream_decoder_finish (ff->decoder);
FLAC__stream_decoder_delete (ff->decoder);
if (ff->buffer)
free(ff->buffer);
if (ff->buffer) free(ff->buffer);
Z_Free(ff);
S_CodecUtilClose(&stream);
@ -382,6 +381,7 @@ snd_codec_t flac_codec =
S_FLAC_CodecOpenStream,
S_FLAC_CodecReadStream,
S_FLAC_CodecRewindStream,
NULL, /* jump */
S_FLAC_CodecCloseStream,
NULL
};

View file

@ -43,12 +43,15 @@
#endif
typedef struct _mik_priv {
/* struct MREADER in libmikmod <= 3.2.0-beta2
* doesn't have iobase members. adding them here
* so that if we compile against 3.2.0-beta2, we
* can still run OK against 3.2.0b3 and newer. */
struct MREADER reader;
/* 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;
@ -132,11 +135,11 @@ static qboolean S_MIKMOD_CodecOpenStream (snd_stream_t *stream)
stream->priv = Z_Malloc(sizeof(mik_priv_t));
priv = (mik_priv_t *) stream->priv;
priv->reader.Seek = MIK_Seek;
priv->reader.Tell = MIK_Tell;
priv->reader.Read = MIK_Read;
priv->reader.Get = MIK_Get;
priv->reader.Eof = MIK_Eof;
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);
@ -147,13 +150,16 @@ static qboolean S_MIKMOD_CodecOpenStream (snd_stream_t *stream)
return false;
}
/* keep default values of fadeout (0: don't fade out volume during when last
* position of the module is being played), extspd (1: do process Protracker
* extended speed effect), panflag (1: do process panning effects), wrap (0:
* don't wrap to restart position when module is finished) are OK with us as
* set internally by libmikmod::Player_Init(). */
/* just change the loop setting to 0, i.e. don't process in-module loops: */
priv->module->loop = 0;
/* 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;
@ -169,6 +175,10 @@ static int S_MIKMOD_CodecReadStream (snd_stream_t *stream, int bytes, void *buff
{
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);
}
@ -180,9 +190,15 @@ static void S_MIKMOD_CodecCloseStream (snd_stream_t *stream)
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);
Player_SetPosition (0); /* FIXME: WRONG: THIS IS NOT A TIME SEEK */
return 0;
}
@ -196,6 +212,7 @@ snd_codec_t mikmod_codec =
S_MIKMOD_CodecOpenStream,
S_MIKMOD_CodecReadStream,
S_MIKMOD_CodecRewindStream,
S_MIKMOD_CodecJumpToOrder,
S_MIKMOD_CodecCloseStream,
NULL
};

View file

@ -452,6 +452,7 @@ snd_codec_t mp3_codec =
S_MP3_CodecOpenStream,
S_MP3_CodecReadStream,
S_MP3_CodecRewindStream,
NULL, /* jump */
S_MP3_CodecCloseStream,
NULL
};

View file

@ -49,7 +49,7 @@ static ssize_t mp3_read (void *f, void *buf, size_t size)
}
static off_t mp3_seek (void *f, off_t offset, int whence)
{
if (f == NULL) return (-1);
if (f == NULL) return -1;
if (FS_fseek((fshandle_t *)f, (long) offset, whence) < 0)
return (off_t)-1;
return (off_t) FS_ftell((fshandle_t *)f);
@ -200,7 +200,7 @@ static int S_MP3_CodecRewindStream (snd_stream_t *stream)
{
mp3_priv_t *priv = (mp3_priv_t *) stream->priv;
off_t res = mpg123_seek(priv->handle, 0, SEEK_SET);
if (res >= 0) return (0);
if (res >= 0) return 0;
return res;
}
@ -214,6 +214,7 @@ snd_codec_t mp3_codec =
S_MP3_CodecOpenStream,
S_MP3_CodecReadStream,
S_MP3_CodecRewindStream,
NULL, /* jump */
S_MP3_CodecCloseStream,
NULL
};

View file

@ -202,6 +202,7 @@ snd_codec_t opus_codec =
S_OPUS_CodecOpenStream,
S_OPUS_CodecReadStream,
S_OPUS_CodecRewindStream,
NULL, /* jump */
S_OPUS_CodecCloseStream,
NULL
};

View file

@ -2,12 +2,7 @@
* Unreal UMX container support.
* UPKG parsing partially based on Unreal Media Ripper (UMR) v0.3
* by Andy Ward <wardwh@swbell.net>, with additional updates
* by O. Sezer - see git repo at https://github.com/sezero/umr/
*
* The cheaper way, i.e. linear search of music object like libxmp
* and libmodplug does, is possible. With this however we're using
* the embedded offset, size and object type directly from the umx
* file, and I feel safer with it.
* by O. Sezer - see git repo at https://github.com/sezero/umr.git
*
* Copyright (C) 2013 O. Sezer <sezero@users.sourceforge.net>
*
@ -63,10 +58,9 @@ struct upkg_hdr {
uint32_t guid[4];
int32_t generation_count;
#define UPKG_HDR_SIZE 64 /* 64 bytes up until here */
/*struct _genhist *gen;*/
struct _genhist *gen;
};
/*COMPILE_TIME_ASSERT(upkg_hdr, offsetof(struct upkg_hdr, gen) == UPKG_HDR_SIZE);*/
COMPILE_TIME_ASSERT(upkg_hdr, sizeof(struct upkg_hdr) == UPKG_HDR_SIZE);
COMPILE_TIME_ASSERT(upkg_hdr, offsetof(struct upkg_hdr, gen) == UPKG_HDR_SIZE);
#define UMUSIC_IT 0
#define UMUSIC_S3M 1
@ -274,21 +268,21 @@ static int probe_umx (fshandle_t *f, const struct upkg_hdr *hdr,
return t;
}
static int32_t probe_header (void *header)
static int32_t probe_header (fshandle_t *f, struct upkg_hdr *hdr)
{
struct upkg_hdr *hdr;
unsigned char *p;
uint32_t *swp;
int i;
if (FS_fread(hdr, 1, UPKG_HDR_SIZE, f) < UPKG_HDR_SIZE)
return -1;
/* byte swap the header - all members are 32 bit LE values */
p = (unsigned char *) header;
swp = (uint32_t *) header;
for (i = 0; i < UPKG_HDR_SIZE/4; i++, p += 4) {
swp[i] = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
}
hdr->tag = (uint32_t) LittleLong(hdr->tag);
hdr->file_version = LittleLong(hdr->file_version);
hdr->pkg_flags = (uint32_t) LittleLong(hdr->pkg_flags);
hdr->name_count = LittleLong(hdr->name_count);
hdr->name_offset = LittleLong(hdr->name_offset);
hdr->export_count = LittleLong(hdr->export_count);
hdr->export_offset = LittleLong(hdr->export_offset);
hdr->import_count = LittleLong(hdr->import_count);
hdr->import_offset = LittleLong(hdr->import_offset);
hdr = (struct upkg_hdr *) header;
if (hdr->tag != UPKG_HDR_TAG) {
Con_DPrintf("Unknown header tag 0x%x\n", hdr->tag);
return -1;
@ -325,14 +319,13 @@ static int32_t probe_header (void *header)
static int process_upkg (fshandle_t *f, int32_t *ofs, int32_t *objsize)
{
char header[UPKG_HDR_SIZE];
struct upkg_hdr header;
if (FS_fread(header, 1, UPKG_HDR_SIZE, f) < UPKG_HDR_SIZE)
return -1;
if (probe_header(header) < 0)
memset(&header, 0, sizeof(header));
if (probe_header(f, &header) < 0)
return -1;
return probe_umx(f, (struct upkg_hdr *)header, ofs, objsize);
return probe_umx(f, &header, ofs, objsize);
}
static qboolean S_UMX_CodecInitialize (void)
@ -399,6 +392,7 @@ snd_codec_t umx_codec =
S_UMX_CodecOpenStream,
S_UMX_CodecReadStream,
S_UMX_CodecRewindStream,
NULL, /* jump */
S_UMX_CodecCloseStream,
NULL
};

View file

@ -196,6 +196,7 @@ snd_codec_t vorbis_codec =
S_VORBIS_CodecOpenStream,
S_VORBIS_CodecReadStream,
S_VORBIS_CodecRewindStream,
NULL, /* jump */
S_VORBIS_CodecCloseStream,
NULL
};

View file

@ -37,9 +37,7 @@ FGetLittleLong
static int FGetLittleLong (FILE *f)
{
int v;
fread(&v, 1, sizeof(v), f);
return LittleLong(v);
}
@ -51,9 +49,7 @@ FGetLittleShort
static short FGetLittleShort(FILE *f)
{
short v;
fread(&v, 1, sizeof(v), f);
return LittleShort(v);
}
@ -268,6 +264,7 @@ snd_codec_t wav_codec =
S_WAV_CodecOpenStream,
S_WAV_CodecReadStream,
S_WAV_CodecRewindStream,
NULL, /* jump */
S_WAV_CodecCloseStream,
NULL
};

View file

@ -1,6 +1,6 @@
/* tracker music (module file) decoding support using libxmp >= v4.2.0
* https://sourceforge.net/projects/xmp/
* https://github.com/cmatsuoka/libxmp.git
* https://github.com/libxmp/libxmp.git
*
* Copyright (C) 2016 O.Sezer <sezero@users.sourceforge.net>
*
@ -105,7 +105,7 @@ static int S_XMP_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer)
* is partial, the rest of the buffer will be zero-filled.
* the last param is the max number that the current sequence
* of song will be looped, or 0 to disable loop checking. */
r = xmp_play_buffer((xmp_context)stream->priv, buffer, bytes, 1);
r = xmp_play_buffer((xmp_context)stream->priv, buffer, bytes, !stream->loop);
if (r == 0) {
return bytes;
}
@ -125,12 +125,16 @@ static void S_XMP_CodecCloseStream (snd_stream_t *stream)
S_CodecUtilClose(&stream);
}
static int S_XMP_CodecJumpToOrder (snd_stream_t *stream, int to)
{
return xmp_set_position((xmp_context)stream->priv, to);
}
static int S_XMP_CodecRewindStream (snd_stream_t *stream)
{
int ret = xmp_seek_time((xmp_context)stream->priv, 0);
if (ret < 0) return ret;
/* reset internal state */
xmp_play_buffer((xmp_context)stream->priv, NULL, 0, 0);
xmp_play_buffer((xmp_context)stream->priv, NULL, 0, 0); /* reset internal state */
return 0;
}
@ -144,6 +148,7 @@ snd_codec_t xmp_codec =
S_XMP_CodecOpenStream,
S_XMP_CodecReadStream,
S_XMP_CodecRewindStream,
S_XMP_CodecJumpToOrder,
S_XMP_CodecCloseStream,
NULL
};

View file

@ -59,6 +59,9 @@ New console commands:
- music_loop 0
Makes the background music to play once and then stop
- music_jump
Jump to a given order in music (only for module (tracker) music)
New cvars:
-------------------------
- bgm_extmusic (0 or 1): Disable or enable playback of external music