mirror of
https://github.com/Shpoike/Quakespasm.git
synced 2025-02-02 22:11:22 +00:00
Backported external music files support using decoder libraries and the
new raw samples interface from Hammer of Thyrion (uhexen2) : - bgmusic.c, bgmusic.h: New BGM interface for background music handling. Handles streaming music as raw sound samples. - bgmnull.c: BGM source for cases where the engine is configured for no sound. - cl_main.c: Include bgmusic.h. Call BGM_Stop() and CDAudio_Stop() in CL_Disconnect(). - cd_sdl.c: Moved bgmvolume boundary checking to bgmusic.c upon value changes. - gl_vidnt.c, gl_vidsdl.c, cl_parse.c: Include bgmusic.h. Add BGM_Pause() and BGM_Resume() calls along with CDAudio_ counterparts. - cl_parse.c: Replace CDAudio_Play() call by the new BGM_PlayCDtrack() which first tries CDAudio_Play() and then streaming music if it fails. - host.c: Include bgmusic.h. Call BGM_Update() just before S_Update() in Host_Frame(). In Host_Init(), call BGM_Init() after other audio init calls. In Host_Shutdown(), call BGM_Shutdown() before all other audio shutdown calls. - snd_dma.c: Include snd_codec.h and bgmusic.h. Call S_CodecInit() from S_Init(). Call S_CodecShutdown() from S_Shutdown(). - snd_codec.c, snd_codec.h: New public codec interface for streaming music as raw samples. Adapted from quake2 and ioquake3 with changes. Individual codecs are responsible for handling any necessary byte swap operations. - snd_codeci.h: New header for snd_codec internals. - snd_wave.c, snd_wave.h: Codec for WAV format streaming music. Adapted from ioquake3 with changes. - snd_vorbis.c, snd_vorbis.h: Codec for Ogg/Vorbis format streaming music. - snd_mp3.c, snd_mp3.h: Codec for MP3 format streaming music using libmad. Adapted from the SoX project with changes. - Makefile: Adjusted for the new sources. Added switches USE_CODEC_WAVE, USE_CODEC_MP3, USE_CODEC_VORBIS for enabling and disabling individual codecs. - Windows makefiles and project files as well as other CodeBlocks project files will be updated shortly. git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@374 af15c1b1-3010-417e-b628-4374ebc0bcbd
This commit is contained in:
parent
ceb1dd2186
commit
cbb7eb0428
20 changed files with 2001 additions and 10 deletions
|
@ -19,6 +19,11 @@ check_gcc = $(shell if echo | $(CC) $(1) -S -o /dev/null -xc - > /dev/null 2>&1;
|
|||
|
||||
# ============================================================================
|
||||
|
||||
# Enable/disable codecs for streaming music support:
|
||||
USE_CODEC_WAVE=yes
|
||||
USE_CODEC_MP3=yes
|
||||
USE_CODEC_VORBIS=yes
|
||||
|
||||
DEBUG ?= 0
|
||||
SDLNET ?= 0
|
||||
|
||||
|
@ -109,9 +114,22 @@ else
|
|||
NET_LIBS :=
|
||||
endif
|
||||
|
||||
CODECLIBS :=
|
||||
ifeq ($(USE_CODEC_WAVE),yes)
|
||||
CFLAGS+= -DUSE_CODEC_WAVE
|
||||
endif
|
||||
ifeq ($(USE_CODEC_VORBIS),yes)
|
||||
CFLAGS+= -DUSE_CODEC_VORBIS
|
||||
CODECLIBS+= vorbis vorbisfile ogg
|
||||
endif
|
||||
ifeq ($(USE_CODEC_MP3),yes)
|
||||
CFLAGS+= -DUSE_CODEC_MP3
|
||||
CODECLIBS+= mad
|
||||
endif
|
||||
|
||||
COMMON_LIBS:= m GL
|
||||
|
||||
LIBS := $(patsubst %,-l%,$(COMMON_LIBS) $(NET_LIBS))
|
||||
LIBS := $(patsubst %,-l%,$(COMMON_LIBS) $(NET_LIBS) $(CODECLIBS))
|
||||
|
||||
# ---------------------------
|
||||
# targets
|
||||
|
@ -132,7 +150,12 @@ DEFAULT_TARGET := quakespasm
|
|||
# objects
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
COMOBJ_SND := snd_dma.o snd_mix.o snd_mem.o
|
||||
MUSIC_OBJS:= bgmusic.o \
|
||||
snd_codec.o \
|
||||
snd_wave.o \
|
||||
snd_vorbis.o \
|
||||
snd_mp3.o
|
||||
COMOBJ_SND := snd_dma.o snd_mix.o snd_mem.o $(MUSIC_OBJS)
|
||||
SYSOBJ_SND := snd_sdl.o
|
||||
SYSOBJ_CDA := cd_sdl.o
|
||||
SYSOBJ_INPUT := in_sdl.o
|
||||
|
|
71
Quake/bgmnull.c
Normal file
71
Quake/bgmnull.c
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Background music handling for Hexen II: Hammer of Thyrion (uHexen2)
|
||||
* Handle cases when we are configured for no sound and no midi driver,
|
||||
* nada...
|
||||
*
|
||||
* Copyright (C) 1999-2005 Id Software, Inc.
|
||||
* Copyright (C) 2010 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"
|
||||
#include "bgmusic.h"
|
||||
|
||||
qboolean bgmloop = true;
|
||||
|
||||
static float old_volume = -1.0f;
|
||||
|
||||
qboolean BGM_Init (void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void BGM_Play (const char *filename)
|
||||
{
|
||||
}
|
||||
|
||||
void BGM_PlayCDtrack (byte track, qboolean looping)
|
||||
{
|
||||
bgmloop = looping;
|
||||
CDAudio_Play(track, looping);
|
||||
}
|
||||
|
||||
void BGM_Stop (void)
|
||||
{
|
||||
}
|
||||
|
||||
void BGM_Pause (void)
|
||||
{
|
||||
}
|
||||
|
||||
void BGM_Resume (void)
|
||||
{
|
||||
}
|
||||
|
||||
void BGM_Update (void)
|
||||
{
|
||||
if (old_volume != bgmvolume.value)
|
||||
{
|
||||
if (bgmvolume.value < 0)
|
||||
Cvar_Set ("bgmvolume", "0");
|
||||
else if (bgmvolume.value > 1)
|
||||
Cvar_Set ("bgmvolume", "1");
|
||||
old_volume = bgmvolume.value;
|
||||
}
|
||||
}
|
||||
|
456
Quake/bgmusic.c
Normal file
456
Quake/bgmusic.c
Normal file
|
@ -0,0 +1,456 @@
|
|||
/*
|
||||
* Background music handling for Hexen II: Hammer of Thyrion (uHexen2)
|
||||
* Handles streaming music as raw sound samples and runs the midi driver
|
||||
*
|
||||
* Copyright (C) 1999-2005 Id Software, Inc.
|
||||
* Copyright (C) 2010 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"
|
||||
#include "snd_codec.h"
|
||||
#include "bgmusic.h"
|
||||
|
||||
/*
|
||||
* MUSIC FILE DIRECTORIES:
|
||||
*
|
||||
* Midi music file have always lived under "midi" directory, we are
|
||||
* not changing that. Any other music files, including cd-rips, are
|
||||
* expected under the "music" directory.
|
||||
*/
|
||||
|
||||
#define MUSIC_DIRNAME "music"
|
||||
|
||||
qboolean bgmloop;
|
||||
|
||||
static float old_volume = -1.0f;
|
||||
|
||||
typedef enum _bgm_player
|
||||
{
|
||||
BGM_NONE = -1,
|
||||
BGM_MIDIDRV,
|
||||
BGM_STREAMER,
|
||||
} bgm_player_t;
|
||||
|
||||
typedef struct music_handler_s
|
||||
{
|
||||
unsigned int type; /* power of two (snd_codec.h) */
|
||||
bgm_player_t player; /* Enumerated bgm player type */
|
||||
int is_available; /* -1 means not present */
|
||||
const char *ext; /* Expected file extension */
|
||||
const char *dir; /* Where to look for music file */
|
||||
struct music_handler_s *next;
|
||||
} music_handler_t;
|
||||
|
||||
static music_handler_t wanted_handlers[] =
|
||||
{
|
||||
{ CODECTYPE_OGG, BGM_STREAMER, -1, ".ogg", MUSIC_DIRNAME, NULL },
|
||||
{ CODECTYPE_MP3, BGM_STREAMER, -1, ".mp3", MUSIC_DIRNAME, NULL },
|
||||
{ CODECTYPE_WAV, BGM_STREAMER, -1, ".wav", MUSIC_DIRNAME, NULL },
|
||||
{ CODECTYPE_FLAC, BGM_STREAMER, -1, ".flac", MUSIC_DIRNAME, NULL },
|
||||
{ CODECTYPE_NONE, BGM_NONE, -1, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
static music_handler_t *music_handlers = NULL;
|
||||
|
||||
#define ANY_CODECTYPE 0xFFFFFFFF
|
||||
#define CDRIP_TYPES (CODECTYPE_OGG | CODECTYPE_MP3 | CODECTYPE_WAV)
|
||||
#define CDRIPTYPE(x) (((x) & CDRIP_TYPES) != 0)
|
||||
|
||||
static snd_stream_t *bgmstream = NULL;
|
||||
|
||||
static void BGM_Play_f (void)
|
||||
{
|
||||
if (Cmd_Argc() == 2)
|
||||
{
|
||||
BGM_Play (Cmd_Argv(1));
|
||||
}
|
||||
else
|
||||
{
|
||||
Con_Printf ("music <musicfile>\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void BGM_Pause_f (void)
|
||||
{
|
||||
BGM_Pause ();
|
||||
}
|
||||
|
||||
static void BGM_Resume_f (void)
|
||||
{
|
||||
BGM_Resume ();
|
||||
}
|
||||
|
||||
static void BGM_Loop_f (void)
|
||||
{
|
||||
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)
|
||||
bgmloop = true;
|
||||
else if (Q_strcasecmp(Cmd_Argv(1),"toggle") == 0)
|
||||
bgmloop = !bgmloop;
|
||||
}
|
||||
|
||||
if (bgmloop)
|
||||
Con_Printf("Music will be looped\n");
|
||||
else
|
||||
Con_Printf("Music will not be looped\n");
|
||||
}
|
||||
|
||||
static void BGM_Stop_f (void)
|
||||
{
|
||||
BGM_Stop();
|
||||
}
|
||||
|
||||
qboolean BGM_Init (void)
|
||||
{
|
||||
music_handler_t *handlers = NULL;
|
||||
int i;
|
||||
|
||||
Cmd_AddCommand("music", BGM_Play_f);
|
||||
Cmd_AddCommand("music_pause", BGM_Pause_f);
|
||||
Cmd_AddCommand("music_resume", BGM_Resume_f);
|
||||
Cmd_AddCommand("music_loop", BGM_Loop_f);
|
||||
Cmd_AddCommand("music_stop", BGM_Stop_f);
|
||||
|
||||
bgmloop = true;
|
||||
|
||||
for (i = 0; wanted_handlers[i].type != CODECTYPE_NONE; i++)
|
||||
{
|
||||
switch (wanted_handlers[i].player)
|
||||
{
|
||||
case BGM_MIDIDRV:
|
||||
/* not supported in quake */
|
||||
break;
|
||||
case BGM_STREAMER:
|
||||
wanted_handlers[i].is_available =
|
||||
S_CodecIsAvailable(wanted_handlers[i].type);
|
||||
break;
|
||||
case BGM_NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (wanted_handlers[i].is_available != -1)
|
||||
{
|
||||
if (handlers)
|
||||
{
|
||||
handlers->next = &wanted_handlers[i];
|
||||
handlers = handlers->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
music_handlers = &wanted_handlers[i];
|
||||
handlers = music_handlers;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BGM_Shutdown (void)
|
||||
{
|
||||
BGM_Stop();
|
||||
/* sever our connections to
|
||||
* midi_drv and snd_codec */
|
||||
music_handlers = NULL;
|
||||
}
|
||||
|
||||
static void BGM_Play_noext (const char *filename, unsigned int allowed_types)
|
||||
{
|
||||
char tmp[MAX_QPATH];
|
||||
music_handler_t *handler;
|
||||
|
||||
handler = music_handlers;
|
||||
while (handler)
|
||||
{
|
||||
if (! (handler->type & allowed_types))
|
||||
{
|
||||
handler = handler->next;
|
||||
continue;
|
||||
}
|
||||
if (!handler->is_available)
|
||||
{
|
||||
/* skip handlers which failed to initialize */
|
||||
/* TODO: implement re-init, make BGM aware of it */
|
||||
handler = handler->next;
|
||||
continue;
|
||||
}
|
||||
q_snprintf(tmp, sizeof(tmp), "%s/%s%s",
|
||||
handler->dir, filename, handler->ext);
|
||||
switch (handler->player)
|
||||
{
|
||||
case BGM_MIDIDRV:
|
||||
/* not supported in quake */
|
||||
break;
|
||||
case BGM_STREAMER:
|
||||
bgmstream = S_CodecOpenStreamType(tmp, handler->type);
|
||||
if (bgmstream)
|
||||
return; /* success */
|
||||
break;
|
||||
case BGM_NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
handler = handler->next;
|
||||
}
|
||||
|
||||
Con_Printf("Couldn't handle music file %s\n", filename);
|
||||
}
|
||||
|
||||
void BGM_Play (const char *filename)
|
||||
{
|
||||
char tmp[MAX_QPATH];
|
||||
const char *ext;
|
||||
music_handler_t *handler;
|
||||
|
||||
if (!filename || !*filename)
|
||||
{
|
||||
Con_DPrintf("null music file name\n");
|
||||
return;
|
||||
}
|
||||
|
||||
BGM_Stop();
|
||||
|
||||
ext = S_FileExtension(filename);
|
||||
if (!ext) /* try all things */
|
||||
{
|
||||
BGM_Play_noext(filename, ANY_CODECTYPE);
|
||||
return;
|
||||
}
|
||||
|
||||
handler = music_handlers;
|
||||
while (handler)
|
||||
{
|
||||
/* skip handlers which failed to initialize */
|
||||
/* TODO: implement re-init, make BGM aware of it */
|
||||
if (handler->is_available &&
|
||||
!Q_strcasecmp(ext, handler->ext))
|
||||
break;
|
||||
handler = handler->next;
|
||||
}
|
||||
if (!handler)
|
||||
{
|
||||
Con_Printf("Unhandled extension for %s\n", filename);
|
||||
return;
|
||||
}
|
||||
q_snprintf(tmp, sizeof(tmp), "%s/%s", handler->dir, filename);
|
||||
switch (handler->player)
|
||||
{
|
||||
case BGM_MIDIDRV:
|
||||
/* not supported in quake */
|
||||
break;
|
||||
case BGM_STREAMER:
|
||||
bgmstream = S_CodecOpenStreamType(tmp, handler->type);
|
||||
if (bgmstream)
|
||||
return; /* success */
|
||||
break;
|
||||
case BGM_NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Con_Printf("Couldn't handle music file %s\n", filename);
|
||||
}
|
||||
|
||||
void BGM_PlayCDtrack (byte track, qboolean looping)
|
||||
{
|
||||
#if 0 /* see below */
|
||||
char tmp[MAX_QPATH];
|
||||
|
||||
BGM_Stop();
|
||||
|
||||
bgmloop = looping;
|
||||
|
||||
if (CDAudio_Play(track, looping) == 0)
|
||||
return; /* success */
|
||||
|
||||
q_snprintf(tmp, sizeof(tmp), "track%02d", (int)track);
|
||||
BGM_Play_noext(tmp, CDRIP_TYPES);
|
||||
#endif
|
||||
/* instead of searching by the order of music_handlers, do so by
|
||||
* the order of searchpath priority: the file from the searchpath
|
||||
* with the highest path_id is most likely from our own gamedir
|
||||
* itself. This way, if a mod has track02 as a *.mp3 file, which
|
||||
* is below *.ogg in the music_handler order, the mp3 will still
|
||||
* have priority over track02.ogg from, say, id1.
|
||||
*/
|
||||
char tmp[MAX_QPATH];
|
||||
const char *ext;
|
||||
unsigned int path_id, prev_id, type;
|
||||
music_handler_t *handler;
|
||||
|
||||
BGM_Stop();
|
||||
if (CDAudio_Play(track, looping) == 0)
|
||||
return; /* success */
|
||||
|
||||
prev_id = 0;
|
||||
type = 0;
|
||||
ext = NULL;
|
||||
handler = music_handlers;
|
||||
while (handler)
|
||||
{
|
||||
if (! handler->is_available)
|
||||
goto _next;
|
||||
q_snprintf(tmp, sizeof(tmp), "%s/track%02d%s",
|
||||
MUSIC_DIRNAME, (int)track, handler->ext);
|
||||
if (COM_FileExists(tmp, &path_id) == -1)
|
||||
goto _next;
|
||||
if (path_id > prev_id)
|
||||
{
|
||||
prev_id = path_id;
|
||||
type = handler->type;
|
||||
ext = handler->ext;
|
||||
}
|
||||
_next:
|
||||
handler = handler->next;
|
||||
}
|
||||
if (ext == NULL)
|
||||
Con_Printf("Couldn't find a cdrip for track %d\n", (int)track);
|
||||
else
|
||||
{
|
||||
q_snprintf(tmp, sizeof(tmp), "%s/track%02d%s",
|
||||
MUSIC_DIRNAME, (int)track, ext);
|
||||
bgmstream = S_CodecOpenStreamType(tmp, type);
|
||||
if (! bgmstream)
|
||||
Con_Printf("Couldn't handle music file %s\n", tmp);
|
||||
}
|
||||
}
|
||||
|
||||
void BGM_Stop (void)
|
||||
{
|
||||
if (bgmstream)
|
||||
{
|
||||
bgmstream->status = STREAM_NONE;
|
||||
S_CodecCloseStream(bgmstream);
|
||||
bgmstream = NULL;
|
||||
s_rawend = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void BGM_Pause (void)
|
||||
{
|
||||
if (bgmstream)
|
||||
{
|
||||
if (bgmstream->status == STREAM_PLAY)
|
||||
bgmstream->status = STREAM_PAUSE;
|
||||
}
|
||||
}
|
||||
|
||||
void BGM_Resume (void)
|
||||
{
|
||||
if (bgmstream)
|
||||
{
|
||||
if (bgmstream->status == STREAM_PAUSE)
|
||||
bgmstream->status = STREAM_PLAY;
|
||||
}
|
||||
}
|
||||
|
||||
static void BGM_UpdateStream (void)
|
||||
{
|
||||
int res; /* Number of bytes read. */
|
||||
int bufferSamples;
|
||||
int fileSamples;
|
||||
int fileBytes;
|
||||
byte raw[16384];
|
||||
|
||||
if (bgmstream->status != STREAM_PLAY)
|
||||
return;
|
||||
|
||||
/* don't bother playing anything if musicvolume is 0 */
|
||||
if (bgmvolume.value <= 0)
|
||||
return;
|
||||
|
||||
/* see how many samples should be copied into the raw buffer */
|
||||
if (s_rawend < paintedtime)
|
||||
s_rawend = paintedtime;
|
||||
|
||||
while (s_rawend < paintedtime + MAX_RAW_SAMPLES)
|
||||
{
|
||||
bufferSamples = MAX_RAW_SAMPLES - (s_rawend - paintedtime);
|
||||
|
||||
/* decide how much data needs to be read from the file */
|
||||
fileSamples = bufferSamples * bgmstream->info.rate / shm->speed;
|
||||
if (!fileSamples)
|
||||
return;
|
||||
|
||||
/* our max buffer size */
|
||||
fileBytes = fileSamples * (bgmstream->info.width * bgmstream->info.channels);
|
||||
if (fileBytes > sizeof(raw))
|
||||
{
|
||||
fileBytes = sizeof(raw);
|
||||
fileSamples = fileBytes / (bgmstream->info.width * bgmstream->info.channels);
|
||||
}
|
||||
|
||||
/* Read */
|
||||
res = S_CodecReadStream(bgmstream, fileBytes, raw);
|
||||
if (res < fileBytes)
|
||||
{
|
||||
fileBytes = res;
|
||||
fileSamples = res / (bgmstream->info.width * bgmstream->info.channels);
|
||||
}
|
||||
|
||||
if (res > 0) /* data: add to raw buffer */
|
||||
{
|
||||
S_RawSamples(fileSamples, bgmstream->info.rate,
|
||||
bgmstream->info.width, bgmstream->info.channels, raw, bgmvolume.value);
|
||||
}
|
||||
else if (res == 0) /* EOF */
|
||||
{
|
||||
if (bgmloop)
|
||||
{
|
||||
if (S_CodecRewindStream(bgmstream) < 0)
|
||||
{
|
||||
BGM_Stop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BGM_Stop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else /* res < 0: some read error */
|
||||
{
|
||||
Con_Printf("Stream read error (%i), stopping.\n", res);
|
||||
BGM_Stop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BGM_Update (void)
|
||||
{
|
||||
if (old_volume != bgmvolume.value)
|
||||
{
|
||||
if (bgmvolume.value < 0)
|
||||
Cvar_Set ("bgmvolume", "0");
|
||||
else if (bgmvolume.value > 1)
|
||||
Cvar_Set ("bgmvolume", "1");
|
||||
old_volume = bgmvolume.value;
|
||||
}
|
||||
if (bgmstream)
|
||||
BGM_UpdateStream ();
|
||||
}
|
||||
|
43
Quake/bgmusic.h
Normal file
43
Quake/bgmusic.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Background music handling for Hexen II: Hammer of Thyrion (uHexen2)
|
||||
* Handles streaming music as raw sound samples and runs the midi driver
|
||||
*
|
||||
* Copyright (C) 1999-2005 Id Software, Inc.
|
||||
* Copyright (C) 2010 O.Sezer <sezero@users.sourceforge.net>
|
||||
*
|
||||
* $Id: bgmusic.h 3818 2010-12-19 09:04:17Z sezero $
|
||||
*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _BGMUSIC_H_
|
||||
#define _BGMUSIC_H_
|
||||
|
||||
extern qboolean bgmloop;
|
||||
|
||||
qboolean BGM_Init (void);
|
||||
void BGM_Shutdown (void);
|
||||
|
||||
void BGM_Play (const char *filename);
|
||||
void BGM_PlayCDtrack (byte track, qboolean looping);
|
||||
void BGM_Stop (void);
|
||||
void BGM_Update (void);
|
||||
void BGM_Pause (void);
|
||||
void BGM_Resume (void);
|
||||
|
||||
#endif /* _BGMUSIC_H_ */
|
||||
|
|
@ -426,13 +426,7 @@ void CDAudio_Update(void)
|
|||
return;
|
||||
|
||||
if (old_cdvolume != bgmvolume.value)
|
||||
{
|
||||
if (bgmvolume.value < 0)
|
||||
Cvar_Set ("bgmvolume", "0.0");
|
||||
else if (bgmvolume.value > 1)
|
||||
Cvar_Set ("bgmvolume", "1.0");
|
||||
CDAudio_SetVolume (bgmvolume.value);
|
||||
}
|
||||
|
||||
if (playing && realtime > endOfTrack)
|
||||
{
|
||||
|
|
|
@ -21,6 +21,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
// cl_main.c -- client main loop
|
||||
|
||||
#include "quakedef.h"
|
||||
#include "bgmusic.h"
|
||||
|
||||
// we need to declare some mouse variables here, because the menu system
|
||||
// references them even when on a unix system.
|
||||
|
@ -111,6 +112,8 @@ void CL_Disconnect (void)
|
|||
{
|
||||
// stop sounds (especially looping!)
|
||||
S_StopAllSounds (true);
|
||||
BGM_Stop();
|
||||
CDAudio_Stop();
|
||||
|
||||
// if running a local server, shut it down
|
||||
if (cls.demoplayback)
|
||||
|
|
|
@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
// cl_parse.c -- parse a message received from the server
|
||||
|
||||
#include "quakedef.h"
|
||||
#include "bgmusic.h"
|
||||
|
||||
const char *svc_strings[] =
|
||||
{
|
||||
|
@ -1104,10 +1105,12 @@ void CL_ParseServerMessage (void)
|
|||
if (cl.paused)
|
||||
{
|
||||
CDAudio_Pause ();
|
||||
BGM_Pause ();
|
||||
}
|
||||
else
|
||||
{
|
||||
CDAudio_Resume ();
|
||||
BGM_Resume ();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -1151,9 +1154,9 @@ void CL_ParseServerMessage (void)
|
|||
cl.cdtrack = MSG_ReadByte ();
|
||||
cl.looptrack = MSG_ReadByte ();
|
||||
if ( (cls.demoplayback || cls.demorecording) && (cls.forcetrack != -1) )
|
||||
CDAudio_Play ((byte)cls.forcetrack, true);
|
||||
BGM_PlayCDtrack ((byte)cls.forcetrack, true);
|
||||
else
|
||||
CDAudio_Play ((byte)cl.cdtrack, true);
|
||||
BGM_PlayCDtrack ((byte)cl.cdtrack, true);
|
||||
break;
|
||||
|
||||
case svc_intermission:
|
||||
|
|
|
@ -21,6 +21,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
// gl_vidnt.c -- NT GL vid component
|
||||
|
||||
#include "quakedef.h"
|
||||
#include "bgmusic.h"
|
||||
#include "winquake.h"
|
||||
#include "resource.h"
|
||||
#include <commctrl.h>
|
||||
|
@ -543,6 +544,7 @@ int VID_SetMode (int modenum)
|
|||
scr_disabled_for_loading = true;
|
||||
|
||||
CDAudio_Pause ();
|
||||
BGM_Pause ();
|
||||
|
||||
if (vid_modenum == NO_MODE)
|
||||
original_mode = windowed_default;
|
||||
|
@ -591,6 +593,7 @@ int VID_SetMode (int modenum)
|
|||
VID_UpdateWindowStatus ();
|
||||
|
||||
CDAudio_Resume ();
|
||||
BGM_Resume ();
|
||||
scr_disabled_for_loading = temp;
|
||||
|
||||
// now we try to make sure we get the focus on the mode switch, because
|
||||
|
|
|
@ -23,6 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
// gl_vidnt.c -- NT GL vid component
|
||||
|
||||
#include "quakedef.h"
|
||||
#include "bgmusic.h"
|
||||
#include "resource.h"
|
||||
#include "SDL.h"
|
||||
|
||||
|
@ -260,6 +261,7 @@ int VID_SetMode (int modenum)
|
|||
scr_disabled_for_loading = true;
|
||||
|
||||
CDAudio_Pause ();
|
||||
BGM_Pause ();
|
||||
|
||||
// set vertical sync
|
||||
if (gl_swap_control)
|
||||
|
@ -328,6 +330,7 @@ int VID_SetMode (int modenum)
|
|||
VID_UpdateWindowStatus ();
|
||||
|
||||
CDAudio_Resume ();
|
||||
BGM_Resume ();
|
||||
scr_disabled_for_loading = temp;
|
||||
|
||||
vid_modenum = modenum;
|
||||
|
|
|
@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
// host.c -- coordinates spawning and killing of local servers
|
||||
|
||||
#include "quakedef.h"
|
||||
#include "bgmusic.h"
|
||||
#include <setjmp.h>
|
||||
|
||||
/*
|
||||
|
@ -728,6 +729,7 @@ void _Host_Frame (float time)
|
|||
time2 = Sys_FloatTime ();
|
||||
|
||||
// update audio
|
||||
BGM_Update(); // adds music raw samples and/or advances midi driver
|
||||
if (cls.signon == SIGNONS)
|
||||
{
|
||||
S_Update (r_origin, vpn, vright, vup);
|
||||
|
@ -851,6 +853,7 @@ void Host_Init (quakeparms_t *parms)
|
|||
R_Init ();
|
||||
S_Init ();
|
||||
CDAudio_Init ();
|
||||
BGM_Init();
|
||||
Sbar_Init ();
|
||||
CL_Init ();
|
||||
|
||||
|
@ -917,6 +920,7 @@ void Host_Shutdown(void)
|
|||
{
|
||||
if (con_initialized)
|
||||
History_Shutdown ();
|
||||
BGM_Shutdown();
|
||||
CDAudio_Shutdown ();
|
||||
S_Shutdown ();
|
||||
IN_Shutdown (); // input is only initialized in Host_Init if we're not dedicated -- kristian
|
||||
|
|
270
Quake/snd_codec.c
Normal file
270
Quake/snd_codec.c
Normal file
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
* Audio Codecs: Adapted from ioquake3 with changes.
|
||||
* For now, only handles streaming music, not sound effects.
|
||||
*
|
||||
* Copyright (C) 1999-2005 Id Software, Inc.
|
||||
* Copyright (C) 2005 Stuart Dalton <badcdev@gmail.com>
|
||||
* Copyright (C) 2010 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"
|
||||
#include "snd_codec.h"
|
||||
#include "snd_codeci.h"
|
||||
|
||||
/* headers for individual codecs */
|
||||
#include "snd_wave.h"
|
||||
#include "snd_mp3.h"
|
||||
#include "snd_vorbis.h"
|
||||
|
||||
|
||||
static snd_codec_t *codecs;
|
||||
|
||||
/*
|
||||
=================
|
||||
S_CodecRegister
|
||||
=================
|
||||
*/
|
||||
static void S_CodecRegister(snd_codec_t *codec)
|
||||
{
|
||||
codec->next = codecs;
|
||||
codecs = codec;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
S_CodecInit
|
||||
=================
|
||||
*/
|
||||
void S_CodecInit (void)
|
||||
{
|
||||
snd_codec_t *codec;
|
||||
codecs = NULL;
|
||||
|
||||
/* Register in the inverse order
|
||||
* of codec choice preference: */
|
||||
#ifdef USE_CODEC_WAVE
|
||||
S_CodecRegister(&wav_codec);
|
||||
#endif
|
||||
#ifdef USE_CODEC_MP3
|
||||
S_CodecRegister(&mp3_codec);
|
||||
#endif
|
||||
#ifdef USE_CODEC_VORBIS
|
||||
S_CodecRegister(&ogg_codec);
|
||||
#endif
|
||||
|
||||
codec = codecs;
|
||||
while (codec)
|
||||
{
|
||||
codec->initialize();
|
||||
codec = codec->next;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
S_CodecShutdown
|
||||
=================
|
||||
*/
|
||||
void S_CodecShutdown (void)
|
||||
{
|
||||
snd_codec_t *codec = codecs;
|
||||
while (codec)
|
||||
{
|
||||
codec->shutdown();
|
||||
codec = codec->next;
|
||||
}
|
||||
codecs = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
S_CodecOpenStream
|
||||
=================
|
||||
*/
|
||||
snd_stream_t *S_CodecOpenStreamType (const char *filename, unsigned int type)
|
||||
{
|
||||
snd_codec_t *codec;
|
||||
snd_stream_t *stream;
|
||||
|
||||
if (type == CODECTYPE_NONE)
|
||||
{
|
||||
Con_Printf("Bad type for %s\n", filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
codec = codecs;
|
||||
while (codec)
|
||||
{
|
||||
if (type == codec->type)
|
||||
break;
|
||||
codec = codec->next;
|
||||
}
|
||||
if (!codec)
|
||||
{
|
||||
Con_Printf("Unknown type for %s\n", filename);
|
||||
return NULL;
|
||||
}
|
||||
stream = codec->codec_open(filename);
|
||||
if (stream)
|
||||
stream->status = STREAM_PLAY;
|
||||
return stream;
|
||||
}
|
||||
|
||||
snd_stream_t *S_CodecOpenStreamExt (const char *filename)
|
||||
{
|
||||
snd_codec_t *codec;
|
||||
snd_stream_t *stream;
|
||||
const char *ext;
|
||||
|
||||
ext = S_FileExtension(filename);
|
||||
if (!ext)
|
||||
{
|
||||
Con_Printf("No extension for %s\n", filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
codec = codecs;
|
||||
while (codec)
|
||||
{
|
||||
if (!Q_strcasecmp(ext, codec->ext))
|
||||
break;
|
||||
codec = codec->next;
|
||||
}
|
||||
if (!codec)
|
||||
{
|
||||
Con_Printf("Unknown extension for %s\n", filename);
|
||||
return NULL;
|
||||
}
|
||||
stream = codec->codec_open(filename);
|
||||
if (stream)
|
||||
stream->status = STREAM_PLAY;
|
||||
return stream;
|
||||
}
|
||||
|
||||
snd_stream_t *S_CodecOpenStreamAny (const char *filename)
|
||||
{
|
||||
snd_codec_t *codec;
|
||||
snd_stream_t *stream;
|
||||
const char *ext;
|
||||
|
||||
ext = S_FileExtension(filename);
|
||||
if (!ext) /* try all available */
|
||||
{
|
||||
char tmp[MAX_QPATH];
|
||||
|
||||
codec = codecs;
|
||||
while (codec)
|
||||
{
|
||||
q_snprintf(tmp, sizeof(tmp), "%s%s", filename, codec->ext);
|
||||
stream = codec->codec_open(tmp);
|
||||
if (stream)
|
||||
{
|
||||
stream->status = STREAM_PLAY;
|
||||
return stream;
|
||||
}
|
||||
codec = codec->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
else /* use the name as is */
|
||||
{
|
||||
codec = codecs;
|
||||
while (codec)
|
||||
{
|
||||
if (!Q_strcasecmp(ext, codec->ext))
|
||||
break;
|
||||
codec = codec->next;
|
||||
}
|
||||
if (!codec)
|
||||
{
|
||||
Con_Printf("Unknown extension for %s\n", filename);
|
||||
return NULL;
|
||||
}
|
||||
stream = codec->codec_open(filename);
|
||||
if (stream)
|
||||
stream->status = STREAM_PLAY;
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
|
||||
void S_CodecCloseStream (snd_stream_t *stream)
|
||||
{
|
||||
stream->status = STREAM_NONE;
|
||||
stream->codec->codec_close(stream);
|
||||
}
|
||||
|
||||
int S_CodecRewindStream (snd_stream_t *stream)
|
||||
{
|
||||
return stream->codec->codec_rewind(stream);
|
||||
}
|
||||
|
||||
int S_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer)
|
||||
{
|
||||
return stream->codec->codec_read(stream, bytes, buffer);
|
||||
}
|
||||
|
||||
/* Util functions (used by codecs) */
|
||||
|
||||
snd_stream_t *S_CodecUtilOpen(const char *filename, snd_codec_t *codec)
|
||||
{
|
||||
snd_stream_t *stream;
|
||||
FILE *handle;
|
||||
qboolean pak;
|
||||
size_t length;
|
||||
|
||||
/* Try to open the file */
|
||||
length = COM_FOpenFile(filename, &handle, NULL);
|
||||
pak = file_from_pak;
|
||||
if (length == (size_t)-1)
|
||||
{
|
||||
Con_DPrintf("Couldn't open %s\n", filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Allocate a stream, Z_Malloc zeroes its content */
|
||||
stream = (snd_stream_t *) Z_Malloc(sizeof(snd_stream_t));
|
||||
stream->codec = codec;
|
||||
stream->fh.file = handle;
|
||||
stream->fh.start = ftell(handle);
|
||||
stream->fh.pos = 0;
|
||||
stream->fh.length = (int) length;
|
||||
stream->fh.pak = stream->pak = pak;
|
||||
return stream;
|
||||
}
|
||||
|
||||
void S_CodecUtilClose(snd_stream_t **stream)
|
||||
{
|
||||
fclose((*stream)->fh.file);
|
||||
Z_Free(*stream);
|
||||
*stream = NULL;
|
||||
}
|
||||
|
||||
int S_CodecIsAvailable (unsigned int type)
|
||||
{
|
||||
snd_codec_t *codec = codecs;
|
||||
while (codec)
|
||||
{
|
||||
if (type == codec->type)
|
||||
return codec->initialized;
|
||||
codec = codec->next;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
101
Quake/snd_codec.h
Normal file
101
Quake/snd_codec.h
Normal file
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Audio Codecs: Adapted from ioquake3 with changes.
|
||||
* For now, only handles streaming music, not sound effects.
|
||||
*
|
||||
* Copyright (C) 1999-2005 Id Software, Inc.
|
||||
* Copyright (C) 2005 Stuart Dalton <badcdev@gmail.com>
|
||||
* Copyright (C) 2010 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _SND_CODEC_H_
|
||||
#define _SND_CODEC_H_
|
||||
|
||||
typedef struct snd_info_s
|
||||
{
|
||||
int rate;
|
||||
int width;
|
||||
int channels;
|
||||
int samples;
|
||||
int size;
|
||||
int dataofs;
|
||||
} snd_info_t;
|
||||
|
||||
typedef enum {
|
||||
STREAM_NONE = -1,
|
||||
STREAM_INIT,
|
||||
STREAM_PAUSE,
|
||||
STREAM_PLAY
|
||||
} stream_status_t;
|
||||
|
||||
typedef struct snd_codec_s snd_codec_t;
|
||||
|
||||
typedef struct snd_stream_s
|
||||
{
|
||||
fshandle_t fh;
|
||||
qboolean pak;
|
||||
snd_info_t info;
|
||||
stream_status_t status;
|
||||
snd_codec_t *codec; /* codec handling this stream */
|
||||
void *priv; /* data private to the codec. */
|
||||
} snd_stream_t;
|
||||
|
||||
|
||||
void S_CodecInit (void);
|
||||
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);
|
||||
/* Decides according to the required type. */
|
||||
|
||||
snd_stream_t *S_CodecOpenStreamAny (const char *filename);
|
||||
/* Decides according to file extension. if the
|
||||
* name has no extension, try all available. */
|
||||
|
||||
snd_stream_t *S_CodecOpenStreamExt (const char *filename);
|
||||
/* 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);
|
||||
|
||||
snd_stream_t *S_CodecUtilOpen(const char *filename, snd_codec_t *codec);
|
||||
void S_CodecUtilClose(snd_stream_t **stream);
|
||||
|
||||
|
||||
#define CODECTYPE_NONE 0
|
||||
#define CODECTYPE_MID (1 << 0)
|
||||
#define CODECTYPE_MOD (1 << 1)
|
||||
#define CODECTYPE_FLAC (1 << 2)
|
||||
#define CODECTYPE_WAV (1 << 3)
|
||||
#define CODECTYPE_MP3 (1 << 4)
|
||||
#define CODECTYPE_OGG (1 << 5)
|
||||
|
||||
#define CODECTYPE_VORBIS CODECTYPE_OGG
|
||||
#define CODECTYPE_WAVE CODECTYPE_WAV
|
||||
#define CODECTYPE_MIDI CODECTYPE_MID
|
||||
|
||||
int S_CodecIsAvailable (unsigned int type);
|
||||
/* return 1 if available, 0 if codec failed init
|
||||
* or -1 if no such codec is present. */
|
||||
|
||||
#endif /* _SND_CODEC_H_ */
|
||||
|
52
Quake/snd_codeci.h
Normal file
52
Quake/snd_codeci.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Audio Codecs: Adapted from ioquake3 with changes.
|
||||
* For now, only handles streaming music, not sound effects.
|
||||
*
|
||||
* Copyright (C) 1999-2005 Id Software, Inc.
|
||||
* Copyright (C) 2005 Stuart Dalton <badcdev@gmail.com>
|
||||
* Copyright (C) 2010 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _SND_CODECI_H_
|
||||
#define _SND_CODECI_H_
|
||||
|
||||
/* Codec internals */
|
||||
typedef qboolean (*CODEC_INIT)(void);
|
||||
typedef void (*CODEC_SHUTDOWN)(void);
|
||||
typedef snd_stream_t *(*CODEC_OPEN)(const char *filename);
|
||||
typedef int (*CODEC_READ)(snd_stream_t *stream, int bytes, void *buffer);
|
||||
typedef int (*CODEC_REWIND)(snd_stream_t *stream);
|
||||
typedef void (*CODEC_CLOSE)(snd_stream_t *stream);
|
||||
|
||||
struct snd_codec_s
|
||||
{
|
||||
unsigned int type; /* handled data type. */
|
||||
qboolean initialized; /* init succeedded */
|
||||
const char *ext; /* expected extension */
|
||||
CODEC_INIT initialize;
|
||||
CODEC_SHUTDOWN shutdown;
|
||||
CODEC_OPEN codec_open;
|
||||
CODEC_READ codec_read;
|
||||
CODEC_REWIND codec_rewind;
|
||||
CODEC_CLOSE codec_close;
|
||||
snd_codec_t *next;
|
||||
};
|
||||
|
||||
#endif /* _SND_CODECI_H_ */
|
||||
|
|
@ -22,6 +22,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
// snd_dma.c -- main control for any streaming sound output device
|
||||
|
||||
#include "quakedef.h"
|
||||
#include "snd_codec.h"
|
||||
#include "bgmusic.h"
|
||||
|
||||
static void S_Play (void);
|
||||
static void S_PlayVol (void);
|
||||
|
@ -192,6 +194,8 @@ void S_Init (void)
|
|||
ambient_sfx[AMBIENT_WATER] = S_PrecacheSound ("ambience/water1.wav");
|
||||
ambient_sfx[AMBIENT_SKY] = S_PrecacheSound ("ambience/wind2.wav");
|
||||
|
||||
S_CodecInit ();
|
||||
|
||||
S_StopAllSounds (true);
|
||||
}
|
||||
|
||||
|
@ -207,6 +211,8 @@ void S_Shutdown (void)
|
|||
sound_started = 0;
|
||||
snd_blocked = 0;
|
||||
|
||||
S_CodecShutdown();
|
||||
|
||||
SNDDMA_Shutdown();
|
||||
shm = NULL;
|
||||
}
|
||||
|
@ -856,6 +862,9 @@ void S_Update (vec3_t origin, vec3_t forward, vec3_t right, vec3_t up)
|
|||
Con_Printf ("----(%i)----\n", total);
|
||||
}
|
||||
|
||||
// add raw data from streamed samples
|
||||
// BGM_Update(); // moved to the main loop just before S_Update ()
|
||||
|
||||
// mix some sound
|
||||
S_Update_();
|
||||
}
|
||||
|
|
466
Quake/snd_mp3.c
Normal file
466
Quake/snd_mp3.c
Normal file
|
@ -0,0 +1,466 @@
|
|||
/*
|
||||
* MP3 decoding support using libmad: Adapted from the SoX library at
|
||||
* http://sourceforge.net/projects/sox/, LGPLv2, Copyright (c) 2007-2009
|
||||
* SoX contributors, written by Fabrizio Gennari <fabrizio.ge@tiscali.it>,
|
||||
* with the decoding part based on the decoder tutorial program madlld
|
||||
* written by Bertrand Petit <madlld@phoe.fmug.org> (BSD license, see at
|
||||
* http://www.bsd-dk.dk/~elrond/audio/madlld/). The tag identification
|
||||
* functions were adapted from the GPL-licensed libid3tag library, see at
|
||||
* http://www.underbit.com/products/mad/.
|
||||
*
|
||||
* 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_MP3)
|
||||
#include "snd_codec.h"
|
||||
#include "snd_codeci.h"
|
||||
#include "snd_mp3.h"
|
||||
#include <mad.h>
|
||||
|
||||
#define ID3_TAG_FLAG_FOOTERPRESENT 0x10
|
||||
|
||||
/* Under Windows, importing data from DLLs is a dicey proposition. This is true
|
||||
* when using dlopen, but also true if linking directly against the DLL if the
|
||||
* header does not mark the data as __declspec(dllexport), which mad.h does not.
|
||||
* Sidestep the issue by defining our own mad_timer_zero. This is needed because
|
||||
* mad_timer_zero is used in some of the mad.h macros.
|
||||
*/
|
||||
#define mad_timer_zero mad_timer_zero_stub
|
||||
static mad_timer_t const mad_timer_zero_stub = {0, 0};
|
||||
|
||||
/* MAD returns values with MAD_F_FRACBITS (28) bits of precision, though it's
|
||||
not certain that all of them are meaningful. Default to 16 bits to
|
||||
align with most users expectation of output file should be 16 bits. */
|
||||
#define MP3_MAD_SAMPLEBITS 16
|
||||
#define MP3_MAD_SAMPLEWIDTH (MP3_MAD_SAMPLEBITS / 8)
|
||||
#define MP3_BUFFER_SIZE (5 * 8192)
|
||||
|
||||
/* Private data */
|
||||
typedef struct _mp3_priv_t
|
||||
{
|
||||
unsigned char mp3_buffer[MP3_BUFFER_SIZE];
|
||||
struct mad_stream Stream;
|
||||
struct mad_frame Frame;
|
||||
struct mad_synth Synth;
|
||||
mad_timer_t Timer;
|
||||
ptrdiff_t cursamp;
|
||||
size_t FrameCount;
|
||||
} mp3_priv_t;
|
||||
|
||||
/* This function merges the functions tagtype() and id3_tag_query()
|
||||
* from MAD's libid3tag, so we don't have to link to it
|
||||
* Returns 0 if the frame is not an ID3 tag, tag length if it is */
|
||||
static qboolean tag_is_id3v1(const unsigned char *data, size_t length)
|
||||
{
|
||||
if (length >= 3 &&
|
||||
data[0] == 'T' && data[1] == 'A' && data[2] == 'G')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static qboolean tag_is_id3v2(const unsigned char *data, size_t length)
|
||||
{
|
||||
if (length >= 10 &&
|
||||
(data[0] == 'I' && data[1] == 'D' && data[2] == '3') &&
|
||||
data[3] < 0xff && data[4] < 0xff &&
|
||||
data[6] < 0x80 && data[7] < 0x80 && data[8] < 0x80 && data[9] < 0x80)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int mp3_tagsize(const unsigned char *data, size_t length)
|
||||
{
|
||||
if (tag_is_id3v1(data, length))
|
||||
return 128;
|
||||
|
||||
if (tag_is_id3v2(data, length))
|
||||
{
|
||||
unsigned char flags;
|
||||
unsigned int size;
|
||||
flags = data[5];
|
||||
size = 10 + (data[6]<<21) + (data[7]<<14) + (data[8]<<7) + data[9];
|
||||
if (flags & ID3_TAG_FLAG_FOOTERPRESENT)
|
||||
size += 10;
|
||||
for ( ; size < length && !data[size]; ++size)
|
||||
; /* Consume padding */
|
||||
return size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Attempts to read an ID3 tag at the current location in stream and
|
||||
* consume it all. Returns -1 if no tag is found. Its up to caller
|
||||
* to recover. */
|
||||
static int mp3_inputtag(snd_stream_t *stream)
|
||||
{
|
||||
mp3_priv_t *p = (mp3_priv_t *) stream->priv;
|
||||
int rc = -1;
|
||||
size_t remaining;
|
||||
size_t tagsize;
|
||||
|
||||
/* FIXME: This needs some more work if we are to ever
|
||||
* look at the ID3 frame. This is because the Stream
|
||||
* may not be able to hold the complete ID3 frame.
|
||||
* We should consume the whole frame inside tagtype()
|
||||
* instead of outside of tagframe(). That would support
|
||||
* recovering when Stream contains less then 8-bytes (header)
|
||||
* and also when ID3v2 is bigger then Stream buffer size.
|
||||
* Need to pass in stream so that buffer can be
|
||||
* consumed as well as letting additional data to be
|
||||
* read in.
|
||||
*/
|
||||
remaining = p->Stream.bufend - p->Stream.next_frame;
|
||||
tagsize = mp3_tagsize(p->Stream.this_frame, remaining);
|
||||
if (tagsize != 0)
|
||||
{
|
||||
mad_stream_skip(&p->Stream, tagsize);
|
||||
rc = 0;
|
||||
}
|
||||
|
||||
/* We know that a valid frame hasn't been found yet
|
||||
* so help libmad out and go back into frame seek mode.
|
||||
* This is true whether an ID3 tag was found or not.
|
||||
*/
|
||||
mad_stream_sync(&p->Stream);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* (Re)fill the stream buffer that is to be decoded. If any data
|
||||
* still exists in the buffer then they are first shifted to be
|
||||
* front of the stream buffer. */
|
||||
static int mp3_inputdata(snd_stream_t *stream)
|
||||
{
|
||||
mp3_priv_t *p = (mp3_priv_t *) stream->priv;
|
||||
size_t bytes_read;
|
||||
size_t remaining;
|
||||
|
||||
remaining = p->Stream.bufend - p->Stream.next_frame;
|
||||
|
||||
/* libmad does not consume all the buffer it's given. Some
|
||||
* data, part of a truncated frame, is left unused at the
|
||||
* end of the buffer. That data must be put back at the
|
||||
* beginning of the buffer and taken in account for
|
||||
* refilling the buffer. This means that the input buffer
|
||||
* must be large enough to hold a complete frame at the
|
||||
* highest observable bit-rate (currently 448 kb/s).
|
||||
* TODO: Is 2016 bytes the size of the largest frame?
|
||||
* (448000*(1152/32000))/8
|
||||
*/
|
||||
memmove(p->mp3_buffer, p->Stream.next_frame, remaining);
|
||||
|
||||
bytes_read = FS_fread(p->mp3_buffer + remaining, 1,
|
||||
MP3_BUFFER_SIZE - remaining, &stream->fh);
|
||||
if (bytes_read == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
mad_stream_buffer(&p->Stream, p->mp3_buffer, bytes_read+remaining);
|
||||
p->Stream.error = MAD_ERROR_NONE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mp3_startread(snd_stream_t *stream)
|
||||
{
|
||||
mp3_priv_t *p = (mp3_priv_t *) stream->priv;
|
||||
size_t ReadSize;
|
||||
|
||||
mad_stream_init(&p->Stream);
|
||||
mad_frame_init(&p->Frame);
|
||||
mad_synth_init(&p->Synth);
|
||||
mad_timer_reset(&p->Timer);
|
||||
|
||||
/* Decode at least one valid frame to find out the input
|
||||
* format. The decoded frame will be saved off so that it
|
||||
* can be processed later.
|
||||
*/
|
||||
ReadSize = FS_fread(p->mp3_buffer, 1, MP3_BUFFER_SIZE, &stream->fh);
|
||||
if (ReadSize != MP3_BUFFER_SIZE)
|
||||
{
|
||||
if (FS_feof(&stream->fh) || FS_ferror(&stream->fh))
|
||||
return -1;
|
||||
}
|
||||
|
||||
mad_stream_buffer(&p->Stream, p->mp3_buffer, ReadSize);
|
||||
|
||||
/* Find a valid frame before starting up. This makes sure
|
||||
* that we have a valid MP3 and also skips past ID3v2 tags
|
||||
* at the beginning of the audio file.
|
||||
*/
|
||||
p->Stream.error = MAD_ERROR_NONE;
|
||||
while (mad_frame_decode(&p->Frame,&p->Stream))
|
||||
{
|
||||
/* check whether input buffer needs a refill */
|
||||
if (p->Stream.error == MAD_ERROR_BUFLEN)
|
||||
{
|
||||
if (mp3_inputdata(stream) == -1)
|
||||
return -1;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Consume any ID3 tags */
|
||||
mp3_inputtag(stream);
|
||||
|
||||
/* FIXME: We should probably detect when we've read
|
||||
* a bunch of non-ID3 data and still haven't found a
|
||||
* frame. In that case we can abort early without
|
||||
* scanning the whole file.
|
||||
*/
|
||||
p->Stream.error = MAD_ERROR_NONE;
|
||||
}
|
||||
|
||||
if (p->Stream.error)
|
||||
{
|
||||
Con_Printf("MP3: No valid MP3 frame found\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch(p->Frame.header.mode)
|
||||
{
|
||||
case MAD_MODE_SINGLE_CHANNEL:
|
||||
case MAD_MODE_DUAL_CHANNEL:
|
||||
case MAD_MODE_JOINT_STEREO:
|
||||
case MAD_MODE_STEREO:
|
||||
stream->info.channels = MAD_NCHANNELS(&p->Frame.header);
|
||||
break;
|
||||
default:
|
||||
Con_Printf("MP3: Cannot determine number of channels\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
p->FrameCount = 1;
|
||||
|
||||
mad_timer_add(&p->Timer,p->Frame.header.duration);
|
||||
mad_synth_frame(&p->Synth,&p->Frame);
|
||||
stream->info.width = MP3_MAD_SAMPLEWIDTH;
|
||||
stream->info.rate = p->Synth.pcm.samplerate;
|
||||
|
||||
p->cursamp = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read up to len samples from p->Synth
|
||||
* If needed, read some more MP3 data, decode them and synth them
|
||||
* Place in buf[].
|
||||
* Return number of samples read. */
|
||||
static int mp3_decode(snd_stream_t *stream, byte *buf, int len)
|
||||
{
|
||||
mp3_priv_t *p = (mp3_priv_t *) stream->priv;
|
||||
int donow, i, done = 0;
|
||||
mad_fixed_t sample;
|
||||
int chan, x;
|
||||
|
||||
do
|
||||
{
|
||||
x = (p->Synth.pcm.length - p->cursamp) * stream->info.channels;
|
||||
donow = q_min(len, x);
|
||||
i = 0;
|
||||
while (i < donow)
|
||||
{
|
||||
for (chan = 0; chan < stream->info.channels; chan++)
|
||||
{
|
||||
sample = p->Synth.pcm.samples[chan][p->cursamp];
|
||||
/* convert from fixed to short,
|
||||
* write in host-endian format. */
|
||||
if (sample <= -MAD_F_ONE)
|
||||
sample = -0x7FFF;
|
||||
else if (sample >= MAD_F_ONE)
|
||||
sample = 0x7FFF;
|
||||
else
|
||||
sample >>= (MAD_F_FRACBITS + 1 - 16);
|
||||
if (bigendien)
|
||||
{
|
||||
*buf++ = (sample >> 8) & 0xFF;
|
||||
*buf++ = sample & 0xFF;
|
||||
}
|
||||
else
|
||||
{ /* LITTLE_ENDIAN */
|
||||
*buf++ = sample & 0xFF;
|
||||
*buf++ = (sample >> 8) & 0xFF;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
p->cursamp++;
|
||||
};
|
||||
|
||||
len -= donow;
|
||||
done += donow;
|
||||
|
||||
if (len == 0)
|
||||
break;
|
||||
|
||||
/* check whether input buffer needs a refill */
|
||||
if (p->Stream.error == MAD_ERROR_BUFLEN)
|
||||
{
|
||||
if (mp3_inputdata(stream) == -1)
|
||||
{
|
||||
/* check feof() ?? */
|
||||
Con_DPrintf("mp3 EOF\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mad_frame_decode(&p->Frame, &p->Stream))
|
||||
{
|
||||
if (MAD_RECOVERABLE(p->Stream.error))
|
||||
{
|
||||
mp3_inputtag(stream);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (p->Stream.error == MAD_ERROR_BUFLEN)
|
||||
continue;
|
||||
else
|
||||
{
|
||||
Con_Printf("MP3: unrecoverable frame level error (%s)\n",
|
||||
mad_stream_errorstr(&p->Stream));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
p->FrameCount++;
|
||||
mad_timer_add(&p->Timer, p->Frame.header.duration);
|
||||
mad_synth_frame(&p->Synth, &p->Frame);
|
||||
p->cursamp = 0;
|
||||
} while (1);
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
static int mp3_stopread(snd_stream_t *stream)
|
||||
{
|
||||
mp3_priv_t *p = (mp3_priv_t*) stream->priv;
|
||||
|
||||
mad_synth_finish(&p->Synth);
|
||||
mad_frame_finish(&p->Frame);
|
||||
mad_stream_finish(&p->Stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static qboolean S_MP3_CodecInitialize (void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void S_MP3_CodecShutdown (void)
|
||||
{
|
||||
}
|
||||
|
||||
static int MP3_check_file (snd_stream_t *stream)
|
||||
{
|
||||
unsigned char magic[16];
|
||||
|
||||
if (FS_fread(magic, 1, 16, &stream->fh) != 16)
|
||||
return -1;
|
||||
if ((magic[0] == 0xFF && (magic[1] & 0xF0) == 0xF0) ||
|
||||
tag_is_id3v2(magic, 16))
|
||||
return 0;
|
||||
if (FS_fseek(&stream->fh, -128, SEEK_END) < 0)
|
||||
return -1;
|
||||
if (FS_fread(magic, 1, 16, &stream->fh) != 16)
|
||||
return -1;
|
||||
if (tag_is_id3v1(magic, 16))
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static snd_stream_t *S_MP3_CodecOpenStream (const char *filename)
|
||||
{
|
||||
snd_stream_t *stream;
|
||||
|
||||
stream = S_CodecUtilOpen(filename, &mp3_codec);
|
||||
if (!stream)
|
||||
return NULL;
|
||||
|
||||
stream->priv = Z_Malloc(sizeof(mp3_priv_t));
|
||||
|
||||
/* check some rudimentary info before feeding it to libmad. */
|
||||
if (MP3_check_file(stream) < 0)
|
||||
{
|
||||
/*
|
||||
Con_Printf("%s format couldn't be identified.\n", filename);
|
||||
Z_Free(stream->priv);
|
||||
S_CodecUtilClose(&stream);
|
||||
return NULL;
|
||||
*/
|
||||
Con_DPrintf("%s has no identifier tags.\n", filename);
|
||||
}
|
||||
|
||||
FS_rewind(&stream->fh);
|
||||
|
||||
if (mp3_startread(stream) < 0)
|
||||
{
|
||||
Con_Printf("%s is not a valid MP3 file.\n", filename);
|
||||
Z_Free(stream->priv);
|
||||
S_CodecUtilClose(&stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
static int S_MP3_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer)
|
||||
{
|
||||
int res = mp3_decode(stream, (byte *)buffer, bytes / stream->info.width);
|
||||
return res * stream->info.width;
|
||||
}
|
||||
|
||||
static void S_MP3_CodecCloseStream (snd_stream_t *stream)
|
||||
{
|
||||
mp3_stopread(stream);
|
||||
Z_Free(stream->priv);
|
||||
S_CodecUtilClose(&stream);
|
||||
}
|
||||
|
||||
static int S_MP3_CodecRewindStream (snd_stream_t *stream)
|
||||
{
|
||||
/* FIXME: do this better */
|
||||
mp3_stopread(stream);
|
||||
FS_rewind(&stream->fh);
|
||||
return mp3_startread(stream);
|
||||
}
|
||||
|
||||
snd_codec_t mp3_codec =
|
||||
{
|
||||
CODECTYPE_MP3,
|
||||
true, /* always available. */
|
||||
".mp3",
|
||||
S_MP3_CodecInitialize,
|
||||
S_MP3_CodecShutdown,
|
||||
S_MP3_CodecOpenStream,
|
||||
S_MP3_CodecReadStream,
|
||||
S_MP3_CodecRewindStream,
|
||||
S_MP3_CodecCloseStream,
|
||||
NULL
|
||||
};
|
||||
|
||||
#endif /* USE_CODEC_MP3 */
|
||||
|
13
Quake/snd_mp3.h
Normal file
13
Quake/snd_mp3.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
/* MP3 decoding support using libmad. */
|
||||
|
||||
#if !defined(_SND_MP3_H_)
|
||||
#define _SND_MP3_H_
|
||||
|
||||
#if defined(USE_CODEC_MP3)
|
||||
|
||||
extern snd_codec_t mp3_codec;
|
||||
|
||||
#endif /* USE_CODEC_MP3 */
|
||||
|
||||
#endif /* ! _SND_MP3_H_ */
|
||||
|
169
Quake/snd_vorbis.c
Normal file
169
Quake/snd_vorbis.c
Normal file
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* Ogg/Vorbis streaming music support, adapted from several open source
|
||||
* Quake engine based projects with many modifications.
|
||||
*
|
||||
* 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_VORBIS)
|
||||
#include "snd_codec.h"
|
||||
#include "snd_codeci.h"
|
||||
#include "snd_vorbis.h"
|
||||
#define OV_EXCLUDE_STATIC_CALLBACKS
|
||||
#include <vorbis/vorbisfile.h>
|
||||
|
||||
/* The OGG codec can return the samples in a number of different
|
||||
* formats, we use the standard signed short format. */
|
||||
#define OGG_SAMPLEWIDTH 2
|
||||
#define OGG_SIGNED_DATA 1
|
||||
|
||||
static int ogg_bigendian = 0;
|
||||
|
||||
/* CALLBACK FUNCTIONS: */
|
||||
|
||||
static int ovc_fclose (void *f)
|
||||
{
|
||||
return 0; /* we fclose() elsewhere. */
|
||||
}
|
||||
|
||||
static int ovc_fseek (void *f, ogg_int64_t off, int whence)
|
||||
{
|
||||
if (f == NULL) return (-1);
|
||||
return FS_fseek((fshandle_t *)f, (long) off, whence);
|
||||
}
|
||||
|
||||
static const ov_callbacks ovc_qfs =
|
||||
{
|
||||
(size_t (*)(void *, size_t, size_t, void *)) FS_fread,
|
||||
(int (*)(void *, ogg_int64_t, int)) ovc_fseek,
|
||||
(int (*)(void *)) ovc_fclose,
|
||||
(long (*)(void *)) FS_ftell
|
||||
};
|
||||
|
||||
static qboolean S_OGG_CodecInitialize (void)
|
||||
{
|
||||
ogg_bigendian = (bigendien == true) ? 1 : 0;
|
||||
ogg_codec.initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void S_OGG_CodecShutdown (void)
|
||||
{
|
||||
}
|
||||
|
||||
static snd_stream_t *S_OGG_CodecOpenStream (const char *filename)
|
||||
{
|
||||
snd_stream_t *stream;
|
||||
OggVorbis_File *ovFile;
|
||||
vorbis_info *ogg_info;
|
||||
int res;
|
||||
|
||||
stream = S_CodecUtilOpen(filename, &ogg_codec);
|
||||
if (!stream)
|
||||
return NULL;
|
||||
|
||||
ovFile = (OggVorbis_File *) Z_Malloc(sizeof(OggVorbis_File));
|
||||
res = ov_open_callbacks(&stream->fh, ovFile, NULL, 0, ovc_qfs);
|
||||
if (res < 0)
|
||||
{
|
||||
Con_Printf("%s is not a valid Ogg Vorbis file (error %i).\n", filename, res);
|
||||
Z_Free(ovFile);
|
||||
S_CodecUtilClose(&stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
stream->priv = ovFile;
|
||||
|
||||
if (!ov_seekable(ovFile))
|
||||
{
|
||||
Con_Printf("OGG_Open: stream %s not seekable.\n", filename);
|
||||
ov_clear(ovFile);
|
||||
Z_Free(ovFile);
|
||||
S_CodecUtilClose(&stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ogg_info = ov_info(ovFile, 0);
|
||||
if (!ogg_info)
|
||||
{
|
||||
Con_Printf("Unable to get stream information for %s.\n", filename);
|
||||
ov_clear(ovFile);
|
||||
Z_Free(ovFile);
|
||||
S_CodecUtilClose(&stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
stream->info.rate = ogg_info->rate;
|
||||
stream->info.channels = ogg_info->channels;
|
||||
stream->info.width = OGG_SAMPLEWIDTH;
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
static int S_OGG_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer)
|
||||
{
|
||||
int section; /* FIXME: handle section changes */
|
||||
int cnt, res, rem;
|
||||
char * ptr;
|
||||
|
||||
cnt = 0; rem = bytes;
|
||||
ptr = (char *) buffer;
|
||||
while (1)
|
||||
{
|
||||
res = ov_read((OggVorbis_File *) stream->priv, ptr, rem, ogg_bigendian,
|
||||
OGG_SAMPLEWIDTH, OGG_SIGNED_DATA, §ion);
|
||||
if (res <= 0)
|
||||
break;
|
||||
rem -= res;
|
||||
cnt += res;
|
||||
if (rem <= 0)
|
||||
break;
|
||||
ptr += res;
|
||||
}
|
||||
return (res >= 0) ? cnt : res;
|
||||
}
|
||||
|
||||
static void S_OGG_CodecCloseStream (snd_stream_t *stream)
|
||||
{
|
||||
ov_clear((OggVorbis_File *)stream->priv);
|
||||
Z_Free(stream->priv);
|
||||
S_CodecUtilClose(&stream);
|
||||
}
|
||||
|
||||
static int S_OGG_CodecRewindStream (snd_stream_t *stream)
|
||||
{
|
||||
return ov_time_seek ((OggVorbis_File *)stream->priv, 0.0);
|
||||
}
|
||||
|
||||
snd_codec_t ogg_codec =
|
||||
{
|
||||
CODECTYPE_OGG,
|
||||
false,
|
||||
".ogg",
|
||||
S_OGG_CodecInitialize,
|
||||
S_OGG_CodecShutdown,
|
||||
S_OGG_CodecOpenStream,
|
||||
S_OGG_CodecReadStream,
|
||||
S_OGG_CodecRewindStream,
|
||||
S_OGG_CodecCloseStream,
|
||||
NULL
|
||||
};
|
||||
|
||||
#endif /* USE_CODEC_VORBIS */
|
||||
|
13
Quake/snd_vorbis.h
Normal file
13
Quake/snd_vorbis.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
/* Ogg/Vorbis streaming music support. */
|
||||
|
||||
#if !defined(_SND_VORBIS_H_)
|
||||
#define _SND_VORBIS_H_
|
||||
|
||||
#if defined(USE_CODEC_VORBIS)
|
||||
|
||||
extern snd_codec_t ogg_codec;
|
||||
|
||||
#endif /* USE_CODEC_VORBIS */
|
||||
|
||||
#endif /* ! _SND_VORBIS_H_ */
|
||||
|
282
Quake/snd_wave.c
Normal file
282
Quake/snd_wave.c
Normal file
|
@ -0,0 +1,282 @@
|
|||
/*
|
||||
* WAV streaming music support. Adapted from ioquake3 with changes.
|
||||
*
|
||||
* Copyright (C) 1999-2005 Id Software, Inc.
|
||||
* Copyright (C) 2005 Stuart Dalton <badcdev@gmail.com>
|
||||
* Copyright (C) 2010 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_WAVE)
|
||||
#include "snd_codec.h"
|
||||
#include "snd_codeci.h"
|
||||
#include "snd_wave.h"
|
||||
|
||||
/*
|
||||
=================
|
||||
FGetLittleLong
|
||||
=================
|
||||
*/
|
||||
static int FGetLittleLong (FILE *f)
|
||||
{
|
||||
int v;
|
||||
|
||||
fread(&v, 1, sizeof(v), f);
|
||||
|
||||
return LittleLong(v);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
FGetLittleShort
|
||||
=================
|
||||
*/
|
||||
static short FGetLittleShort(FILE *f)
|
||||
{
|
||||
short v;
|
||||
|
||||
fread(&v, 1, sizeof(v), f);
|
||||
|
||||
return LittleShort(v);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
WAV_ReadChunkInfo
|
||||
=================
|
||||
*/
|
||||
static int WAV_ReadChunkInfo(FILE *f, char *name)
|
||||
{
|
||||
int len, r;
|
||||
|
||||
name[4] = 0;
|
||||
|
||||
r = fread(name, 1, 4, f);
|
||||
if (r != 4)
|
||||
return -1;
|
||||
|
||||
len = FGetLittleLong(f);
|
||||
if (len < 0)
|
||||
{
|
||||
Con_Printf("WAV: Negative chunk length\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
WAV_FindRIFFChunk
|
||||
|
||||
Returns the length of the data in the chunk, or -1 if not found
|
||||
=================
|
||||
*/
|
||||
static int WAV_FindRIFFChunk(FILE *f, const char *chunk)
|
||||
{
|
||||
char name[5];
|
||||
int len;
|
||||
|
||||
while ((len = WAV_ReadChunkInfo(f, name)) >= 0)
|
||||
{
|
||||
/* If this is the right chunk, return */
|
||||
if (!strncmp(name, chunk, 4))
|
||||
return len;
|
||||
len = ((len + 1) & ~1); /* pad by 2 . */
|
||||
|
||||
/* Not the right chunk - skip it */
|
||||
fseek(f, len, SEEK_CUR);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
WAV_ReadRIFFHeader
|
||||
=================
|
||||
*/
|
||||
static qboolean WAV_ReadRIFFHeader(const char *name, FILE *file, snd_info_t *info)
|
||||
{
|
||||
char dump[16];
|
||||
int wav_format;
|
||||
int bits;
|
||||
int fmtlen = 0;
|
||||
|
||||
if (fread(dump, 1, 12, file) < 12 ||
|
||||
strncmp(dump, "RIFF", 4) != 0 ||
|
||||
strncmp(&dump[8], "WAVE", 4) != 0)
|
||||
{
|
||||
Con_Printf("%s is missing RIFF/WAVE chunks\n", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Scan for the format chunk */
|
||||
if ((fmtlen = WAV_FindRIFFChunk(file, "fmt ")) < 0)
|
||||
{
|
||||
Con_Printf("%s is missing fmt chunk\n", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Save the parameters */
|
||||
wav_format = FGetLittleShort(file);
|
||||
if (wav_format != WAV_FORMAT_PCM)
|
||||
{
|
||||
Con_Printf("%s is not Microsoft PCM format\n", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
info->channels = FGetLittleShort(file);
|
||||
info->rate = FGetLittleLong(file);
|
||||
FGetLittleLong(file);
|
||||
FGetLittleShort(file);
|
||||
bits = FGetLittleShort(file);
|
||||
|
||||
if (bits != 8 && bits != 16)
|
||||
{
|
||||
Con_Printf("%s is not 8 or 16 bit\n", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
info->width = bits / 8;
|
||||
info->dataofs = 0;
|
||||
|
||||
/* Skip the rest of the format chunk if required */
|
||||
if (fmtlen > 16)
|
||||
{
|
||||
fmtlen -= 16;
|
||||
fseek(file, fmtlen, SEEK_CUR);
|
||||
}
|
||||
|
||||
/* Scan for the data chunk */
|
||||
if ((info->size = WAV_FindRIFFChunk(file, "data")) < 0)
|
||||
{
|
||||
Con_Printf("%s is missing data chunk\n", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
info->samples = (info->size / info->width) / info->channels;
|
||||
if (info->samples == 0)
|
||||
{
|
||||
Con_Printf("%s has zero samples\n", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
S_WAV_CodecOpenStream
|
||||
=================
|
||||
*/
|
||||
snd_stream_t *S_WAV_CodecOpenStream(const char *filename)
|
||||
{
|
||||
snd_stream_t *stream;
|
||||
long start;
|
||||
|
||||
stream = S_CodecUtilOpen(filename, &wav_codec);
|
||||
if (!stream)
|
||||
return NULL;
|
||||
|
||||
start = stream->fh.start;
|
||||
|
||||
/* Read the RIFF header */
|
||||
/* The file reads are sequential, therefore no need
|
||||
* for the FS_*() functions: We will manipulate the
|
||||
* file by ourselves from now on. */
|
||||
if (!WAV_ReadRIFFHeader(filename, stream->fh.file, &stream->info))
|
||||
{
|
||||
S_CodecUtilClose(&stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
stream->fh.start = ftell(stream->fh.file); /* reset to data position */
|
||||
if (stream->fh.start - start + stream->info.size > stream->fh.length)
|
||||
{
|
||||
Con_Printf("%s data size mismatch\n", filename);
|
||||
S_CodecUtilClose(&stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
S_WAV_CodecReadStream
|
||||
=================
|
||||
*/
|
||||
int S_WAV_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer)
|
||||
{
|
||||
int remaining = stream->info.size - stream->fh.pos;
|
||||
int i, samples;
|
||||
|
||||
if (remaining <= 0)
|
||||
return 0;
|
||||
if (bytes > remaining)
|
||||
bytes = remaining;
|
||||
stream->fh.pos += bytes;
|
||||
fread(buffer, 1, bytes, stream->fh.file);
|
||||
if (stream->info.width == 2)
|
||||
{
|
||||
samples = bytes / 2;
|
||||
for (i = 0; i < samples; i++)
|
||||
((short *)buffer)[i] = LittleShort( ((short *)buffer)[i] );
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static void S_WAV_CodecCloseStream (snd_stream_t *stream)
|
||||
{
|
||||
S_CodecUtilClose(&stream);
|
||||
}
|
||||
|
||||
static int S_WAV_CodecRewindStream (snd_stream_t *stream)
|
||||
{
|
||||
FS_rewind(&stream->fh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static qboolean S_WAV_CodecInitialize (void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void S_WAV_CodecShutdown (void)
|
||||
{
|
||||
}
|
||||
|
||||
snd_codec_t wav_codec =
|
||||
{
|
||||
CODECTYPE_WAVE,
|
||||
true, /* always available. */
|
||||
".wav",
|
||||
S_WAV_CodecInitialize,
|
||||
S_WAV_CodecShutdown,
|
||||
S_WAV_CodecOpenStream,
|
||||
S_WAV_CodecReadStream,
|
||||
S_WAV_CodecRewindStream,
|
||||
S_WAV_CodecCloseStream,
|
||||
NULL
|
||||
};
|
||||
|
||||
#endif /* USE_CODEC_WAVE */
|
||||
|
13
Quake/snd_wave.h
Normal file
13
Quake/snd_wave.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
/* WAV streaming music support. */
|
||||
|
||||
#if !defined(_SND_WAVE_H_)
|
||||
#define _SND_WAVE_H_
|
||||
|
||||
#if defined(USE_CODEC_WAVE)
|
||||
|
||||
extern snd_codec_t wav_codec;
|
||||
|
||||
#endif /* USE_CODEC_WAVE */
|
||||
|
||||
#endif /* ! _SND_WAVE_H_ */
|
||||
|
Loading…
Reference in a new issue