]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/sound/snd_cache.cpp
hello world
[icculus/iodoom3.git] / neo / sound / snd_cache.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
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.
13
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.
18
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/>.
21
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.
23
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.
25
26 ===========================================================================
27 */
28 #include "../idlib/precompiled.h"
29 #pragma hdrstop
30
31 #include "snd_local.h"
32
33 #define USE_SOUND_CACHE_ALLOCATOR
34
35 #ifdef USE_SOUND_CACHE_ALLOCATOR
36 static idDynamicBlockAlloc<byte, 1<<20, 1<<10>  soundCacheAllocator;
37 #else
38 static idDynamicAlloc<byte, 1<<20, 1<<10>               soundCacheAllocator;
39 #endif
40
41
42 /*
43 ===================
44 idSoundCache::idSoundCache()
45 ===================
46 */
47 idSoundCache::idSoundCache() {
48         soundCacheAllocator.Init();
49         soundCacheAllocator.SetLockMemory( true );
50         listCache.AssureSize( 1024, NULL );
51         listCache.SetGranularity( 256 );
52         insideLevelLoad = false;
53 }
54
55 /*
56 ===================
57 idSoundCache::~idSoundCache()
58 ===================
59 */
60 idSoundCache::~idSoundCache() {
61         listCache.DeleteContents( true );
62         soundCacheAllocator.Shutdown();
63 }
64
65 /*
66 ===================
67 idSoundCache::::GetObject
68
69 returns a single cached object pointer
70 ===================
71 */
72 const idSoundSample* idSoundCache::GetObject( const int index ) const {
73         if (index<0 || index>listCache.Num()) {
74                 return NULL;
75         }
76         return listCache[index]; 
77 }
78
79 /*
80 ===================
81 idSoundCache::FindSound
82
83 Adds a sound object to the cache and returns a handle for it.
84 ===================
85 */
86 idSoundSample *idSoundCache::FindSound( const idStr& filename, bool loadOnDemandOnly ) {
87         idStr fname;
88
89         fname = filename;
90         fname.BackSlashesToSlashes();
91         fname.ToLower();
92
93         declManager->MediaPrint( "%s\n", fname.c_str() );
94
95         // check to see if object is already in cache
96         for( int i = 0; i < listCache.Num(); i++ ) {
97                 idSoundSample *def = listCache[i];
98                 if ( def && def->name == fname ) {
99                         def->levelLoadReferenced = true;
100                         if ( def->purged && !loadOnDemandOnly ) {
101                                 def->Load();
102                         }
103                         return def;
104                 }
105         }
106
107         // create a new entry
108         idSoundSample *def = new idSoundSample;
109
110         int shandle = listCache.FindNull();
111         if ( shandle != -1 ) {
112                 listCache[shandle] = def;
113         } else {
114                 shandle = listCache.Append( def );
115         }
116
117         def->name = fname;
118         def->levelLoadReferenced = true;
119         def->onDemand = loadOnDemandOnly;
120         def->purged = true;
121
122         if ( !loadOnDemandOnly ) {
123                 // this may make it a default sound if it can't be loaded
124                 def->Load();
125         }
126
127         return def;
128 }
129
130 /*
131 ===================
132 idSoundCache::ReloadSounds
133
134 Completely nukes the current cache
135 ===================
136 */
137 void idSoundCache::ReloadSounds( bool force ) {
138         int i;
139
140         for( i = 0; i < listCache.Num(); i++ ) {
141                 idSoundSample *def = listCache[i];
142                 if ( def ) {
143                         def->Reload( force );
144                 }
145         }
146 }
147
148 /*
149 ====================
150 BeginLevelLoad
151
152 Mark all file based images as currently unused,
153 but don't free anything.  Calls to ImageFromFile() will
154 either mark the image as used, or create a new image without
155 loading the actual data.
156 ====================
157 */
158 void idSoundCache::BeginLevelLoad() {
159         insideLevelLoad = true;
160
161         for ( int i = 0 ; i < listCache.Num() ; i++ ) {
162                 idSoundSample *sample = listCache[ i ];
163                 if ( !sample ) {
164                         continue;
165                 }
166
167                 if ( com_purgeAll.GetBool() ) {
168                         sample->PurgeSoundSample();
169                 }
170
171                 sample->levelLoadReferenced = false;
172         }
173
174         soundCacheAllocator.FreeEmptyBaseBlocks();
175 }
176
177 /*
178 ====================
179 EndLevelLoad
180
181 Free all samples marked as unused
182 ====================
183 */
184 void idSoundCache::EndLevelLoad() {
185         int     useCount, purgeCount;
186         common->Printf( "----- idSoundCache::EndLevelLoad -----\n" );
187
188         insideLevelLoad = false;
189
190         // purge the ones we don't need
191         useCount = 0;
192         purgeCount = 0;
193         for ( int i = 0 ; i < listCache.Num() ; i++ ) {
194                 idSoundSample   *sample = listCache[ i ];
195                 if ( !sample ) {
196                         continue;
197                 }
198                 if ( sample->purged ) {
199                         continue;
200                 }
201                 if ( !sample->levelLoadReferenced ) {
202 //                      common->Printf( "Purging %s\n", sample->name.c_str() );
203                         purgeCount += sample->objectMemSize;
204                         sample->PurgeSoundSample();
205                 } else {
206                         useCount += sample->objectMemSize;
207                 }
208         }
209
210         soundCacheAllocator.FreeEmptyBaseBlocks();
211
212         common->Printf( "%5ik referenced\n", useCount / 1024 );
213         common->Printf( "%5ik purged\n", purgeCount / 1024 );
214         common->Printf( "----------------------------------------\n" );
215 }
216
217 /*
218 ===================
219 idSoundCache::PrintMemInfo
220 ===================
221 */
222 void idSoundCache::PrintMemInfo( MemInfo_t *mi ) {
223         int i, j, num = 0, total = 0;
224         int *sortIndex;
225         idFile *f;
226
227         f = fileSystem->OpenFileWrite( mi->filebase + "_sounds.txt" );
228         if ( !f ) {
229                 return;
230         }
231
232         // count
233         for ( i = 0; i < listCache.Num(); i++, num++ ) {
234                 if ( !listCache[i] ) {
235                         break;
236                 }
237         }
238
239         // sort first
240         sortIndex = new int[num];
241
242         for ( i = 0; i < num; i++ ) {
243                 sortIndex[i] = i;
244         }
245
246         for ( i = 0; i < num - 1; i++ ) {
247                 for ( j = i + 1; j < num; j++ ) {
248                         if ( listCache[sortIndex[i]]->objectMemSize < listCache[sortIndex[j]]->objectMemSize ) {
249                                 int temp = sortIndex[i];
250                                 sortIndex[i] = sortIndex[j];
251                                 sortIndex[j] = temp;
252                         }
253                 }
254         }
255
256         // print next
257         for ( i = 0; i < num; i++ ) {
258                 idSoundSample *sample = listCache[sortIndex[i]];
259
260                 // this is strange
261                 if ( !sample ) {
262                         continue;
263                 }
264
265                 total += sample->objectMemSize;
266                 f->Printf( "%s %s\n", idStr::FormatNumber( sample->objectMemSize ).c_str(), sample->name.c_str() );
267         }
268
269         mi->soundAssetsTotal = total;
270
271         f->Printf( "\nTotal sound bytes allocated: %s\n", idStr::FormatNumber( total ).c_str() );
272         fileSystem->CloseFile( f );
273 }
274
275
276 /*
277 ==========================================================================
278
279 idSoundSample
280
281 ==========================================================================
282 */
283
284 /*
285 ===================
286 idSoundSample::idSoundSample
287 ===================
288 */
289 idSoundSample::idSoundSample() {
290         memset( &objectInfo, 0, sizeof(waveformatex_t) );
291         objectSize = 0;
292         objectMemSize = 0;
293         nonCacheData = NULL;
294         amplitudeData = NULL;
295         openalBuffer = NULL;
296         hardwareBuffer = false;
297         defaultSound = false;
298         onDemand = false;
299         purged = false;
300         levelLoadReferenced = false;
301 }
302
303 /*
304 ===================
305 idSoundSample::~idSoundSample
306 ===================
307 */
308 idSoundSample::~idSoundSample() {
309         PurgeSoundSample();
310 }
311
312 /*
313 ===================
314 idSoundSample::LengthIn44kHzSamples
315 ===================
316 */
317 int idSoundSample::LengthIn44kHzSamples( void ) const {
318         // objectSize is samples
319         if ( objectInfo.nSamplesPerSec == 11025 ) {
320                 return objectSize << 2;
321         } else if ( objectInfo.nSamplesPerSec == 22050 ) {
322                 return objectSize << 1;
323         } else {
324                 return objectSize << 0;                 
325         }
326 }
327
328 /*
329 ===================
330 idSoundSample::MakeDefault
331 ===================
332 */
333 void idSoundSample::MakeDefault( void ) {       
334         int             i;
335         float   v;
336         int             sample;
337
338         memset( &objectInfo, 0, sizeof( objectInfo ) );
339
340         objectInfo.nChannels = 1;
341         objectInfo.wBitsPerSample = 16;
342         objectInfo.nSamplesPerSec = 44100;
343
344         objectSize = MIXBUFFER_SAMPLES * 2;
345         objectMemSize = objectSize * sizeof( short );
346
347         nonCacheData = (byte *)soundCacheAllocator.Alloc( objectMemSize );
348
349         short *ncd = (short *)nonCacheData;
350
351         for ( i = 0; i < MIXBUFFER_SAMPLES; i ++ ) {
352                 v = sin( idMath::PI * 2 * i / 64 );
353                 sample = v * 0x4000;
354                 ncd[i*2+0] = sample;
355                 ncd[i*2+1] = sample;
356         }
357
358         if ( idSoundSystemLocal::useOpenAL ) {
359                 alGetError();
360                 alGenBuffers( 1, &openalBuffer );
361                 if ( alGetError() != AL_NO_ERROR ) {
362                         common->Error( "idSoundCache: error generating OpenAL hardware buffer" );
363                 }
364                 
365                 alGetError();
366                 alBufferData( openalBuffer, objectInfo.nChannels==1?AL_FORMAT_MONO16:AL_FORMAT_STEREO16, nonCacheData, objectMemSize, objectInfo.nSamplesPerSec );
367                 if ( alGetError() != AL_NO_ERROR ) {
368                         common->Error( "idSoundCache: error loading data into OpenAL hardware buffer" );
369                 } else {
370                         hardwareBuffer = true;
371                 }
372         }
373
374         defaultSound = true;
375 }
376
377 /*
378 ===================
379 idSoundSample::CheckForDownSample
380 ===================
381 */
382 void idSoundSample::CheckForDownSample( void ) {
383         if ( !idSoundSystemLocal::s_force22kHz.GetBool() ) {
384                 return;
385         }
386         if ( objectInfo.wFormatTag != WAVE_FORMAT_TAG_PCM || objectInfo.nSamplesPerSec != 44100 ) {
387                 return;
388         }
389         int shortSamples = objectSize >> 1;
390         short *converted = (short *)soundCacheAllocator.Alloc( shortSamples * sizeof( short ) );
391
392         if ( objectInfo.nChannels == 1 ) {
393                 for ( int i = 0; i < shortSamples; i++ ) {
394                         converted[i] = ((short *)nonCacheData)[i*2];
395                 }
396         } else {
397                 for ( int i = 0; i < shortSamples; i += 2 ) {
398                         converted[i+0] = ((short *)nonCacheData)[i*2+0];
399                         converted[i+1] = ((short *)nonCacheData)[i*2+1];
400                 }
401         }
402         soundCacheAllocator.Free( nonCacheData );
403         nonCacheData = (byte *)converted;
404         objectSize >>= 1;
405         objectMemSize >>= 1;
406         objectInfo.nAvgBytesPerSec >>= 1;
407         objectInfo.nSamplesPerSec >>= 1;
408 }
409
410 /*
411 ===================
412 idSoundSample::GetNewTimeStamp
413 ===================
414 */
415 ID_TIME_T idSoundSample::GetNewTimeStamp( void ) const {
416         ID_TIME_T timestamp;
417
418         fileSystem->ReadFile( name, NULL, &timestamp );
419         if ( timestamp == FILE_NOT_FOUND_TIMESTAMP ) {
420                 idStr oggName = name;
421                 oggName.SetFileExtension( ".ogg" );
422                 fileSystem->ReadFile( oggName, NULL, &timestamp );
423         }
424         return timestamp;
425 }
426
427 /*
428 ===================
429 idSoundSample::Load
430
431 Loads based on name, possibly doing a MakeDefault if necessary
432 ===================
433 */
434 void idSoundSample::Load( void ) {      
435         defaultSound = false;
436         purged = false;
437         hardwareBuffer = false;
438
439         timestamp = GetNewTimeStamp();
440
441         if ( timestamp == FILE_NOT_FOUND_TIMESTAMP ) {
442                 common->Warning( "Couldn't load sound '%s' using default", name.c_str() );
443                 MakeDefault();
444                 return;
445         }
446
447         // load it
448         idWaveFile      fh;
449         waveformatex_t info;
450
451         if ( fh.Open( name, &info ) == -1 ) {
452                 common->Warning( "Couldn't load sound '%s' using default", name.c_str() );
453                 MakeDefault();
454                 return;
455         }
456
457         if ( info.nChannels != 1 && info.nChannels != 2 ) {
458                 common->Warning( "idSoundSample: %s has %i channels, using default", name.c_str(), info.nChannels );
459                 fh.Close();
460                 MakeDefault();
461                 return;
462         }
463
464         if ( info.wBitsPerSample != 16 ) {
465                 common->Warning( "idSoundSample: %s is %dbits, expected 16bits using default", name.c_str(), info.wBitsPerSample );
466                 fh.Close();
467                 MakeDefault();
468                 return;
469         }
470
471         if ( info.nSamplesPerSec != 44100 && info.nSamplesPerSec != 22050 && info.nSamplesPerSec != 11025 ) {
472                 common->Warning( "idSoundCache: %s is %dHz, expected 11025, 22050 or 44100 Hz. Using default", name.c_str(), info.nSamplesPerSec );
473                 fh.Close();
474                 MakeDefault();
475                 return;
476         }
477
478         objectInfo = info;
479         objectSize = fh.GetOutputSize();
480         objectMemSize = fh.GetMemorySize();
481
482         nonCacheData = (byte *)soundCacheAllocator.Alloc( objectMemSize );
483         fh.Read( nonCacheData, objectMemSize, NULL );
484
485         // optionally convert it to 22kHz to save memory
486         CheckForDownSample();
487
488         // create hardware audio buffers 
489         if ( idSoundSystemLocal::useOpenAL ) {
490                 // PCM loads directly
491                 if ( objectInfo.wFormatTag == WAVE_FORMAT_TAG_PCM ) {
492                         alGetError();
493                         alGenBuffers( 1, &openalBuffer );
494                         if ( alGetError() != AL_NO_ERROR )
495                                 common->Error( "idSoundCache: error generating OpenAL hardware buffer" );
496                         if ( alIsBuffer( openalBuffer ) ) {
497                                 alGetError();
498                                 alBufferData( openalBuffer, objectInfo.nChannels==1?AL_FORMAT_MONO16:AL_FORMAT_STEREO16, nonCacheData, objectMemSize, objectInfo.nSamplesPerSec );
499                                 if ( alGetError() != AL_NO_ERROR ) {
500                                         common->Error( "idSoundCache: error loading data into OpenAL hardware buffer" );
501                                 } else {
502                                         // Compute amplitude block size
503                                         int blockSize = 512 * objectInfo.nSamplesPerSec / 44100 ;
504
505                                         // Allocate amplitude data array
506                                         amplitudeData = (byte *)soundCacheAllocator.Alloc( ( objectSize / blockSize + 1 ) * 2 * sizeof( short) );
507
508                                         // Creating array of min/max amplitude pairs per blockSize samples
509                                         int i;
510                                         for ( i = 0; i < objectSize; i+=blockSize ) {
511                                                 short min = 32767;
512                                                 short max = -32768;
513
514                                                 int j;
515                                                 for ( j = 0; j < Min( objectSize - i, blockSize ); j++ ) {
516                                                         min = ((short *)nonCacheData)[ i + j ] < min ? ((short *)nonCacheData)[ i + j ] : min;
517                                                         max = ((short *)nonCacheData)[ i + j ] > max ? ((short *)nonCacheData)[ i + j ] : max;
518                                                 }
519
520                                                 ((short *)amplitudeData)[ ( i / blockSize ) * 2     ] = min;
521                                                 ((short *)amplitudeData)[ ( i / blockSize ) * 2 + 1 ] = max;
522                                         }
523
524                                         hardwareBuffer = true;
525                                 }
526                         }
527                 } 
528                 
529                 // OGG decompressed at load time (when smaller than s_decompressionLimit seconds, 6 seconds by default)
530                 if ( objectInfo.wFormatTag == WAVE_FORMAT_TAG_OGG ) {
531 #if defined(MACOS_X)
532                         if ( ( objectSize < ( ( int ) objectInfo.nSamplesPerSec * idSoundSystemLocal::s_decompressionLimit.GetInteger() ) ) ) {
533 #else
534                         if ( ( alIsExtensionPresent( ID_ALCHAR "EAX-RAM" ) == AL_TRUE ) && ( objectSize < ( ( int ) objectInfo.nSamplesPerSec * idSoundSystemLocal::s_decompressionLimit.GetInteger() ) ) ) {
535 #endif
536                                 alGetError();
537                                 alGenBuffers( 1, &openalBuffer );
538                                 if ( alGetError() != AL_NO_ERROR )
539                                         common->Error( "idSoundCache: error generating OpenAL hardware buffer" );
540                                 if ( alIsBuffer( openalBuffer ) ) {
541                                         idSampleDecoder *decoder = idSampleDecoder::Alloc();
542                                         float *destData = (float *)soundCacheAllocator.Alloc( ( LengthIn44kHzSamples() + 1 ) * sizeof( float ) );
543                                         
544                                         // Decoder *always* outputs 44 kHz data
545                                         decoder->Decode( this, 0, LengthIn44kHzSamples(), destData );
546
547                                         // Downsample back to original frequency (save memory)
548                                         if ( objectInfo.nSamplesPerSec == 11025 ) {
549                                                 for ( int i = 0; i < objectSize; i++ ) {
550                                                         if ( destData[i*4] < -32768.0f )
551                                                                 ((short *)destData)[i] = -32768;
552                                                         else if ( destData[i*4] > 32767.0f )
553                                                                 ((short *)destData)[i] = 32767;
554                                                         else
555                                                                 ((short *)destData)[i] = idMath::FtoiFast( destData[i*4] );
556                                                 }
557                                         } else if ( objectInfo.nSamplesPerSec == 22050 ) {
558                                                 for ( int i = 0; i < objectSize; i++ ) {
559                                                         if ( destData[i*2] < -32768.0f )
560                                                                 ((short *)destData)[i] = -32768;
561                                                         else if ( destData[i*2] > 32767.0f )
562                                                                 ((short *)destData)[i] = 32767;
563                                                         else
564                                                                 ((short *)destData)[i] = idMath::FtoiFast( destData[i*2] );
565                                                 }
566                                         } else {
567                                                 for ( int i = 0; i < objectSize; i++ ) {
568                                                         if ( destData[i] < -32768.0f )
569                                                                 ((short *)destData)[i] = -32768;
570                                                         else if ( destData[i] > 32767.0f )
571                                                                 ((short *)destData)[i] = 32767;
572                                                         else
573                                                                 ((short *)destData)[i] = idMath::FtoiFast( destData[i] );
574                                                 }
575                                         }
576
577                                         alGetError();
578                                         alBufferData( openalBuffer, objectInfo.nChannels==1?AL_FORMAT_MONO16:AL_FORMAT_STEREO16, destData, objectSize * sizeof( short ), objectInfo.nSamplesPerSec );
579                                         if ( alGetError() != AL_NO_ERROR )
580                                                 common->Error( "idSoundCache: error loading data into OpenAL hardware buffer" );
581                                         else {
582                                                 // Compute amplitude block size
583                                                 int blockSize = 512 * objectInfo.nSamplesPerSec / 44100 ;
584
585                                                 // Allocate amplitude data array
586                                                 amplitudeData = (byte *)soundCacheAllocator.Alloc( ( objectSize / blockSize + 1 ) * 2 * sizeof( short ) );
587
588                                                 // Creating array of min/max amplitude pairs per blockSize samples
589                                                 int i;
590                                                 for ( i = 0; i < objectSize; i+=blockSize ) {
591                                                         short min = 32767;
592                                                         short max = -32768;
593                                                         
594                                                         int j;
595                                                         for ( j = 0; j < Min( objectSize - i, blockSize ); j++ ) {
596                                                                 min = ((short *)destData)[ i + j ] < min ? ((short *)destData)[ i + j ] : min;
597                                                                 max = ((short *)destData)[ i + j ] > max ? ((short *)destData)[ i + j ] : max;
598                                                         }
599
600                                                         ((short *)amplitudeData)[ ( i / blockSize ) * 2     ] = min;
601                                                         ((short *)amplitudeData)[ ( i / blockSize ) * 2 + 1 ] = max;
602                                                 }
603                                                 
604                                                 hardwareBuffer = true;
605                                         }
606
607                                         soundCacheAllocator.Free( (byte *)destData );
608                                         idSampleDecoder::Free( decoder );
609                                 }
610                         }
611                 }
612
613                 // Free memory if sample was loaded into hardware
614                 if ( hardwareBuffer ) {
615                         soundCacheAllocator.Free( nonCacheData );
616                         nonCacheData = NULL;
617                 }
618         }
619
620         fh.Close();
621 }
622
623 /*
624 ===================
625 idSoundSample::PurgeSoundSample
626 ===================
627 */
628 void idSoundSample::PurgeSoundSample() {
629         purged = true;
630
631         if ( hardwareBuffer && idSoundSystemLocal::useOpenAL ) {
632                 alGetError();
633                 alDeleteBuffers( 1, &openalBuffer );
634                 if ( alGetError() != AL_NO_ERROR ) {
635                         common->Error( "idSoundCache: error unloading data from OpenAL hardware buffer" );
636                 } else {
637                         openalBuffer = 0;
638                         hardwareBuffer = false;
639                 }
640         }
641
642         if ( amplitudeData ) {
643                 soundCacheAllocator.Free( amplitudeData );
644                 amplitudeData = NULL;
645         }
646
647         if ( nonCacheData ) {
648                 soundCacheAllocator.Free( nonCacheData );
649                 nonCacheData = NULL;
650         }
651 }
652
653 /*
654 ===================
655 idSoundSample::Reload
656 ===================
657 */
658 void idSoundSample::Reload( bool force ) {
659         if ( !force ) {
660                 ID_TIME_T newTimestamp;
661
662                 // check the timestamp
663                 newTimestamp = GetNewTimeStamp();
664
665                 if ( newTimestamp == FILE_NOT_FOUND_TIMESTAMP ) {
666                         if ( !defaultSound ) {
667                                 common->Warning( "Couldn't load sound '%s' using default", name.c_str() );
668                                 MakeDefault();
669                         }
670                         return;
671                 }
672                 if ( newTimestamp == timestamp ) {
673                         return; // don't need to reload it
674                 }
675         }
676
677         common->Printf( "reloading %s\n", name.c_str() );
678         PurgeSoundSample();
679         Load();
680 }
681
682 /*
683 ===================
684 idSoundSample::FetchFromCache
685
686 Returns true on success.
687 ===================
688 */
689 bool idSoundSample::FetchFromCache( int offset, const byte **output, int *position, int *size, const bool allowIO ) {
690         offset &= 0xfffffffe;
691
692         if ( objectSize == 0 || offset < 0 || offset > objectSize * (int)sizeof( short ) || !nonCacheData ) {
693                 return false;
694         }
695
696         if ( output ) {
697                 *output = nonCacheData + offset;
698         }
699         if ( position ) {
700                 *position = 0;
701         }
702         if ( size ) {
703                 *size = objectSize * sizeof( short ) - offset;
704                 if ( *size > SCACHE_SIZE ) {
705                         *size = SCACHE_SIZE;
706                 }
707         }
708         return true;
709 }