Opus file decoding support. it is not enabled by default: just edit the Makefile for USE_CODEC_OPUS. 2013.02.28 O.Sezer Index: bgmusic.c =================================================================== --- bgmusic.c (revision 823) +++ bgmusic.c (working copy) @@ -54,6 +54,7 @@ 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 }, Index: snd_codec.c =================================================================== --- snd_codec.c (revision 823) +++ snd_codec.c (working copy) @@ -31,6 +31,7 @@ #include "snd_wave.h" #include "snd_mp3.h" #include "snd_vorbis.h" +#include "snd_opus.h" static snd_codec_t *codecs; @@ -67,6 +68,9 @@ #ifdef USE_CODEC_VORBIS S_CodecRegister(&vorbis_codec); #endif +#ifdef USE_CODEC_OPUS + S_CodecRegister(&opus_codec); +#endif codec = codecs; while (codec) { Index: snd_codec.h =================================================================== --- snd_codec.h (revision 823) +++ snd_codec.h (working copy) @@ -88,6 +88,7 @@ #define CODECTYPE_WAV (1U << 3) #define CODECTYPE_MP3 (1U << 4) #define CODECTYPE_VORBIS (1U << 5) +#define CODECTYPE_OPUS (1U << 6) #define CODECTYPE_WAVE CODECTYPE_WAV #define CODECTYPE_MIDI CODECTYPE_MID Index: Makefile =================================================================== --- Makefile (revision 823) +++ Makefile (working copy) @@ -12,6 +12,7 @@ USE_CODEC_WAVE=1 USE_CODEC_MP3=1 USE_CODEC_VORBIS=1 +USE_CODEC_OPUS=0 # which library to use for mp3 decoding: mad or mpg123 MP3LIB=mad @@ -135,6 +136,14 @@ ifeq ($(USE_CODEC_WAVE),1) CFLAGS+= -DUSE_CODEC_WAVE endif +ifeq ($(USE_CODEC_OPUS),1) +# opus and opusfile put their *.h under /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) @@ -171,6 +180,7 @@ snd_codec.o \ snd_wave.o \ snd_vorbis.o \ + snd_opus.o \ $(mp3_obj) COMOBJ_SND := snd_dma.o snd_mix.o snd_mem.o $(MUSIC_OBJS) SYSOBJ_SND := snd_sdl.o Index: Makefile.darwin =================================================================== --- Makefile.darwin (revision 823) +++ Makefile.darwin (working copy) @@ -13,6 +13,7 @@ USE_CODEC_WAVE=1 USE_CODEC_MP3=1 USE_CODEC_VORBIS=1 +USE_CODEC_OPUS=0 # which library to use for mp3 decoding: mad or mpg123 MP3LIB=mad @@ -123,6 +124,12 @@ ifeq ($(USE_CODEC_WAVE),1) CFLAGS+= -DUSE_CODEC_WAVE endif +ifeq ($(USE_CODEC_OPUS),1) +CFLAGS+= -DUSE_CODEC_OPUS +CODEC_INC = -I../MacOSX/codecs/include +CODEC_LINK= -L../MacOSX/codecs/lib +CODECLIBS+= -lopusfile -lopus -logg +endif ifeq ($(USE_CODEC_VORBIS),1) CFLAGS+= -DUSE_CODEC_VORBIS $(cpp_vorbisdec) CODEC_INC = -I../MacOSX/codecs/include @@ -168,6 +175,7 @@ snd_codec.o \ snd_wave.o \ snd_vorbis.o \ + snd_opus.o \ $(mp3_obj) COMOBJ_SND := snd_dma.o snd_mix.o snd_mem.o $(MUSIC_OBJS) SYSOBJ_SND := snd_sdl.o Index: Makefile.w32 =================================================================== --- Makefile.w32 (revision 823) +++ Makefile.w32 (working copy) @@ -13,6 +13,7 @@ USE_CODEC_WAVE=1 USE_CODEC_MP3=1 USE_CODEC_VORBIS=1 +USE_CODEC_OPUS=0 # which library to use for mp3 decoding: mad or mpg123 MP3LIB=mad @@ -131,6 +132,12 @@ CODEC_INC = -I../Windows/codecs/include CODEC_LINK= -L../Windows/codecs/x86 endif +ifeq ($(USE_CODEC_OPUS),1) +CFLAGS+= -DUSE_CODEC_OPUS +CODEC_INC = -I../Windows/codecs/include +CODEC_LINK= -L../Windows/codecs/x86 +CODECLIBS+= -lopusfile -lopus -logg +endif ifeq ($(USE_CODEC_VORBIS),1) CFLAGS+= -DUSE_CODEC_VORBIS $(cpp_vorbisdec) CODEC_INC = -I../Windows/codecs/include @@ -174,6 +181,7 @@ snd_codec.o \ snd_wave.o \ snd_vorbis.o \ + snd_opus.o \ $(mp3_obj) COMOBJ_SND := snd_dma.o snd_mix.o snd_mem.o $(MUSIC_OBJS) SYSOBJ_SND := snd_sdl.o Index: Makefile.w64 =================================================================== --- Makefile.w64 (revision 823) +++ Makefile.w64 (working copy) @@ -13,6 +13,7 @@ USE_CODEC_WAVE=1 USE_CODEC_MP3=1 USE_CODEC_VORBIS=1 +USE_CODEC_OPUS=0 # which library to use for mp3 decoding: mad or mpg123 MP3LIB=mad @@ -131,6 +132,12 @@ CODEC_INC = -I../Windows/codecs/include CODEC_LINK= -L../Windows/codecs/x64 endif +ifeq ($(USE_CODEC_OPUS),1) +CFLAGS+= -DUSE_CODEC_OPUS +CODEC_INC = -I../Windows/codecs/include +CODEC_LINK= -L../Windows/codecs/x64 +CODECLIBS+= -lopusfile -lopus -logg +endif ifeq ($(USE_CODEC_VORBIS),1) CFLAGS+= -DUSE_CODEC_VORBIS $(cpp_vorbisdec) CODEC_INC = -I../Windows/codecs/include @@ -174,6 +181,7 @@ snd_codec.o \ snd_wave.o \ snd_vorbis.o \ + snd_opus.o \ $(mp3_obj) COMOBJ_SND := snd_dma.o snd_mix.o snd_mem.o $(MUSIC_OBJS) SYSOBJ_SND := snd_sdl.o Index: snd_opus.c =================================================================== --- snd_opus.c (revision 0) +++ snd_opus.c (revision 0) @@ -0,0 +1,215 @@ +/* + * Ogg/Opus streaming music support, loosely based on several open source + * Quake engine based projects with many modifications. + * + * Copyright (C) 2012-2013 O.Sezer + * + * 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_OPUS) +#include "snd_codec.h" +#include "snd_codeci.h" +#include "snd_opus.h" + +#include +#include + + +/* CALLBACK FUNCTIONS: */ + +static int opc_fclose (void *f) +{ + return 0; /* we fclose() elsewhere. */ +} + +static int opc_fread (void *f, unsigned char *buf, int size) +{ + int ret; + + if (size < 0) + { + errno = EINVAL; + return -1; + } + + ret = (int) FS_fread(buf, 1, (size_t)size, (fshandle_t *)f); + if (ret == 0 && errno != 0) + ret = -1; + return ret; +} + +static int opc_fseek (void *f, opus_int64 off, int whence) +{ + if (f == NULL) return (-1); + return FS_fseek((fshandle_t *)f, (long) off, whence); +} + +static opus_int64 opc_ftell (void *f) +{ + return (opus_int64) FS_ftell((fshandle_t *)f); +} + +static const OpusFileCallbacks opc_qfs = +{ + (int (*)(void *, unsigned char *, int)) opc_fread, + (int (*)(void *, opus_int64, int)) opc_fseek, + (opus_int64 (*)(void *)) opc_ftell, + (int (*)(void *)) opc_fclose +}; + +static qboolean S_OPUS_CodecInitialize (void) +{ + return true; +} + +static void S_OPUS_CodecShutdown (void) +{ +} + +static snd_stream_t *S_OPUS_CodecOpenStream (const char *filename) +{ + snd_stream_t *stream; + OggOpusFile *opFile; + const OpusHead *op_info; + long numstreams; + int res; + + stream = S_CodecUtilOpen(filename, &opus_codec); + if (!stream) + return NULL; + + opFile = op_open_callbacks(&stream->fh, &opc_qfs, NULL, 0, &res); + if (!opFile) + { + Con_Printf("%s is not a valid Opus file (error %i).\n", + filename, res); + goto _fail; + } + + stream->priv = opFile; + + if (!op_seekable(opFile)) + { + Con_Printf("Opus stream %s not seekable.\n", filename); + goto _fail; + } + + op_info = op_head(opFile, -1); + if (!op_info) + { + Con_Printf("Unable to get stream information for %s.\n", filename); + goto _fail; + } + + /* FIXME: handle section changes */ + numstreams = op_info->stream_count; + if (numstreams != 1) + { + Con_Printf("More than one (%ld) stream in %s\n", + (long)op_info->stream_count, filename); + goto _fail; + } + + if (op_info->channel_count != 1 && op_info->channel_count != 2) + { + Con_Printf("Unsupported number of channels %d in %s\n", + op_info->channel_count, filename); + goto _fail; + } + + /* All Opus audio is coded at 48 kHz, and should also be decoded + * at 48 kHz for playback: info->input_sample_rate only tells us + * the sampling rate of the original input before opus encoding. + * S_RawSamples() shall already downsample this, as necessary. */ + stream->info.rate = 48000; + stream->info.channels = op_info->channel_count; + /* op_read() yields 16-bit output using native endian ordering: */ + stream->info.width = 2; + + return stream; +_fail: + if (opFile) + op_free(opFile); + S_CodecUtilClose(&stream); + return NULL; +} + +static int S_OPUS_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer) +{ + int section; /* FIXME: handle section changes */ + int cnt, res, rem; + opus_int16 * ptr; + + rem = bytes / stream->info.width; + if (rem / stream->info.channels <= 0) + return 0; + + cnt = 0; + ptr = (opus_int16 *) buffer; + while (1) + { + /* op_read() yields 16-bit output using native endian ordering. returns + * the number of samples read per channel on success, or a negative value + * on failure. */ + res = op_read((OggOpusFile *)stream->priv, ptr, rem, §ion); + if (res <= 0) + break; + cnt += res; + res *= stream->info.channels; + rem -= res; + if (rem <= 0) + break; + ptr += res; + } + + if (res < 0) + return res; + + cnt *= (stream->info.channels * stream->info.width); + return cnt; +} + +static void S_OPUS_CodecCloseStream (snd_stream_t *stream) +{ + op_free((OggOpusFile *)stream->priv); + S_CodecUtilClose(&stream); +} + +static int S_OPUS_CodecRewindStream (snd_stream_t *stream) +{ + return op_pcm_seek ((OggOpusFile *)stream->priv, 0); +} + +snd_codec_t opus_codec = +{ + CODECTYPE_OPUS, + true, /* always available. */ + "opus", + S_OPUS_CodecInitialize, + S_OPUS_CodecShutdown, + S_OPUS_CodecOpenStream, + S_OPUS_CodecReadStream, + S_OPUS_CodecRewindStream, + S_OPUS_CodecCloseStream, + NULL +}; + +#endif /* USE_CODEC_OPUS */ + Index: snd_opus.h =================================================================== --- snd_opus.h (revision 0) +++ snd_opus.h (revision 0) @@ -0,0 +1,13 @@ +/* Ogg/Opus streaming music support. */ + +#if !defined(_SND_OPUS_H_) +#define _SND_OPUS_H_ 1 + +#if defined(USE_CODEC_OPUS) + +extern snd_codec_t opus_codec; + +#endif /* USE_CODEC_OPUS */ + +#endif /* ! _SND_OPUS_H_ */ +