2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
26 ===========================================================================
29 #include "../idlib/precompiled.h"
32 #include "snd_local.h"
33 #include "OggVorbis/vorbis/codec.h"
34 #include "OggVorbis/vorbis/vorbisfile.h"
38 ===================================================================================
40 Thread safe decoder memory allocator.
42 Each OggVorbis decoder consumes about 150kB of memory.
44 ===================================================================================
47 idDynamicBlockAlloc<byte, 1<<20, 128> decoderMemoryAllocator;
49 const int MIN_OGGVORBIS_MEMORY = 768 * 1024;
52 void *_decoder_malloc( size_t size );
53 void *_decoder_calloc( size_t num, size_t size );
54 void *_decoder_realloc( void *memblock, size_t size );
55 void _decoder_free( void *memblock );
58 void *_decoder_malloc( size_t size ) {
59 void *ptr = decoderMemoryAllocator.Alloc( size );
60 assert( size == 0 || ptr != NULL );
64 void *_decoder_calloc( size_t num, size_t size ) {
65 void *ptr = decoderMemoryAllocator.Alloc( num * size );
66 assert( ( num * size ) == 0 || ptr != NULL );
67 memset( ptr, 0, num * size );
71 void *_decoder_realloc( void *memblock, size_t size ) {
72 void *ptr = decoderMemoryAllocator.Resize( (byte *)memblock, size );
73 assert( size == 0 || ptr != NULL );
77 void _decoder_free( void *memblock ) {
78 decoderMemoryAllocator.Free( (byte *)memblock );
83 ===================================================================================
85 OggVorbis file loading/decoding.
87 ===================================================================================
95 size_t FS_ReadOGG( void *dest, size_t size1, size_t size2, void *fh ) {
96 idFile *f = reinterpret_cast<idFile *>(fh);
97 return f->Read( dest, size1 * size2 );
105 int FS_SeekOGG( void *fh, ogg_int64_t to, int type ) {
106 fsOrigin_t retype = FS_SEEK_SET;
108 if ( type == SEEK_CUR ) {
109 retype = FS_SEEK_CUR;
110 } else if ( type == SEEK_END ) {
111 retype = FS_SEEK_END;
112 } else if ( type == SEEK_SET ) {
113 retype = FS_SEEK_SET;
115 common->FatalError( "fs_seekOGG: seek without type\n" );
117 idFile *f = reinterpret_cast<idFile *>(fh);
118 return f->Seek( to, retype );
126 int FS_CloseOGG( void *fh ) {
135 long FS_TellOGG( void *fh ) {
136 idFile *f = reinterpret_cast<idFile *>(fh);
145 int ov_openFile( idFile *f, OggVorbis_File *vf ) {
146 ov_callbacks callbacks;
148 memset( vf, 0, sizeof( OggVorbis_File ) );
150 callbacks.read_func = FS_ReadOGG;
151 callbacks.seek_func = FS_SeekOGG;
152 callbacks.close_func = FS_CloseOGG;
153 callbacks.tell_func = FS_TellOGG;
154 return ov_open_callbacks((void *)f, vf, NULL, -1, callbacks);
162 int idWaveFile::OpenOGG( const char* strFileName, waveformatex_t *pwfx ) {
165 memset( pwfx, 0, sizeof( waveformatex_t ) );
167 mhmmio = fileSystem->OpenFileRead( strFileName );
172 Sys_EnterCriticalSection( CRITICAL_SECTION_ONE );
174 ov = new OggVorbis_File;
176 if( ov_openFile( mhmmio, ov ) < 0 ) {
178 Sys_LeaveCriticalSection( CRITICAL_SECTION_ONE );
179 fileSystem->CloseFile( mhmmio );
184 mfileTime = mhmmio->Timestamp();
186 vorbis_info *vi = ov_info( ov, -1 );
188 mpwfx.Format.nSamplesPerSec = vi->rate;
189 mpwfx.Format.nChannels = vi->channels;
190 mpwfx.Format.wBitsPerSample = sizeof(short) * 8;
191 mdwSize = ov_pcm_total( ov, -1 ) * vi->channels; // pcm samples * num channels
192 mbIsReadingFromMemory = false;
194 if ( idSoundSystemLocal::s_realTimeDecoding.GetBool() ) {
197 fileSystem->CloseFile( mhmmio );
201 mpwfx.Format.wFormatTag = WAVE_FORMAT_TAG_OGG;
202 mhmmio = fileSystem->OpenFileRead( strFileName );
203 mMemSize = mhmmio->Length();
209 mpwfx.Format.wFormatTag = WAVE_FORMAT_TAG_PCM;
210 mMemSize = mdwSize * sizeof( short );
213 memcpy( pwfx, &mpwfx, sizeof( waveformatex_t ) );
215 Sys_LeaveCriticalSection( CRITICAL_SECTION_ONE );
227 int idWaveFile::ReadOGG( byte* pBuffer, int dwSizeToRead, int *pdwSizeRead ) {
228 int total = dwSizeToRead;
229 char *bufferPtr = (char *)pBuffer;
230 OggVorbis_File *ov = (OggVorbis_File *) ogg;
233 int ret = ov_read( ov, bufferPtr, total >= 4096 ? 4096 : total, Swap_IsBigEndian(), 2, 1, &ov->stream );
242 } while( total > 0 );
244 dwSizeToRead = (byte *)bufferPtr - pBuffer;
246 if ( pdwSizeRead != NULL ) {
247 *pdwSizeRead = dwSizeToRead;
258 int idWaveFile::CloseOGG( void ) {
259 OggVorbis_File *ov = (OggVorbis_File *) ogg;
261 Sys_EnterCriticalSection( CRITICAL_SECTION_ONE );
264 Sys_LeaveCriticalSection( CRITICAL_SECTION_ONE );
265 fileSystem->CloseFile( mhmmio );
275 ===================================================================================
279 ===================================================================================
282 class idSampleDecoderLocal : public idSampleDecoder {
284 virtual void Decode( idSoundSample *sample, int sampleOffset44k, int sampleCount44k, float *dest );
285 virtual void ClearDecoder( void );
286 virtual idSoundSample * GetSample( void ) const;
287 virtual int GetLastDecodeTime( void ) const;
290 int DecodePCM( idSoundSample *sample, int sampleOffset44k, int sampleCount44k, float *dest );
291 int DecodeOGG( idSoundSample *sample, int sampleOffset44k, int sampleCount44k, float *dest );
294 bool failed; // set if decoding failed
295 int lastFormat; // last format being decoded
296 idSoundSample * lastSample; // last sample being decoded
297 int lastSampleOffset; // last offset into the decoded sample
298 int lastDecodeTime; // last time decoding sound
299 idFile_Memory file; // encoded file in memory
301 OggVorbis_File ogg; // OggVorbis file
304 idBlockAlloc<idSampleDecoderLocal, 64> sampleDecoderAllocator;
308 idSampleDecoder::Init
311 void idSampleDecoder::Init( void ) {
312 decoderMemoryAllocator.Init();
313 decoderMemoryAllocator.SetLockMemory( true );
314 decoderMemoryAllocator.SetFixedBlocks( idSoundSystemLocal::s_realTimeDecoding.GetBool() ? 10 : 1 );
319 idSampleDecoder::Shutdown
322 void idSampleDecoder::Shutdown( void ) {
323 decoderMemoryAllocator.Shutdown();
324 sampleDecoderAllocator.Shutdown();
329 idSampleDecoder::Alloc
332 idSampleDecoder *idSampleDecoder::Alloc( void ) {
333 idSampleDecoderLocal *decoder = sampleDecoderAllocator.Alloc();
340 idSampleDecoder::Free
343 void idSampleDecoder::Free( idSampleDecoder *decoder ) {
344 idSampleDecoderLocal *localDecoder = static_cast<idSampleDecoderLocal *>( decoder );
345 localDecoder->ClearDecoder();
346 sampleDecoderAllocator.Free( localDecoder );
351 idSampleDecoder::GetNumUsedBlocks
354 int idSampleDecoder::GetNumUsedBlocks( void ) {
355 return decoderMemoryAllocator.GetNumUsedBlocks();
360 idSampleDecoder::GetUsedBlockMemory
363 int idSampleDecoder::GetUsedBlockMemory( void ) {
364 return decoderMemoryAllocator.GetUsedBlockMemory();
369 idSampleDecoderLocal::Clear
372 void idSampleDecoderLocal::Clear( void ) {
374 lastFormat = WAVE_FORMAT_TAG_PCM;
376 lastSampleOffset = 0;
382 idSampleDecoderLocal::ClearDecoder
385 void idSampleDecoderLocal::ClearDecoder( void ) {
386 Sys_EnterCriticalSection( CRITICAL_SECTION_ONE );
388 switch( lastFormat ) {
389 case WAVE_FORMAT_TAG_PCM: {
392 case WAVE_FORMAT_TAG_OGG: {
394 memset( &ogg, 0, sizeof( ogg ) );
401 Sys_LeaveCriticalSection( CRITICAL_SECTION_ONE );
406 idSampleDecoderLocal::GetSample
409 idSoundSample *idSampleDecoderLocal::GetSample( void ) const {
415 idSampleDecoderLocal::GetLastDecodeTime
418 int idSampleDecoderLocal::GetLastDecodeTime( void ) const {
419 return lastDecodeTime;
424 idSampleDecoderLocal::Decode
427 void idSampleDecoderLocal::Decode( idSoundSample *sample, int sampleOffset44k, int sampleCount44k, float *dest ) {
430 if ( sample->objectInfo.wFormatTag != lastFormat || sample != lastSample ) {
434 lastDecodeTime = soundSystemLocal.CurrentSoundTime;
437 memset( dest, 0, sampleCount44k * sizeof( dest[0] ) );
441 // samples can be decoded both from the sound thread and the main thread for shakes
442 Sys_EnterCriticalSection( CRITICAL_SECTION_ONE );
444 switch( sample->objectInfo.wFormatTag ) {
445 case WAVE_FORMAT_TAG_PCM: {
446 readSamples44k = DecodePCM( sample, sampleOffset44k, sampleCount44k, dest );
449 case WAVE_FORMAT_TAG_OGG: {
450 readSamples44k = DecodeOGG( sample, sampleOffset44k, sampleCount44k, dest );
459 Sys_LeaveCriticalSection( CRITICAL_SECTION_ONE );
461 if ( readSamples44k < sampleCount44k ) {
462 memset( dest + readSamples44k, 0, ( sampleCount44k - readSamples44k ) * sizeof( dest[0] ) );
468 idSampleDecoderLocal::DecodePCM
471 int idSampleDecoderLocal::DecodePCM( idSoundSample *sample, int sampleOffset44k, int sampleCount44k, float *dest ) {
473 int pos, size, readSamples;
475 lastFormat = WAVE_FORMAT_TAG_PCM;
478 int shift = 22050 / sample->objectInfo.nSamplesPerSec;
479 int sampleOffset = sampleOffset44k >> shift;
480 int sampleCount = sampleCount44k >> shift;
482 if ( sample->nonCacheData == NULL ) {
483 assert( false ); // this should never happen ( note: I've seen that happen with the main thread down in idGameLocal::MapClear clearing entities - TTimo )
488 if ( !sample->FetchFromCache( sampleOffset * sizeof( short ), &first, &pos, &size, false ) ) {
493 if ( size - pos < sampleCount * sizeof( short ) ) {
494 readSamples = ( size - pos ) / sizeof( short );
496 readSamples = sampleCount;
499 // duplicate samples for 44kHz output
500 SIMDProcessor->UpSamplePCMTo44kHz( dest, (const short *)(first+pos), readSamples, sample->objectInfo.nSamplesPerSec, sample->objectInfo.nChannels );
502 return ( readSamples << shift );
507 idSampleDecoderLocal::DecodeOGG
510 int idSampleDecoderLocal::DecodeOGG( idSoundSample *sample, int sampleOffset44k, int sampleCount44k, float *dest ) {
511 int readSamples, totalSamples;
513 int shift = 22050 / sample->objectInfo.nSamplesPerSec;
514 int sampleOffset = sampleOffset44k >> shift;
515 int sampleCount = sampleCount44k >> shift;
517 // open OGG file if not yet opened
518 if ( lastSample == NULL ) {
519 // make sure there is enough space for another decoder
520 if ( decoderMemoryAllocator.GetFreeBlockMemory() < MIN_OGGVORBIS_MEMORY ) {
523 if ( sample->nonCacheData == NULL ) {
524 assert( false ); // this should never happen
528 file.SetData( (const char *)sample->nonCacheData, sample->objectMemSize );
529 if ( ov_openFile( &file, &ogg ) < 0 ) {
533 lastFormat = WAVE_FORMAT_TAG_OGG;
537 // seek to the right offset if necessary
538 if ( sampleOffset != lastSampleOffset ) {
539 if ( ov_pcm_seek( &ogg, sampleOffset / sample->objectInfo.nChannels ) != 0 ) {
545 lastSampleOffset = sampleOffset;
547 // decode OGG samples
548 totalSamples = sampleCount;
552 int ret = ov_read_float( &ogg, &samples, totalSamples / sample->objectInfo.nChannels, &ogg.stream );
561 ret *= sample->objectInfo.nChannels;
563 SIMDProcessor->UpSampleOGGTo44kHz( dest + ( readSamples << shift ), samples, ret, sample->objectInfo.nSamplesPerSec, sample->objectInfo.nChannels );
567 } while( totalSamples > 0 );
569 lastSampleOffset += readSamples;
571 return ( readSamples << shift );