diff --git a/engine/common/common.h b/engine/common/common.h index cc33743e..eaa5328a 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -479,6 +479,7 @@ typedef enum WF_UNKNOWN = 0, WF_PCMDATA, WF_MPGDATA, + WF_VORBISDATA, WF_TOTALCOUNT, // must be last } sndformat_t; diff --git a/engine/common/soundlib/snd_ogg.c b/engine/common/soundlib/snd_ogg.c index 8f6e7d0c..9119b95f 100644 --- a/engine/common/soundlib/snd_ogg.c +++ b/engine/common/soundlib/snd_ogg.c @@ -31,6 +31,12 @@ typedef struct ogg_filestream_s size_t position; } ogg_filestream_t; +typedef struct vorbis_streaming_ctx_s +{ + file_t *file; + OggVorbis_File vf; +} vorbis_streaming_ctx_t; + static void OggFilestream_Init( ogg_filestream_t *filestream, const char *name, const byte *buffer, size_t filesize ) { filestream->name = name; @@ -91,6 +97,24 @@ static opus_int64 OpusCallback_Tell( void *datasource ) return OggFilestream_Tell( datasource ); } +static size_t FS_ReadOggVorbis( void *ptr, size_t blockSize, size_t nmemb, void *datasource ) +{ + vorbis_streaming_ctx_t *ctx = (vorbis_streaming_ctx_t*)datasource; + return g_fsapi.Read( ctx->file, ptr, blockSize * nmemb ); +} + +static int FS_SeekOggVorbis( void *datasource, int64_t offset, int whence ) +{ + vorbis_streaming_ctx_t *ctx = (vorbis_streaming_ctx_t*)datasource; + return g_fsapi.Seek( ctx->file, offset, whence ); +} + +static long FS_TellOggVorbis( void *datasource ) +{ + vorbis_streaming_ctx_t *ctx = (vorbis_streaming_ctx_t*)datasource; + return g_fsapi.Tell( ctx->file ); +} + static const ov_callbacks ov_callbacks_membuf = { OggFilestream_Read, OggFilestream_Seek, @@ -98,6 +122,13 @@ static const ov_callbacks ov_callbacks_membuf = { OggFilestream_Tell }; +static const ov_callbacks ov_callbacks_fs = { + FS_ReadOggVorbis, + FS_SeekOggVorbis, + NULL, + FS_TellOggVorbis +}; + static const OpusFileCallbacks op_callbacks_membuf = { OpusCallback_Read, OggFilestream_Seek, @@ -108,7 +139,7 @@ static const OpusFileCallbacks op_callbacks_membuf = { /* ================================================================= - Ogg Vorbis decompression + Ogg Vorbis decompression & streaming ================================================================= */ @@ -156,6 +187,124 @@ qboolean Sound_LoadOggVorbis( const char *name, const byte *buffer, fs_offset_t return true; } +stream_t *Stream_OpenOggVorbis( const char *filename ) +{ + stream_t *stream; + vorbis_streaming_ctx_t *ctx; + + ctx = (vorbis_streaming_ctx_t*)Mem_Calloc( host.soundpool, sizeof( vorbis_streaming_ctx_t )); + ctx->file = FS_Open( filename, "rb", false ); + if (!ctx->file) { + Mem_Free( ctx ); + return NULL; + } + + stream = (stream_t*)Mem_Calloc( host.soundpool, sizeof( stream_t )); + stream->file = ctx->file; + stream->pos = 0; + + if( ov_open_callbacks( ctx, &ctx->vf, NULL, 0, ov_callbacks_fs ) < 0 ) + { + Con_DPrintf( S_ERROR "%s: failed to load (%s): file openning error\n", __func__, filename ); + FS_Close( ctx->file ); + Mem_Free( stream ); + Mem_Free( ctx ); + return NULL; + } + + vorbis_info *info = ov_info( &ctx->vf, -1 ); + if( info->channels < 1 || info->channels > 2 ) { + Con_DPrintf( S_ERROR "%s: failed to load (%s): unsuppored channels count\n", __func__, filename ); + FS_Close( ctx->file ); + Mem_Free( stream ); + Mem_Free( ctx ); + return NULL; + } + + stream->buffsize = 0; // how many samples left from previous frame + stream->channels = info->channels; + stream->rate = info->rate; + stream->width = 2; // always 16 bit + stream->ptr = ctx; + stream->type = WF_VORBISDATA; + + return stream; +} + +int Stream_ReadOggVorbis( stream_t *stream, int needBytes, void *buffer ) +{ + int section; + int bytesWritten = 0; + vorbis_streaming_ctx_t *ctx = (vorbis_streaming_ctx_t*)stream->ptr; + + while( 1 ) + { + byte *data; + int outsize; + + if( !stream->buffsize ) + { + if(( stream->pos = ov_read( &ctx->vf, (char*)stream->temp, OUTBUF_SIZE, 0, stream->width, 1, §ion )) <= 0 ) + break; // there was EoF or error + } + + // check remaining size + if( bytesWritten + stream->pos > needBytes ) + outsize = ( needBytes - bytesWritten ); + else outsize = stream->pos; + + // copy raw sample to output buffer + data = (byte *)buffer + bytesWritten; + memcpy( data, &stream->temp[stream->buffsize], outsize ); + bytesWritten += outsize; + stream->pos -= outsize; + stream->buffsize += outsize; + + // continue from this sample on a next call + if( bytesWritten >= needBytes ) + return bytesWritten; + + stream->buffsize = 0; // no bytes remaining + } + + return 0; +} + +int Stream_SetPosOggVorbis( stream_t *stream, int newpos ) +{ + vorbis_streaming_ctx_t *ctx = (vorbis_streaming_ctx_t*)stream->ptr; + if( ov_raw_seek_lap( &ctx->vf, newpos ) == 0 ) { + stream->buffsize = 0; // flush any previous data + return true; + } + return false; // failed to seek +} + +int Stream_GetPosOggVorbis( stream_t *stream ) +{ + vorbis_streaming_ctx_t *ctx = (vorbis_streaming_ctx_t*)stream->ptr; + return ov_raw_tell( &ctx->vf ); +} + +void Stream_FreeOggVorbis( stream_t *stream ) +{ + if( stream->ptr ) + { + vorbis_streaming_ctx_t *ctx = (vorbis_streaming_ctx_t*)stream->ptr; + ov_clear( &ctx->vf ); + Mem_Free( stream->ptr ); + stream->ptr = NULL; + } + + if( stream->file ) + { + FS_Close( stream->file ); + stream->file = NULL; + } + + Mem_Free( stream ); +} + /* ================================================================= diff --git a/engine/common/soundlib/snd_utils.c b/engine/common/soundlib/snd_utils.c index 0f30e37a..0c5684f5 100644 --- a/engine/common/soundlib/snd_utils.c +++ b/engine/common/soundlib/snd_utils.c @@ -61,6 +61,7 @@ static const streamfmt_t stream_game[] = { { "%s%s.%s", "mp3", Stream_OpenMPG, Stream_ReadMPG, Stream_SetPosMPG, Stream_GetPosMPG, Stream_FreeMPG }, { "%s%s.%s", "wav", Stream_OpenWAV, Stream_ReadWAV, Stream_SetPosWAV, Stream_GetPosWAV, Stream_FreeWAV }, +{ "%s%s.%s", "ogg", Stream_OpenOggVorbis, Stream_ReadOggVorbis, Stream_SetPosOggVorbis, Stream_GetPosOggVorbis, Stream_FreeOggVorbis }, { NULL, NULL, NULL, NULL, NULL, NULL, NULL } }; diff --git a/engine/common/soundlib/soundlib.h b/engine/common/soundlib/soundlib.h index 1b9131fd..b9179bec 100644 --- a/engine/common/soundlib/soundlib.h +++ b/engine/common/soundlib/soundlib.h @@ -135,5 +135,10 @@ int Stream_ReadMPG( stream_t *stream, int bytes, void *buffer ); int Stream_SetPosMPG( stream_t *stream, int newpos ); int Stream_GetPosMPG( stream_t *stream ); void Stream_FreeMPG( stream_t *stream ); +stream_t *Stream_OpenOggVorbis( const char *filename ); +int Stream_ReadOggVorbis( stream_t *stream, int bytes, void *buffer ); +int Stream_SetPosOggVorbis( stream_t *stream, int newpos ); +int Stream_GetPosOggVorbis( stream_t *stream ); +void Stream_FreeOggVorbis( stream_t *stream ); #endif//SOUNDLIB_H