mirror of
https://github.com/DrBeef/ioq3quest.git
synced 2024-11-14 00:21:16 +00:00
Add Ogg Opus support
This commit is contained in:
parent
1b0fb6e8d8
commit
3dd5c5d862
4 changed files with 649 additions and 0 deletions
184
Makefile
184
Makefile
|
@ -165,6 +165,10 @@ ifndef USE_CODEC_VORBIS
|
||||||
USE_CODEC_VORBIS=0
|
USE_CODEC_VORBIS=0
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifndef USE_CODEC_OPUS
|
||||||
|
USE_CODEC_OPUS=1
|
||||||
|
endif
|
||||||
|
|
||||||
ifndef USE_MUMBLE
|
ifndef USE_MUMBLE
|
||||||
USE_MUMBLE=1
|
USE_MUMBLE=1
|
||||||
endif
|
endif
|
||||||
|
@ -181,6 +185,10 @@ ifndef USE_INTERNAL_SPEEX
|
||||||
USE_INTERNAL_SPEEX=1
|
USE_INTERNAL_SPEEX=1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifndef USE_INTERNAL_OPUS
|
||||||
|
USE_INTERNAL_OPUS=1
|
||||||
|
endif
|
||||||
|
|
||||||
ifndef USE_INTERNAL_ZLIB
|
ifndef USE_INTERNAL_ZLIB
|
||||||
USE_INTERNAL_ZLIB=1
|
USE_INTERNAL_ZLIB=1
|
||||||
endif
|
endif
|
||||||
|
@ -226,6 +234,8 @@ UIDIR=$(MOUNT_DIR)/ui
|
||||||
Q3UIDIR=$(MOUNT_DIR)/q3_ui
|
Q3UIDIR=$(MOUNT_DIR)/q3_ui
|
||||||
JPDIR=$(MOUNT_DIR)/jpeg-8c
|
JPDIR=$(MOUNT_DIR)/jpeg-8c
|
||||||
SPEEXDIR=$(MOUNT_DIR)/libspeex
|
SPEEXDIR=$(MOUNT_DIR)/libspeex
|
||||||
|
OPUSDIR=$(MOUNT_DIR)/opus-1.0.2
|
||||||
|
OPUSFILEDIR=$(MOUNT_DIR)/opusfile-0.2
|
||||||
ZDIR=$(MOUNT_DIR)/zlib
|
ZDIR=$(MOUNT_DIR)/zlib
|
||||||
Q3ASMDIR=$(MOUNT_DIR)/tools/asm
|
Q3ASMDIR=$(MOUNT_DIR)/tools/asm
|
||||||
LBURGDIR=$(MOUNT_DIR)/tools/lcc/lburg
|
LBURGDIR=$(MOUNT_DIR)/tools/lcc/lburg
|
||||||
|
@ -914,6 +924,19 @@ ifeq ($(USE_CODEC_VORBIS),1)
|
||||||
CLIENT_CFLAGS += -DUSE_CODEC_VORBIS
|
CLIENT_CFLAGS += -DUSE_CODEC_VORBIS
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(USE_CODEC_OPUS),1)
|
||||||
|
CLIENT_CFLAGS += -DUSE_CODEC_OPUS
|
||||||
|
ifeq ($(USE_INTERNAL_OPUS),1)
|
||||||
|
CLIENT_CFLAGS += -DOPUS_BUILD -DFLOATING_POINT -DUSE_ALLOCA \
|
||||||
|
-I$(OPUSDIR)/include -I$(OPUSDIR)/celt -I$(OPUSDIR)/silk \
|
||||||
|
-I$(OPUSDIR)/silk/float
|
||||||
|
|
||||||
|
CLIENT_CFLAGS += -I$(OPUSFILEDIR)/include
|
||||||
|
else
|
||||||
|
CLIENT_LIBS += -lopusfile -lopus -logg
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(USE_RENDERER_DLOPEN),1)
|
ifeq ($(USE_RENDERER_DLOPEN),1)
|
||||||
CLIENT_CFLAGS += -DUSE_RENDERER_DLOPEN
|
CLIENT_CFLAGS += -DUSE_RENDERER_DLOPEN
|
||||||
endif
|
endif
|
||||||
|
@ -1166,6 +1189,7 @@ makedirs:
|
||||||
@if [ ! -d $(BUILD_DIR) ];then $(MKDIR) $(BUILD_DIR);fi
|
@if [ ! -d $(BUILD_DIR) ];then $(MKDIR) $(BUILD_DIR);fi
|
||||||
@if [ ! -d $(B) ];then $(MKDIR) $(B);fi
|
@if [ ! -d $(B) ];then $(MKDIR) $(B);fi
|
||||||
@if [ ! -d $(B)/client ];then $(MKDIR) $(B)/client;fi
|
@if [ ! -d $(B)/client ];then $(MKDIR) $(B)/client;fi
|
||||||
|
@if [ ! -d $(B)/client/opus ];then $(MKDIR) $(B)/client/opus;fi
|
||||||
@if [ ! -d $(B)/renderergl1 ];then $(MKDIR) $(B)/renderergl1;fi
|
@if [ ! -d $(B)/renderergl1 ];then $(MKDIR) $(B)/renderergl1;fi
|
||||||
@if [ ! -d $(B)/renderergl2 ];then $(MKDIR) $(B)/renderergl2;fi
|
@if [ ! -d $(B)/renderergl2 ];then $(MKDIR) $(B)/renderergl2;fi
|
||||||
@if [ ! -d $(B)/renderergl2/glsl ];then $(MKDIR) $(B)/renderergl2/glsl;fi
|
@if [ ! -d $(B)/renderergl2/glsl ];then $(MKDIR) $(B)/renderergl2/glsl;fi
|
||||||
|
@ -1405,6 +1429,7 @@ Q3OBJ = \
|
||||||
$(B)/client/snd_codec.o \
|
$(B)/client/snd_codec.o \
|
||||||
$(B)/client/snd_codec_wav.o \
|
$(B)/client/snd_codec_wav.o \
|
||||||
$(B)/client/snd_codec_ogg.o \
|
$(B)/client/snd_codec_ogg.o \
|
||||||
|
$(B)/client/snd_codec_opus.o \
|
||||||
\
|
\
|
||||||
$(B)/client/qal.o \
|
$(B)/client/qal.o \
|
||||||
$(B)/client/snd_openal.o \
|
$(B)/client/snd_openal.o \
|
||||||
|
@ -1717,6 +1742,149 @@ Q3OBJ += \
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(USE_CODEC_OPUS),1)
|
||||||
|
ifeq ($(USE_INTERNAL_OPUS),1)
|
||||||
|
Q3OBJ += \
|
||||||
|
$(B)/client/opus/opus.o \
|
||||||
|
$(B)/client/opus/opus_decoder.o \
|
||||||
|
$(B)/client/opus/opus_encoder.o \
|
||||||
|
$(B)/client/opus/opus_multistream.o \
|
||||||
|
$(B)/client/opus/repacketizer.o \
|
||||||
|
\
|
||||||
|
$(B)/client/opus/bands.o \
|
||||||
|
$(B)/client/opus/celt.o \
|
||||||
|
$(B)/client/opus/cwrs.o \
|
||||||
|
$(B)/client/opus/entcode.o \
|
||||||
|
$(B)/client/opus/entdec.o \
|
||||||
|
$(B)/client/opus/entenc.o \
|
||||||
|
$(B)/client/opus/kiss_fft.o \
|
||||||
|
$(B)/client/opus/laplace.o \
|
||||||
|
$(B)/client/opus/mathops.o \
|
||||||
|
$(B)/client/opus/mdct.o \
|
||||||
|
$(B)/client/opus/modes.o \
|
||||||
|
$(B)/client/opus/pitch.o \
|
||||||
|
$(B)/client/opus/celt_lpc.o \
|
||||||
|
$(B)/client/opus/quant_bands.o \
|
||||||
|
$(B)/client/opus/rate.o \
|
||||||
|
$(B)/client/opus/vq.o \
|
||||||
|
\
|
||||||
|
$(B)/client/opus/CNG.o \
|
||||||
|
$(B)/client/opus/code_signs.o \
|
||||||
|
$(B)/client/opus/init_decoder.o \
|
||||||
|
$(B)/client/opus/decode_core.o \
|
||||||
|
$(B)/client/opus/decode_frame.o \
|
||||||
|
$(B)/client/opus/decode_parameters.o \
|
||||||
|
$(B)/client/opus/decode_indices.o \
|
||||||
|
$(B)/client/opus/decode_pulses.o \
|
||||||
|
$(B)/client/opus/decoder_set_fs.o \
|
||||||
|
$(B)/client/opus/dec_API.o \
|
||||||
|
$(B)/client/opus/enc_API.o \
|
||||||
|
$(B)/client/opus/encode_indices.o \
|
||||||
|
$(B)/client/opus/encode_pulses.o \
|
||||||
|
$(B)/client/opus/gain_quant.o \
|
||||||
|
$(B)/client/opus/interpolate.o \
|
||||||
|
$(B)/client/opus/LP_variable_cutoff.o \
|
||||||
|
$(B)/client/opus/NLSF_decode.o \
|
||||||
|
$(B)/client/opus/NSQ.o \
|
||||||
|
$(B)/client/opus/NSQ_del_dec.o \
|
||||||
|
$(B)/client/opus/PLC.o \
|
||||||
|
$(B)/client/opus/shell_coder.o \
|
||||||
|
$(B)/client/opus/tables_gain.o \
|
||||||
|
$(B)/client/opus/tables_LTP.o \
|
||||||
|
$(B)/client/opus/tables_NLSF_CB_NB_MB.o \
|
||||||
|
$(B)/client/opus/tables_NLSF_CB_WB.o \
|
||||||
|
$(B)/client/opus/tables_other.o \
|
||||||
|
$(B)/client/opus/tables_pitch_lag.o \
|
||||||
|
$(B)/client/opus/tables_pulses_per_block.o \
|
||||||
|
$(B)/client/opus/VAD.o \
|
||||||
|
$(B)/client/opus/control_audio_bandwidth.o \
|
||||||
|
$(B)/client/opus/quant_LTP_gains.o \
|
||||||
|
$(B)/client/opus/VQ_WMat_EC.o \
|
||||||
|
$(B)/client/opus/HP_variable_cutoff.o \
|
||||||
|
$(B)/client/opus/NLSF_encode.o \
|
||||||
|
$(B)/client/opus/NLSF_VQ.o \
|
||||||
|
$(B)/client/opus/NLSF_unpack.o \
|
||||||
|
$(B)/client/opus/NLSF_del_dec_quant.o \
|
||||||
|
$(B)/client/opus/process_NLSFs.o \
|
||||||
|
$(B)/client/opus/stereo_LR_to_MS.o \
|
||||||
|
$(B)/client/opus/stereo_MS_to_LR.o \
|
||||||
|
$(B)/client/opus/check_control_input.o \
|
||||||
|
$(B)/client/opus/control_SNR.o \
|
||||||
|
$(B)/client/opus/init_encoder.o \
|
||||||
|
$(B)/client/opus/control_codec.o \
|
||||||
|
$(B)/client/opus/A2NLSF.o \
|
||||||
|
$(B)/client/opus/ana_filt_bank_1.o \
|
||||||
|
$(B)/client/opus/biquad_alt.o \
|
||||||
|
$(B)/client/opus/bwexpander_32.o \
|
||||||
|
$(B)/client/opus/bwexpander.o \
|
||||||
|
$(B)/client/opus/debug.o \
|
||||||
|
$(B)/client/opus/decode_pitch.o \
|
||||||
|
$(B)/client/opus/inner_prod_aligned.o \
|
||||||
|
$(B)/client/opus/lin2log.o \
|
||||||
|
$(B)/client/opus/log2lin.o \
|
||||||
|
$(B)/client/opus/LPC_analysis_filter.o \
|
||||||
|
$(B)/client/opus/LPC_inv_pred_gain.o \
|
||||||
|
$(B)/client/opus/table_LSF_cos.o \
|
||||||
|
$(B)/client/opus/NLSF2A.o \
|
||||||
|
$(B)/client/opus/NLSF_stabilize.o \
|
||||||
|
$(B)/client/opus/NLSF_VQ_weights_laroia.o \
|
||||||
|
$(B)/client/opus/pitch_est_tables.o \
|
||||||
|
$(B)/client/opus/resampler.o \
|
||||||
|
$(B)/client/opus/resampler_down2_3.o \
|
||||||
|
$(B)/client/opus/resampler_down2.o \
|
||||||
|
$(B)/client/opus/resampler_private_AR2.o \
|
||||||
|
$(B)/client/opus/resampler_private_down_FIR.o \
|
||||||
|
$(B)/client/opus/resampler_private_IIR_FIR.o \
|
||||||
|
$(B)/client/opus/resampler_private_up2_HQ.o \
|
||||||
|
$(B)/client/opus/resampler_rom.o \
|
||||||
|
$(B)/client/opus/sigm_Q15.o \
|
||||||
|
$(B)/client/opus/sort.o \
|
||||||
|
$(B)/client/opus/sum_sqr_shift.o \
|
||||||
|
$(B)/client/opus/stereo_decode_pred.o \
|
||||||
|
$(B)/client/opus/stereo_encode_pred.o \
|
||||||
|
$(B)/client/opus/stereo_find_predictor.o \
|
||||||
|
$(B)/client/opus/stereo_quant_pred.o \
|
||||||
|
\
|
||||||
|
$(B)/client/opus/apply_sine_window_FLP.o \
|
||||||
|
$(B)/client/opus/corrMatrix_FLP.o \
|
||||||
|
$(B)/client/opus/encode_frame_FLP.o \
|
||||||
|
$(B)/client/opus/find_LPC_FLP.o \
|
||||||
|
$(B)/client/opus/find_LTP_FLP.o \
|
||||||
|
$(B)/client/opus/find_pitch_lags_FLP.o \
|
||||||
|
$(B)/client/opus/find_pred_coefs_FLP.o \
|
||||||
|
$(B)/client/opus/LPC_analysis_filter_FLP.o \
|
||||||
|
$(B)/client/opus/LTP_analysis_filter_FLP.o \
|
||||||
|
$(B)/client/opus/LTP_scale_ctrl_FLP.o \
|
||||||
|
$(B)/client/opus/noise_shape_analysis_FLP.o \
|
||||||
|
$(B)/client/opus/prefilter_FLP.o \
|
||||||
|
$(B)/client/opus/process_gains_FLP.o \
|
||||||
|
$(B)/client/opus/regularize_correlations_FLP.o \
|
||||||
|
$(B)/client/opus/residual_energy_FLP.o \
|
||||||
|
$(B)/client/opus/solve_LS_FLP.o \
|
||||||
|
$(B)/client/opus/warped_autocorrelation_FLP.o \
|
||||||
|
$(B)/client/opus/wrappers_FLP.o \
|
||||||
|
$(B)/client/opus/autocorrelation_FLP.o \
|
||||||
|
$(B)/client/opus/burg_modified_FLP.o \
|
||||||
|
$(B)/client/opus/bwexpander_FLP.o \
|
||||||
|
$(B)/client/opus/energy_FLP.o \
|
||||||
|
$(B)/client/opus/inner_product_FLP.o \
|
||||||
|
$(B)/client/opus/k2a_FLP.o \
|
||||||
|
$(B)/client/opus/levinsondurbin_FLP.o \
|
||||||
|
$(B)/client/opus/LPC_inv_pred_gain_FLP.o \
|
||||||
|
$(B)/client/opus/pitch_analysis_core_FLP.o \
|
||||||
|
$(B)/client/opus/scale_copy_vector_FLP.o \
|
||||||
|
$(B)/client/opus/scale_vector_FLP.o \
|
||||||
|
$(B)/client/opus/schur_FLP.o \
|
||||||
|
$(B)/client/opus/sort_FLP.o \
|
||||||
|
\
|
||||||
|
$(B)/client/http.o \
|
||||||
|
$(B)/client/info.o \
|
||||||
|
$(B)/client/internal.o \
|
||||||
|
$(B)/client/opusfile.o \
|
||||||
|
$(B)/client/stream.o
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(USE_INTERNAL_ZLIB),1)
|
ifeq ($(USE_INTERNAL_ZLIB),1)
|
||||||
Q3OBJ += \
|
Q3OBJ += \
|
||||||
$(B)/client/adler32.o \
|
$(B)/client/adler32.o \
|
||||||
|
@ -2331,6 +2499,21 @@ $(B)/client/%.o: $(BLIBDIR)/%.c
|
||||||
$(B)/client/%.o: $(SPEEXDIR)/%.c
|
$(B)/client/%.o: $(SPEEXDIR)/%.c
|
||||||
$(DO_CC)
|
$(DO_CC)
|
||||||
|
|
||||||
|
$(B)/client/opus/%.o: $(OPUSDIR)/src/%.c
|
||||||
|
$(DO_CC)
|
||||||
|
|
||||||
|
$(B)/client/opus/%.o: $(OPUSDIR)/celt/%.c
|
||||||
|
$(DO_CC)
|
||||||
|
|
||||||
|
$(B)/client/opus/%.o: $(OPUSDIR)/silk/%.c
|
||||||
|
$(DO_CC)
|
||||||
|
|
||||||
|
$(B)/client/opus/%.o: $(OPUSDIR)/silk/float/%.c
|
||||||
|
$(DO_CC)
|
||||||
|
|
||||||
|
$(B)/client/%.o: $(OPUSFILEDIR)/src/%.c
|
||||||
|
$(DO_CC)
|
||||||
|
|
||||||
$(B)/client/%.o: $(ZDIR)/%.c
|
$(B)/client/%.o: $(ZDIR)/%.c
|
||||||
$(DO_CC)
|
$(DO_CC)
|
||||||
|
|
||||||
|
@ -2595,6 +2778,7 @@ ifeq ($(PLATFORM),mingw32)
|
||||||
USE_RENDERER_DLOPEN=$(USE_RENDERER_DLOPEN) \
|
USE_RENDERER_DLOPEN=$(USE_RENDERER_DLOPEN) \
|
||||||
USE_OPENAL_DLOPEN=$(USE_OPENAL_DLOPEN) \
|
USE_OPENAL_DLOPEN=$(USE_OPENAL_DLOPEN) \
|
||||||
USE_CURL_DLOPEN=$(USE_CURL_DLOPEN) \
|
USE_CURL_DLOPEN=$(USE_CURL_DLOPEN) \
|
||||||
|
USE_INTERNAL_OPUS=$(USE_INTERNAL_OPUS) \
|
||||||
USE_INTERNAL_SPEEX=$(USE_INTERNAL_SPEEX) \
|
USE_INTERNAL_SPEEX=$(USE_INTERNAL_SPEEX) \
|
||||||
USE_INTERNAL_ZLIB=$(USE_INTERNAL_ZLIB) \
|
USE_INTERNAL_ZLIB=$(USE_INTERNAL_ZLIB) \
|
||||||
USE_INTERNAL_JPEG=$(USE_INTERNAL_JPEG)
|
USE_INTERNAL_JPEG=$(USE_INTERNAL_JPEG)
|
||||||
|
|
|
@ -124,6 +124,10 @@ void S_CodecInit()
|
||||||
{
|
{
|
||||||
codecs = NULL;
|
codecs = NULL;
|
||||||
|
|
||||||
|
#ifdef USE_CODEC_OPUS
|
||||||
|
S_CodecRegister(&opus_codec);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_CODEC_VORBIS
|
#ifdef USE_CODEC_VORBIS
|
||||||
S_CodecRegister(&ogg_codec);
|
S_CodecRegister(&ogg_codec);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -95,4 +95,13 @@ void S_OGG_CodecCloseStream(snd_stream_t *stream);
|
||||||
int S_OGG_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer);
|
int S_OGG_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer);
|
||||||
#endif // USE_CODEC_VORBIS
|
#endif // USE_CODEC_VORBIS
|
||||||
|
|
||||||
|
// Ogg Opus codec
|
||||||
|
#ifdef USE_CODEC_OPUS
|
||||||
|
extern snd_codec_t opus_codec;
|
||||||
|
void *S_OggOpus_CodecLoad(const char *filename, snd_info_t *info);
|
||||||
|
snd_stream_t *S_OggOpus_CodecOpenStream(const char *filename);
|
||||||
|
void S_OggOpus_CodecCloseStream(snd_stream_t *stream);
|
||||||
|
int S_OggOpus_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer);
|
||||||
|
#endif // USE_CODEC_OPUS
|
||||||
|
|
||||||
#endif // !_SND_CODEC_H_
|
#endif // !_SND_CODEC_H_
|
||||||
|
|
452
code/client/snd_codec_opus.c
Normal file
452
code/client/snd_codec_opus.c
Normal file
|
@ -0,0 +1,452 @@
|
||||||
|
/*
|
||||||
|
===========================================================================
|
||||||
|
Copyright (C) 1999-2005 Id Software, Inc.
|
||||||
|
Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
|
||||||
|
Copyright (C) 2005-2006 Joerg Dietrich <dietrich_joerg@gmx.de>
|
||||||
|
|
||||||
|
This file is part of Quake III Arena source code.
|
||||||
|
|
||||||
|
Quake III Arena source code 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.
|
||||||
|
|
||||||
|
Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
===========================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Ogg Opus support is enabled by this define
|
||||||
|
#ifdef USE_CODEC_OPUS
|
||||||
|
|
||||||
|
// includes for the Q3 sound system
|
||||||
|
#include "client.h"
|
||||||
|
#include "snd_codec.h"
|
||||||
|
|
||||||
|
// includes for the Ogg Opus codec
|
||||||
|
#include <errno.h>
|
||||||
|
#include <opusfile.h>
|
||||||
|
|
||||||
|
// samples are 16 bit
|
||||||
|
#define OPUS_SAMPLEWIDTH 2
|
||||||
|
|
||||||
|
// Q3 Ogg Opus codec
|
||||||
|
snd_codec_t opus_codec =
|
||||||
|
{
|
||||||
|
"opus",
|
||||||
|
S_OggOpus_CodecLoad,
|
||||||
|
S_OggOpus_CodecOpenStream,
|
||||||
|
S_OggOpus_CodecReadStream,
|
||||||
|
S_OggOpus_CodecCloseStream,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
// callbacks for opusfile
|
||||||
|
|
||||||
|
// fread() replacement
|
||||||
|
int S_OggOpus_Callback_read(void *datasource, unsigned char *ptr, int size )
|
||||||
|
{
|
||||||
|
snd_stream_t *stream;
|
||||||
|
int bytesRead = 0;
|
||||||
|
|
||||||
|
// check if input is valid
|
||||||
|
if(!ptr)
|
||||||
|
{
|
||||||
|
errno = EFAULT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!size)
|
||||||
|
{
|
||||||
|
// It's not an error, caller just wants zero bytes!
|
||||||
|
errno = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size < 0)
|
||||||
|
{
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!datasource)
|
||||||
|
{
|
||||||
|
errno = EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we use a snd_stream_t in the generic pointer to pass around
|
||||||
|
stream = (snd_stream_t *) datasource;
|
||||||
|
|
||||||
|
// read it with the Q3 function FS_Read()
|
||||||
|
bytesRead = FS_Read(ptr, size, stream->file);
|
||||||
|
|
||||||
|
// update the file position
|
||||||
|
stream->pos += bytesRead;
|
||||||
|
|
||||||
|
return bytesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fseek() replacement
|
||||||
|
int S_OggOpus_Callback_seek(void *datasource, opus_int64 offset, int whence)
|
||||||
|
{
|
||||||
|
snd_stream_t *stream;
|
||||||
|
int retVal = 0;
|
||||||
|
|
||||||
|
// check if input is valid
|
||||||
|
if(!datasource)
|
||||||
|
{
|
||||||
|
errno = EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// snd_stream_t in the generic pointer
|
||||||
|
stream = (snd_stream_t *) datasource;
|
||||||
|
|
||||||
|
// we must map the whence to its Q3 counterpart
|
||||||
|
switch(whence)
|
||||||
|
{
|
||||||
|
case SEEK_SET :
|
||||||
|
{
|
||||||
|
// set the file position in the actual file with the Q3 function
|
||||||
|
retVal = FS_Seek(stream->file, (long) offset, FS_SEEK_SET);
|
||||||
|
|
||||||
|
// something has gone wrong, so we return here
|
||||||
|
if(retVal < 0)
|
||||||
|
{
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep track of file position
|
||||||
|
stream->pos = (int) offset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SEEK_CUR :
|
||||||
|
{
|
||||||
|
// set the file position in the actual file with the Q3 function
|
||||||
|
retVal = FS_Seek(stream->file, (long) offset, FS_SEEK_CUR);
|
||||||
|
|
||||||
|
// something has gone wrong, so we return here
|
||||||
|
if(retVal < 0)
|
||||||
|
{
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep track of file position
|
||||||
|
stream->pos += (int) offset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SEEK_END :
|
||||||
|
{
|
||||||
|
// Quake 3 seems to have trouble with FS_SEEK_END
|
||||||
|
// so we use the file length and FS_SEEK_SET
|
||||||
|
|
||||||
|
// set the file position in the actual file with the Q3 function
|
||||||
|
retVal = FS_Seek(stream->file, (long) stream->length + (long) offset, FS_SEEK_SET);
|
||||||
|
|
||||||
|
// something has gone wrong, so we return here
|
||||||
|
if(retVal < 0)
|
||||||
|
{
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep track of file position
|
||||||
|
stream->pos = stream->length + (int) offset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default :
|
||||||
|
{
|
||||||
|
// unknown whence, so we return an error
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// stream->pos shouldn't be smaller than zero or bigger than the filesize
|
||||||
|
stream->pos = (stream->pos < 0) ? 0 : stream->pos;
|
||||||
|
stream->pos = (stream->pos > stream->length) ? stream->length : stream->pos;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fclose() replacement
|
||||||
|
int S_OggOpus_Callback_close(void *datasource)
|
||||||
|
{
|
||||||
|
// we do nothing here and close all things manually in S_OggOpus_CodecCloseStream()
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ftell() replacement
|
||||||
|
opus_int64 S_OggOpus_Callback_tell(void *datasource)
|
||||||
|
{
|
||||||
|
snd_stream_t *stream;
|
||||||
|
|
||||||
|
// check if input is valid
|
||||||
|
if(!datasource)
|
||||||
|
{
|
||||||
|
errno = EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// snd_stream_t in the generic pointer
|
||||||
|
stream = (snd_stream_t *) datasource;
|
||||||
|
|
||||||
|
return (opus_int64) FS_FTell(stream->file);
|
||||||
|
}
|
||||||
|
|
||||||
|
// the callback structure
|
||||||
|
const OpusFileCallbacks S_OggOpus_Callbacks =
|
||||||
|
{
|
||||||
|
&S_OggOpus_Callback_read,
|
||||||
|
&S_OggOpus_Callback_seek,
|
||||||
|
&S_OggOpus_Callback_tell,
|
||||||
|
&S_OggOpus_Callback_close
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
=================
|
||||||
|
S_OggOpus_CodecOpenStream
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
snd_stream_t *S_OggOpus_CodecOpenStream(const char *filename)
|
||||||
|
{
|
||||||
|
snd_stream_t *stream;
|
||||||
|
|
||||||
|
// Opus codec control structure
|
||||||
|
OggOpusFile *of;
|
||||||
|
|
||||||
|
// some variables used to get informations about the file
|
||||||
|
const OpusHead *opusInfo;
|
||||||
|
ogg_int64_t numSamples;
|
||||||
|
|
||||||
|
// check if input is valid
|
||||||
|
if(!filename)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open the stream
|
||||||
|
stream = S_CodecUtilOpen(filename, &opus_codec);
|
||||||
|
if(!stream)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// open the codec with our callbacks and stream as the generic pointer
|
||||||
|
of = op_open_callbacks(stream, &S_OggOpus_Callbacks, NULL, 0, NULL );
|
||||||
|
if (!of)
|
||||||
|
{
|
||||||
|
S_CodecUtilClose(&stream);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the stream must be seekable
|
||||||
|
if(!op_seekable(of))
|
||||||
|
{
|
||||||
|
op_free(of);
|
||||||
|
|
||||||
|
S_CodecUtilClose(&stream);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the info about channels and rate
|
||||||
|
opusInfo = op_head(of, -1);
|
||||||
|
if(!opusInfo)
|
||||||
|
{
|
||||||
|
op_free(of);
|
||||||
|
|
||||||
|
S_CodecUtilClose(&stream);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(opusInfo->stream_count != 1)
|
||||||
|
{
|
||||||
|
op_free(of);
|
||||||
|
|
||||||
|
S_CodecUtilClose(&stream);
|
||||||
|
|
||||||
|
Com_Printf("Only Ogg Opus files with one stream are support\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(opusInfo->channel_count != 1 && opusInfo->channel_count != 2)
|
||||||
|
{
|
||||||
|
op_free(of);
|
||||||
|
|
||||||
|
S_CodecUtilClose(&stream);
|
||||||
|
|
||||||
|
Com_Printf("Only mono and stereo Ogg Opus files are supported\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the number of sample-frames in the file
|
||||||
|
numSamples = op_pcm_total(of, -1);
|
||||||
|
|
||||||
|
// fill in the info-structure in the stream
|
||||||
|
stream->info.rate = 48000;
|
||||||
|
stream->info.width = OPUS_SAMPLEWIDTH;
|
||||||
|
stream->info.channels = opusInfo->channel_count;
|
||||||
|
stream->info.samples = numSamples;
|
||||||
|
stream->info.size = stream->info.samples * stream->info.channels * stream->info.width;
|
||||||
|
stream->info.dataofs = 0;
|
||||||
|
|
||||||
|
// We use stream->pos for the file pointer in the compressed ogg file
|
||||||
|
stream->pos = 0;
|
||||||
|
|
||||||
|
// We use the generic pointer in stream for the opus codec control structure
|
||||||
|
stream->ptr = of;
|
||||||
|
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=================
|
||||||
|
S_OggOpus_CodecCloseStream
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
void S_OggOpus_CodecCloseStream(snd_stream_t *stream)
|
||||||
|
{
|
||||||
|
// check if input is valid
|
||||||
|
if(!stream)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// let the opus codec cleanup its stuff
|
||||||
|
op_free((OggOpusFile *) stream->ptr);
|
||||||
|
|
||||||
|
// close the stream
|
||||||
|
S_CodecUtilClose(&stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=================
|
||||||
|
S_OggOpus_CodecReadStream
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
int S_OggOpus_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer)
|
||||||
|
{
|
||||||
|
// buffer handling
|
||||||
|
int samplesRead, samplesLeft, c;
|
||||||
|
opus_int16 *bufPtr;
|
||||||
|
|
||||||
|
// check if input is valid
|
||||||
|
if(!(stream && buffer))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(bytes <= 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
samplesRead = 0;
|
||||||
|
samplesLeft = bytes / stream->info.channels / stream->info.width;
|
||||||
|
bufPtr = buffer;
|
||||||
|
|
||||||
|
if(samplesLeft <= 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cycle until we have the requested or all available bytes read
|
||||||
|
while(-1)
|
||||||
|
{
|
||||||
|
// read some samples from the opus codec
|
||||||
|
c = op_read((OggOpusFile *) stream->ptr, bufPtr + samplesRead * stream->info.channels, samplesLeft * stream->info.channels, NULL);
|
||||||
|
|
||||||
|
// no more samples are left
|
||||||
|
if(c <= 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
samplesRead += c;
|
||||||
|
samplesLeft -= c;
|
||||||
|
|
||||||
|
// we have enough samples
|
||||||
|
if(samplesLeft <= 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return samplesRead * stream->info.channels * stream->info.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=====================================================================
|
||||||
|
S_OggOpus_CodecLoad
|
||||||
|
|
||||||
|
We handle S_OggOpus_CodecLoad as a special case of the streaming functions
|
||||||
|
where we read the whole stream at once.
|
||||||
|
======================================================================
|
||||||
|
*/
|
||||||
|
void *S_OggOpus_CodecLoad(const char *filename, snd_info_t *info)
|
||||||
|
{
|
||||||
|
snd_stream_t *stream;
|
||||||
|
byte *buffer;
|
||||||
|
int bytesRead;
|
||||||
|
|
||||||
|
// check if input is valid
|
||||||
|
if(!(filename && info))
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// open the file as a stream
|
||||||
|
stream = S_OggOpus_CodecOpenStream(filename);
|
||||||
|
if(!stream)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy over the info
|
||||||
|
info->rate = stream->info.rate;
|
||||||
|
info->width = stream->info.width;
|
||||||
|
info->channels = stream->info.channels;
|
||||||
|
info->samples = stream->info.samples;
|
||||||
|
info->size = stream->info.size;
|
||||||
|
info->dataofs = stream->info.dataofs;
|
||||||
|
|
||||||
|
// allocate a buffer
|
||||||
|
// this buffer must be free-ed by the caller of this function
|
||||||
|
buffer = Hunk_AllocateTempMemory(info->size);
|
||||||
|
if(!buffer)
|
||||||
|
{
|
||||||
|
S_OggOpus_CodecCloseStream(stream);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill the buffer
|
||||||
|
bytesRead = S_OggOpus_CodecReadStream(stream, info->size, buffer);
|
||||||
|
|
||||||
|
// we don't even have read a single byte
|
||||||
|
if(bytesRead <= 0)
|
||||||
|
{
|
||||||
|
Hunk_FreeTempMemory(buffer);
|
||||||
|
S_OggOpus_CodecCloseStream(stream);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
S_OggOpus_CodecCloseStream(stream);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // USE_CODEC_VORBIS
|
Loading…
Reference in a new issue