Commit 93468f1b authored by Adriano Moura's avatar Adriano Moura
Browse files

Import as much of the bgm sytem as possible

parent 8759023d
......@@ -3,6 +3,21 @@ FRONTEND_SUPPORTS_RGB565=1
TARGET_NAME=tyrquake
STATIC_LINKING=0
USE_CODEC_WAVE=1
USE_CODEC_FLAC=1
USE_CODEC_MP3=0
USE_CODEC_VORBIS=1
USE_CODEC_OPUS=0
# either mikmod (preferred) or modplug, not both
USE_CODEC_MIKMOD=0
USE_CODEC_MODPLUG=0
USE_CODEC_UMX=0
# which library to use for mp3 decoding: mad or mpg123
MP3LIB=mad
# which library to use for ogg decoding: vorbis or tremor
VORBISLIB=vorbis
ifeq ($(platform),)
platform = unix
ifeq ($(shell uname -a),)
......@@ -265,9 +280,76 @@ else
CFLAGS += -O3 -DNDEBUG
endif
LDFLAGS += $(LIBM)
CORE_DIR := .
ifneq ($(VORBISLIB),vorbis)
ifneq ($(VORBISLIB),tremor)
$(error Invalid VORBISLIB setting)
endif
endif
ifneq ($(MP3LIB),mpg123)
ifneq ($(MP3LIB),mad)
$(error Invalid MP3LIB setting)
endif
endif
ifeq ($(MP3LIB),mad)
mp3_obj=snd_mp3.o
lib_mp3dec=-lmad
endif
ifeq ($(MP3LIB),mpg123)
mp3_obj=snd_mpg123.o
lib_mp3dec=-lmpg123
endif
ifeq ($(VORBISLIB),vorbis)
cpp_vorbisdec=
lib_vorbisdec=-lvorbisfile -lvorbis -logg
endif
ifeq ($(VORBISLIB),tremor)
cpp_vorbisdec=-DVORBIS_USE_TREMOR
lib_vorbisdec=-lvorbisidec -logg
endif
CODECLIBS :=
ifeq ($(USE_CODEC_WAVE),1)
CFLAGS+= -DUSE_CODEC_WAVE
endif
ifeq ($(USE_CODEC_FLAC),1)
CFLAGS+= -DUSE_CODEC_FLAC
CODECLIBS+= -lFLAC
endif
ifeq ($(USE_CODEC_OPUS),1)
# opus and opusfile put their *.h under <includedir>/opus,
# but they include the headers without the opus directory
# prefix and rely on pkg-config. ewww...
CFLAGS+= -DUSE_CODEC_OPUS
CFLAGS+= $(shell pkg-config --cflags opusfile)
CODECLIBS+= $(shell pkg-config --libs opusfile)
endif
ifeq ($(USE_CODEC_VORBIS),1)
CFLAGS+= -DUSE_CODEC_VORBIS $(cpp_vorbisdec)
CODECLIBS+= $(lib_vorbisdec)
endif
ifeq ($(USE_CODEC_MP3),1)
CFLAGS+= -DUSE_CODEC_MP3
CODECLIBS+= $(lib_mp3dec)
endif
ifeq ($(USE_CODEC_MIKMOD),1)
CFLAGS+= -DUSE_CODEC_MIKMOD
CODECLIBS+= -lmikmod
endif
ifeq ($(USE_CODEC_MODPLUG),1)
CFLAGS+= -DUSE_CODEC_MODPLUG
CODECLIBS+= -lmodplug
endif
ifeq ($(USE_CODEC_UMX),1)
CFLAGS+= -DUSE_CODEC_UMX
endif
LDFLAGS += $(CODECLIBS)
include Makefile.common
OBJECTS = $(SOURCES_C:.c=.o)
......
......@@ -62,6 +62,16 @@ SOURCES_C := \
$(CORE_DIR)/common/screen.c \
$(CORE_DIR)/common/shell.c \
$(CORE_DIR)/common/bgmusic.c \
$(CORE_DIR)/common/snd_codec.c \
$(CORE_DIR)/common/snd_flac.c \
$(CORE_DIR)/common/snd_mikmod.c \
$(CORE_DIR)/common/snd_modplug.c \
$(CORE_DIR)/common/snd_mp3.c \
$(CORE_DIR)/common/snd_mpg123.c \
$(CORE_DIR)/common/snd_opus.c \
$(CORE_DIR)/common/snd_umx.c \
$(CORE_DIR)/common/snd_vorbis.c \
$(CORE_DIR)/common/snd_wave.c \
$(CORE_DIR)/common/snd_dma.c \
$(CORE_DIR)/common/snd_mem.c \
$(CORE_DIR)/common/snd_mix.c \
......
/*
* Background music handling for Quakespasm (adapted from uHexen2)
* Handles streaming music as raw sound samples and runs the midi driver
*
* Copyright (C) 1999-2005 Id Software, Inc.
* Copyright (C) 2010-2012 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 "console.h"
#include "common.h"
......@@ -5,31 +29,431 @@
#include "sound.h"
#include "cdaudio.h"
#include "snd_codec.h"
#include "bgmusic.h"
qboolean BGM_Init (void) {
#define MUSIC_DIRNAME "music"
qboolean bgmloop;
cvar_t bgm_extmusic = {"bgm_extmusic", "1", false};
static qboolean no_extmusic= false;
static float old_volume = -1.0f;
typedef enum _bgm_player
{
BGM_NONE = -1,
BGM_MIDIDRV = 1,
BGM_STREAMER
} bgm_player_t;
typedef struct music_handler_s
{
unsigned int type; /* 1U << n (see 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_VORBIS,BGM_STREAMER,-1, "ogg", MUSIC_DIRNAME, NULL },
{ CODECTYPE_OPUS, BGM_STREAMER, -1, "opus", MUSIC_DIRNAME, NULL },
{ CODECTYPE_MP3, BGM_STREAMER, -1, "mp3", MUSIC_DIRNAME, NULL },
{ CODECTYPE_FLAC, BGM_STREAMER, -1, "flac", MUSIC_DIRNAME, NULL },
{ CODECTYPE_WAV, BGM_STREAMER, -1, "wav", MUSIC_DIRNAME, NULL },
{ CODECTYPE_MOD, BGM_STREAMER, -1, "it", MUSIC_DIRNAME, NULL },
{ CODECTYPE_MOD, BGM_STREAMER, -1, "s3m", MUSIC_DIRNAME, NULL },
{ CODECTYPE_MOD, BGM_STREAMER, -1, "xm", MUSIC_DIRNAME, NULL },
{ CODECTYPE_MOD, BGM_STREAMER, -1, "mod", MUSIC_DIRNAME, NULL },
{ CODECTYPE_UMX, BGM_STREAMER, -1, "umx", 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_VORBIS | CODECTYPE_MP3 | CODECTYPE_FLAC | 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;
}
}
void BGM_Shutdown (void) {
return;
static void BGM_Pause_f (void)
{
BGM_Pause ();
}
void BGM_Play (const char *filename) {
return;
static void BGM_Resume_f (void)
{
BGM_Resume ();
}
void BGM_Stop (void) {
return;
static void BGM_Loop_f (void)
{
if (Cmd_Argc() == 2)
{
if (strcasecmp(Cmd_Argv(1), "0") == 0 ||
strcasecmp(Cmd_Argv(1),"off") == 0)
bgmloop = false;
else if (strcasecmp(Cmd_Argv(1), "1") == 0 ||
strcasecmp(Cmd_Argv(1),"on") == 0)
bgmloop = true;
else if (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");
}
void BGM_Update (void) {
return;
static void BGM_Stop_f (void)
{
BGM_Stop();
}
void BGM_Pause (void) {
qboolean BGM_Init (void)
{
music_handler_t *handlers = NULL;
int i;
Cvar_RegisterVariable(&bgm_extmusic);
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);
if (COM_CheckParm("-noextmusic") != 0)
no_extmusic = true;
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)
{
handler = handler->next;
continue;
}
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;
BGM_Stop();
if (music_handlers == NULL)
return;
if (!filename || !*filename)
{
Con_DPrintf("null music file name\n");
return;
}
ext = COM_FileExtension(filename);
if (! *ext) /* try all things */
{
BGM_Play_noext(filename, ANY_CODECTYPE);
return;
}
handler = music_handlers;
while (handler)
{
if (handler->is_available &&
!strcasecmp(ext, handler->ext))
break;
handler = handler->next;
}
if (!handler)
{
Con_Printf("Unhandled extension for %s\n", filename);
return;
}
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_Resume (void) {
void BGM_PlayCDtrack (byte track, qboolean looping)
{
/* 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 type;
music_handler_t *handler;
BGM_Stop();
if (CDAudio_Play(track, looping) == 0)
return; /* success */
if (music_handlers == NULL)
return;
if (no_extmusic || !bgm_extmusic.value)
return;
type = 0;
ext = NULL;
handler = music_handlers;
while (handler)
{
if (! handler->is_available)
goto _next;
if (! CDRIPTYPE(handler->type))
goto _next;
snprintf(tmp, sizeof(tmp), "%s/track%02d.%s",
MUSIC_DIRNAME, (int)track, handler->ext);
if (! COM_FileExists(tmp))
goto _next;
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
{
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;
}
}
void BGM_PlayCDtrack (byte track, qboolean looping) {
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 > (int) sizeof(raw))
{
fileBytes = (int) 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)
{
res = S_CodecRewindStream(bgmstream);
if (res != 0)
{
Con_Printf("Stream seek error (%i), stopping.\n", res);
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 ();
}
/*
* 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-2012 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 <bsd/string.h>
#include "quakedef.h"
#include "sound.h"
#include "common.h"
#include "console.h"
#include "snd_codec.h"
#include "snd_codeci.h"
/* headers for individual codecs */
#include "snd_mikmod.h"
#include "snd_modplug.h"
#include "snd_umx.h"
#include "snd_wave.h"
#include "snd_flac.h"
#include "snd_mp3.h"
#include "snd_vorbis.h"
#include "snd_opus.h"
static snd_codec_t *codecs;
/*
=================