convert line ends
[canaan.git] / prj / tech / libsrc / sound / dsample.cpp
blob2d04a39c10941190f6563d2f92d9efd97e07db00
1 ////////////////////////////////////////////////////////////////////////
2 // $Header: x:/prj/tech/libsrc/sound/RCS/dsample.cpp 1.1 1998/03/20 11:45:53 PATMAC Exp $
3 //
4 // (c) 1998 Looking Glass Technologies Inc.
5 // Pat McElhatton
6 //
7 // Module name: Sample
8 // File name: sample.cpp
9 //
10 // Description: Basic digital sound playback thingie
12 ////////////////////////////////////////////////////////////////////////
14 #include <windows.h>
15 #include <lg.h>
16 #include <assert.h>
18 #include <dlgsndi.h>
19 #include <mprintf.h>
21 #include <mixerlck.h>
23 #include <matrix.h>
25 // this is used as a sample ID in logging
26 #define SAMPLE_ID ((mGroup << 16) + mSerialNum)
28 // this is the sample name used in logging
29 #define SAMPLE_NAME ((long) mpName)
31 // state flag convenience macros
32 #define IS_MUTED (mStateFlags & kSndFlagMuted)
33 #define IS_PAUSED (mStateFlags & kSndFlagPaused)
34 #define IS_STREAM (mStateFlags & kSndFlagStream)
35 #define IS_LOOP (mStateFlags & kSndFlagLooped)
36 #define IS_AUDIBLE (mStateFlags & kSndFlagAudible)
37 #define IS_RUNNING (!IS_PAUSED && (mStateFlags == kSndStatePlaying))
39 // estimated sample position
40 #define ESTIMATED_POSITION( when ) \
41 ( (((float) mFrequency * ((when) - mBaseTime)) / 1000.0 ) + mBasePos )
43 #define ESTIMATED_POSITION_NOW ESTIMATED_POSITION( timeGetTime() )
45 // allow only one thread in critical sections
46 #define MIXER_MUTEX \
47 cMixerAutoLock __mixer_lock__( mpMixer->MutexRef() )
49 ////////////////////////////////////
50 // IMPLEMENT_UNAGGREGATABLE
52 // This does all the implementations of IUnknown methods
55 IMPLEMENT_UNAGGREGATABLE_SELF_DELETE(cDSndSample, ISndSample);
57 ///////////////////////////////////
58 // cDSndSample constructor
60 // This just sets the state to something safe.
61 // Init will tell us if everything is ok.
64 cDSndSample::cDSndSample( cDSndMixer *pMixer,
65 eSndSampleCreateFlagSet flags )
66 : cSndSample( (cSndMixer *) pMixer, flags )
68 // DirectSound stuff
69 mpSampleImp = NULL;
70 mp3DBuffer = NULL;
71 // End DirectSound stuff
72 m3DMethod = kSnd3DMethodNone;
76 ///////////////////////////////////
77 // cDSndSample destructor
79 // make sure that the implementor is gone
82 cDSndSample::~cDSndSample()
84 MIXER_MUTEX;
86 if ( mpSampleImp != NULL ) {
87 mpMixer->DoTrace( (void *) mBufferLen, kSndBufferFree );
88 mpSampleImp->Release();
89 mpSampleImp = NULL;
90 // tell the mixer we're gone & a channel is free
91 mpMixer->FreeChannel();
93 if ( mp3DBuffer != NULL ) {
94 mp3DBuffer->Release();
95 mp3DBuffer = NULL;
99 ///////////////////////////////////
101 // MakeAudible - allocate and init a direct sound buffer
102 // Return TRUE if it succeeded
104 BOOL
105 cDSndSample::MakeAudible()
107 DSBUFFERDESC db;
108 WAVEFORMATEX fmt;
109 IDirectSoundBuffer *pDSB;
110 HRESULT res;
111 MIXER_MUTEX;
112 cDSndMixer *pMixer = (cDSndMixer *) mpMixer;
114 TLOG3( "Smp::MakeAudible %s %ld audible %ld inaudible",
115 SAMPLE_NAME, mNumAudible, mNumInaudible );
117 if ( IS_AUDIBLE ) {
118 return TRUE;
121 // allocate a DirectSoundBuffer to this sample
122 if ( mpMixer->AllocChannel( this ) == FALSE ) {
123 // couldn't allocate a channel!
124 return FALSE;
127 // init the DirectSound buffer descriptor
128 memset( &db, 0, sizeof(db) ); // zero all unused/reserved fields
129 db.dwBufferBytes = mBufferLen;
130 db.dwSize = sizeof(db);
131 if ( (m3DMethod == kSnd3DMethodSoftware) || (m3DMethod == kSnd3DMethodHardware) ) {
132 // a 3D sample with volume & frequency control (no pan!)
133 db.dwFlags = DSBCAPS_CTRL3D | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY;
134 if ( m3DMethod == kSnd3DMethodHardware ) {
135 db.dwFlags |= DSBCAPS_LOCHARDWARE;
137 } else {
138 // a non-3D sample with volume, frequency & pan control
139 db.dwFlags = DSBCAPS_CTRLDEFAULT;
141 db.lpwfxFormat = &fmt;
143 // init the wave format info
144 memset( &fmt, 0, sizeof(fmt) ); // zero all unused/reserved fields
145 fmt.wFormatTag = WAVE_FORMAT_PCM;
146 fmt.nChannels = (WORD) mAttribs.nChannels;
147 fmt.nSamplesPerSec = mAttribs.sampleRate;
148 fmt.wBitsPerSample = (WORD) mAttribs.bitsPerSample;
149 // the following assumes an integral bytes/sample (PCM, not ADPCM)
150 fmt.nBlockAlign = (WORD) ((mAttribs.nChannels
151 * mAttribs.bitsPerSample) / 8);
152 fmt.nAvgBytesPerSec = mAttribs.sampleRate * fmt.nBlockAlign;
154 // create the DirectSoundBuffer
155 res = pMixer->GetDevice()->CreateSoundBuffer( &db, &pDSB, NULL);
156 if ( res == DS_OK ) {
157 mpMixer->DoTrace( (void *) mBufferLen, kSndBufferAllocate );
158 mpSampleImp = pDSB;
159 MoveToList( mpMixer->AudibleHead() );
160 SetFlags( kSndFlagAudible );
162 // load shadowed state into hardware
163 mGroupVolume = mpMixer->GetGroupVolume( mGroup ) + mpMixer->GetMasterVolume();
164 switch ( m3DMethod ) {
165 case kSnd3DMethodPanVol:
166 SetPanVol3D();
167 // tell mixer that pan/volume is in use
168 ((cDSndMixer *) mpMixer)->UsePanVol();
169 break;
171 case kSnd3DMethodSoftware:
172 case kSnd3DMethodHardware:
173 // request DirectSound 3D support
174 res = mpSampleImp->QueryInterface( IID_IDirectSound3DBuffer,
175 (void **) &mp3DBuffer );
176 //TBD: check res
177 if ( res != DS_OK ) {
178 // note that this masks kSndOnly8Bit
179 return kSnd3DInitFailure;
181 Set3DPosition( &m3DPosition );
182 Set3DVelocity( &m3DVelocity );
183 Set3DConeAngles( m3DConeInnerAngle, m3DConeOuterAngle );
184 Set3DConeOrientation( &m3DConeOrientation );
185 Set3DDistanceRange( m3DMinDistance, m3DMaxDistance );
186 Set3DMode( m3DMode );
187 SetAmbientVolume( mAmbientVolume );
188 // fall thru to set regular pan & volume below
190 case kSnd3DMethodNone:
191 SetVolume( mVolume );
192 mpSampleImp->SetPan( mPan );
193 break;
195 mpSampleImp->SetFrequency( mFrequency );
197 } else {
198 DSOUND_ERROR( res, "MakeAudible / CreateSoundBuffer" );
199 return FALSE;
202 mLastWrite = 0;
204 return TRUE;
208 void
209 cDSndSample::LLStop( void )
211 HRESULT res;
213 assert(mpSampleImp != NULL);
214 res = mpSampleImp->Stop();
215 DSOUND_ERROR_CHECK( res, "LLStop" );
219 void
220 cDSndSample::LLRelease( void )
222 mpSampleImp->Release();
223 mpSampleImp = NULL;
224 if ( mp3DBuffer != NULL ) {
225 mp3DBuffer->Release();
226 mp3DBuffer = NULL;
230 void
231 cDSndSample::LLInit( void )
236 HRESULT
237 cDSndSample::LLStart( void )
239 HRESULT res;
240 int flags;
241 uint32 avail;
243 if ( mStateFlags & (kSndFlagStream | kSndFlagLooped) ) {
244 // resume in loop mode
245 flags = DSBPLAY_LOOPING;
246 } else {
247 // resume one-shot
248 flags = 0;
251 // the stream is audible, so preload it
252 if ( fnFillCB != NULL ) {
253 // if buffer has not been filled, do so now
254 avail = AvailToWrite();
255 if ( avail != 0 ) {
256 (fnFillCB)( this, mpFillCBData, avail );
257 ClearFlags( kSndFlagResyncNeeded );
260 assert(mpSampleImp != NULL);
261 if ( mInitBuffPos != -1 ) {
262 // user specified to start play at position in mInitBuffPos
263 SetPosition( mInitBuffPos );
264 mInitBuffPos = -1;
266 res = mpSampleImp->Play(0,0,flags);
267 DSOUND_ERROR_CHECK( res, "LLStart" );
269 return res;
272 void
273 cDSndSample::LLPause( void )
275 HRESULT res;
277 assert(mpSampleImp != NULL);
278 res = mpSampleImp->Stop();
279 DSOUND_ERROR_CHECK( res, "LLPause" );
282 void
283 cDSndSample::LLResume( void )
285 Start();
288 void
289 cDSndSample::LLUnMute( void )
291 uint32 playPos, avail;
293 if ( MakeAudible() ) {
294 if ( IS_STREAM ) {
295 avail = AvailToWrite();
296 (fnFillCB)( this, mpFillCBData, avail );
297 } else {
298 // for one-shots, we must fill the entire buffer, so set the player position
299 // to 0 so it will start filling there
300 playPos = mBasePos;
301 mBasePos = 0;
302 (fnFillCB)( this, mpFillCBData, mBufferLen );
303 SetPosition( playPos );
309 BOOL
310 cDSndSample::IsPlaying()
312 DWORD stat;
313 if ( mpSampleImp == NULL ) {
314 return FALSE;
315 } else {
316 mpSampleImp->GetStatus( &stat );
317 return stat & DSBSTATUS_PLAYING;
321 ////////////////////////////////////
323 // SetVolume - set the sample volume
326 void
327 cDSndSample::LLSetVolume( int32 vol )
329 HRESULT res;
331 TLOG3( "Smp::SetVolume %s %d + group %d", SAMPLE_NAME, mVolume, mGroupVolume );
332 if ( mpSampleImp != NULL ) {
334 if ( m3DMethod == kSnd3DMethodPanVol ) {
335 SetPanVol3D();
336 } else {
337 if ( vol < kSndMinVolume ) vol = kSndMinVolume;
338 if ( vol > kSndMaxVolume ) vol = kSndMaxVolume;
339 res = mpSampleImp->SetVolume(vol);
340 DSOUND_ERROR_CHECK( res, "SetVolume / SetVolume" );
348 // do fake 3d localization by setting volume & pan, based on
349 // the sample volume & 3D position
351 void
352 cDSndSample::SetPanVol3D( void )
354 int32 vol, pan;
355 HRESULT res;
357 if ( mpSampleImp != NULL ) {
358 mpMixer->Get3DPositionPanVolume( &m3DPosition, &pan, &vol );
359 vol += (mVolume + mGroupVolume);
360 TLOG3( "Smp::SetPanVol3D %s pan %d vol %d", SAMPLE_NAME, pan, vol );
362 if ( vol < kSndMinVolume ) vol = kSndMinVolume;
363 if ( vol > kSndMaxVolume ) vol = kSndMaxVolume;
364 res = mpSampleImp->SetVolume(vol);
365 DSOUND_ERROR_CHECK( res, "SetPanVol3D / SetVolume" );
367 if ( pan < kSndPanLeft ) {
368 pan = kSndPanLeft;
369 } else if ( pan > kSndPanRight ) {
370 pan = kSndPanRight;
372 res = mpSampleImp->SetPan( pan );
373 DSOUND_ERROR_CHECK( res, "SetPanVol3D / SetPan" );
379 ////////////////////////////////////
381 // SetPan - Set the pan position of the sample -10000 (left) ... 10000 (right)
384 STDMETHODIMP_(void) cDSndSample::SetPan(int32 pan)
386 HRESULT res;
388 mPan = pan;
390 TLOG2( "Smp::SetPan %s %d", SAMPLE_NAME, mPan );
391 if ( mpSampleImp != NULL ) {
392 if ( pan < kSndPanLeft ) {
393 pan = kSndPanLeft;
394 } else if ( pan > kSndPanRight ) {
395 pan = kSndPanRight;
397 res = mpSampleImp->SetPan( pan );
398 DSOUND_ERROR_CHECK( res, "SetPan / SetPan" );
402 ////////////////////////////////////
404 // SetFrequency - Set the frequency of the sample
407 STDMETHODIMP_(void) cDSndSample::SetFrequency(uint32 freq)
409 DWORD now;
410 HRESULT res;
412 TLOG2( "Smp::SetFrequency %s %d", SAMPLE_NAME, freq );
413 // clamp frequency to dsound supported range
414 if ( freq < kSndMinFrequency ) {
415 freq = kSndMinFrequency;
416 } else if ( freq > kSndMaxFrequency ) {
417 freq = kSndMaxFrequency;
419 if ( !IS_AUDIBLE ) {
420 if ( IS_RUNNING ) {
421 // muted & running - just recalculate current
422 // position before changing frequency
423 now = timeGetTime();
424 mBasePos = ESTIMATED_POSITION( now );
425 mBaseTime = now;
426 TLOG2( "Smp::SetFrequency - reset baseTime %ld basePos %ld", mBaseTime, mBasePos );
428 } else {
429 // pass frequency down to dsound
430 assert(mpSampleImp != NULL);
431 res = mpSampleImp->SetFrequency(freq);
432 DSOUND_ERROR_CHECK( res, "SetFrequency / SetFrequency" );
434 mFrequency = freq;
438 void
439 cDSndSample::LLSetPosition( uint32 pos )
441 HRESULT res;
443 // set the hardware play position
444 res = mpSampleImp->SetCurrentPosition( pos * mBytesPerSample );
445 DSOUND_ERROR_CHECK( res, "SetPosition / SetCurrentPosition" );
449 uint32
450 cDSndSample::LLGetPosition( void )
452 uint32 writePos, playPos;
453 HRESULT res;
455 // get position in DirectSoundBuffer
456 assert(mpSampleImp != NULL);
457 res = mpSampleImp->GetCurrentPosition( &playPos, &writePos );
458 DSOUND_ERROR_CHECK( res, "GetPosition / GetCurrentPosition" );
459 TLOG3( "Smp::GetPosition %s hwpos = %d baseoff %d", SAMPLE_NAME, playPos, mBaseOffset );
461 // for streams, handle fact that play is looping on buffer
462 if ( playPos > mLastWrite ) {
463 // play position after write position means that play
464 // position base is one buffer length behind write
465 // position base
466 playPos += (mBaseOffset - mBufferLen);
467 } else {
468 playPos += mBaseOffset;
471 return playPos / mBytesPerSample;
475 ////////////////////////////////////
476 // LoadBufferIndirect
478 // Attempt to lock the buffer and call app-supplied function to copy memory
479 // into it. Give the data and the length to be copied. This should be used
480 // for streaming samples
482 STDMETHODIMP_(eSndError)
483 cDSndSample::LoadBufferIndirect( SndLoadFunction funk,
484 void *pFunkData,
485 uint32 len )
487 assert(mpSampleImp != NULL);
488 TLOG3( "Smp::LoadBufferIndirect %s, %d byte pos %d", SAMPLE_NAME, len, mLastWrite + mBaseOffset );
490 void *p1, *p2, *p11, *p22;
491 uint32 sz1, sz2;
493 if ( IS_STREAM && (len == 0) ) {
495 // Our caller has passed len 0, indicating that it is done passing data
496 // to this stream, so start the shutdown of the stream
498 if ( (mState == kSndStatePlaying) || (mState == kSndStateInited) ) {
499 // remember the last write point, needed for detecting when
500 // playback really completes. We can't use mLastWrite for this,
501 // since it will be changed by silence filling
502 mFinalWrite = mLastWrite;
503 // silence-fill part of buffer holding data which has already been played
504 uint32 avail = AvailToWrite();
505 TLOG3("Smp:LoadBufferIndirect avail %ld, mLastWrite %ld, mLastRead %ld",
506 avail, mLastWrite, mLastRead );
507 SilenceFill( avail );
508 //TBD silence fill (do we need to worry about completely full buffer case?)
510 SetFlags( kSndFlagEndOfData );
511 return kSndOk;
514 if ( LockBuffer( &p1, &sz1, &p2, &sz2, len) ) {
515 p11 = funk( pFunkData, p1, sz1 );
516 if ( p11 != p1 ) {
517 // getdata function put data somewhere other than p1 - move data
518 memcpy( p1, p11, sz1 );
521 TLOG2( " section one %d bytes @ 0x%lx", sz1, (long) p1 );
522 if ( sz2 ) {
523 p22 = funk( pFunkData, p2, sz2 );
524 if ( p22 != p2 ) {
525 // getdata function put data somewhere other than p2 - move data
526 memcpy( p2, p22, sz2 );
528 mpWriteBase = (char *) p2;
529 TLOG2( " section two %d bytes @ 0x%lx", sz2, (long) p2 );
532 if ( mDumpFile != NULL ) {
533 // dump the data
534 fwrite( p1, 1, sz1, mDumpFile );
535 if ( sz2 != 0 ) {
536 fwrite( p2, 1, sz2, mDumpFile );
539 UnlockBuffer(p1, sz1, p2, sz2);
541 mLastWrite += len;
543 if ( IS_STREAM && (mLastWrite >= mBufferLen) ) {
544 mLastWrite -= mBufferLen;
545 mBaseOffset += mBufferLen;
548 if ( (sz1 + sz2) != len ) {
549 // DEBUG! spot for a breakpoint...
550 sz1 = sz2;
553 return kSndOk;
556 // DEBUG! spot for a breakpoint...
557 TBD( "what if LockBuffer fails?" );
558 return kSndUnknownError;
560 ////////////////////////////////////
561 // BufferReady
563 // Takes: length of buffer
565 // Returns TRUE if buffer ready
569 STDMETHODIMP_(BOOL)
570 cDSndSample::BufferReady( uint32 len )
572 // we're always ready for nothing
573 if ( len == 0 ) {
574 return TRUE;
577 if ( mpSampleImp == NULL ) {
578 return FALSE;
581 // now see if we're reading where we want to be writing...
582 return ( len > AvailToWrite() ) ? FALSE : TRUE;
585 ////////////////////////////////////
586 // LockBuffer
588 // Internally used routine to try to lock the buffer
590 BOOL cDSndSample::LockBuffer(void **p1, uint32 *sz1, void **p2, uint32 *sz2, uint32 len)
592 HRESULT res;
593 uint32 playPos, writePos, availWrite;
595 assert(mpSampleImp != NULL);
597 res = mpSampleImp->GetCurrentPosition( &playPos, &writePos );
598 DSOUND_ERROR_CHECK( res, "LockBuffer / GetCurrentPosition" );
599 TLOG3( "Smp::LockBuffer writePos %d, playPos %d, %d bytes", mLastWrite, playPos, len );
601 // see if there is enough room in buffer for data
602 availWrite = AvailToWrite( playPos );
603 if ( len > availWrite ) {
604 TLOG3( "!!Smp::LockBuffer %s avail to write %d, %d bytes!!", SAMPLE_NAME, availWrite, len );
605 return FALSE;
608 // try to lock the requested range
609 res = mpSampleImp->Lock(mLastWrite, len, p1, sz1, p2, sz2, 0);
610 DSOUND_ERROR_CHECK( res, "LockBuffer / Lock 2nd try" );
612 // this wont happen too often. I think it happens when
613 // we've been suspended or if the hardware is physically removed
614 if( res == DSERR_BUFFERLOST ) {
615 TLOG1( "!!Smp::LockBuffer %s lost DSound buffer, attempting restore", SAMPLE_NAME );
616 // try once to restore
617 mpSampleImp->Restore();
619 // do the lock from the last known write position
620 res = mpSampleImp->Lock(mLastWrite, len, p1, sz1, p2, sz2, 0);
621 DSOUND_ERROR_CHECK( res, "LockBuffer / Lock 2nd try" );
623 return (SUCCEEDED(res));
628 void
629 cDSndSample::UnlockBuffer(void *p1, uint32 sz1, void *p2, uint32 sz2)
631 mpSampleImp->Unlock(p1, sz1, p2, sz2);
635 ////////////////////////////////////
637 // AvailToWrite - return the number of bytes that can be written
638 // to a stream sample's buffer (internal version)
641 uint32
642 cDSndSample::AvailToWrite( uint32 readPos )
644 uint32 avail;
645 if ( IS_STREAM ) {
646 if ( mLastWrite < readPos ) {
647 // readPos is ahead of last write position in buffer, valid data
648 // wraps around end of buffer. Free space starts at mLastWrite
649 // and ends at readPos
650 avail = readPos - mLastWrite;
651 } else {
652 // readPos is behind last write position in buffer, valid data does not
653 // wrap arounc end of buffer. Free space starts at mLastWrite, wraps
654 // around buffer end, and ends at readPos
655 avail = mBufferLen - (mLastWrite - readPos);
658 // don't fill the last few bytes of buffer - full buffer is indistinguishable
659 // from empty buffer
660 if ( avail > 4 ) {
661 avail -= 4;
662 } else {
663 avail = 0;
665 } else {
666 // for one-shots, no need to worry about buffer wraparound
667 avail = mBufferLen - mLastWrite;
670 TLOG3( "Smp::AvailToWrite %s %ld bytes pos %ld", SAMPLE_NAME, avail, readPos );
671 return avail;
676 // AvailToWrite - return the number of bytes that can be written
677 // to a stream sample's buffer
679 STDMETHODIMP_(uint32) cDSndSample::AvailToWrite( void )
681 uint32 playPos, writePos;
682 HRESULT res;
684 if ( !IS_AUDIBLE ) {
685 // don't have a DirectSound buffer - can't load any data
686 return 0;
689 res = mpSampleImp->GetCurrentPosition( &playPos, &writePos );
690 DSOUND_ERROR_CHECK( res, "AvailToWrite / GetCurrentPosition" );
692 return AvailToWrite( playPos );
697 // CheckStream - call stream fill callback, stop if needed
699 STDMETHODIMP_(void)
700 cDSndSample::CheckStream()
702 uint32 prevReadPos = mLastRead;
703 uint32 writePos;
704 uint32 readPos;
705 BOOL doStop;
706 HRESULT res;
708 if ( IS_MUTED ) {
709 // handle muted stream - look for end-of-data condition
710 if ( IS_RUNNING ) {
711 TLOG2( "Smp::CheckStream %s MUTED, readPos %d", SAMPLE_NAME, readPos );
712 readPos = ESTIMATED_POSITION_NOW;
713 if ( readPos >= mNumSamples ) {
714 // stream sample has reached its virtual end-of-data
715 DeferredStop();
718 return;
721 assert(mpSampleImp != NULL);
723 res = mpSampleImp->GetCurrentPosition( &mLastRead, &writePos );
724 DSOUND_ERROR_CHECK( res, "CheckStream / GetCurrentPosition" );
725 uint32 avail = AvailToWrite( mLastRead );
726 TLOG3( "Smp::CheckStream %s, availToWrite %d, readPos %d", SAMPLE_NAME, avail, mLastRead );
728 switch( mState ) {
729 case kSndStatePlaying:
730 case kSndStateInited:
731 if ( mStateFlags & kSndFlagEndOfData ) {
732 // we are waiting for data already in buffer to be played
733 // before we can release sample
734 doStop = FALSE;
735 if ( prevReadPos < mFinalWrite ) {
736 // available play data does not wrap around
737 if ( (mLastRead > mFinalWrite) || (mLastRead < prevReadPos) ) {
738 // ring buffer is empty
739 TLOG3("Smp::CheckStream %s stopping no-wrap lastWrite %d, prevRead %d\n",
740 SAMPLE_NAME, mFinalWrite, prevReadPos );
741 doStop = TRUE;
743 } else {
744 // available play data does wrap around
745 if ( (mLastRead > mFinalWrite) && (mLastRead < prevReadPos) ) {
746 // ring buffer is empty
747 TLOG3("Smp::CheckStream %s stopping wrap lastWrite %d, prevRead %d\n",
748 SAMPLE_NAME, mFinalWrite, prevReadPos );
749 doStop = TRUE;
752 if ( doStop ) {
753 DeferredStop();
754 } else {
755 // fill region which has been played since previous call with silence
756 SilenceFill( avail );
759 } else {
761 // still really playing - let's move some data!
762 if ( (fnFillCB != NULL) && (avail != 0) ) {
763 // let app add data
764 (fnFillCB)( this, mpFillCBData, avail );
767 break;
769 case kSndStateCreated:
770 case kSndStateDestroyed:
771 case kSndStateStopped:
772 default:
773 break;
778 // SilenceFill - fill nBytes with silence (zeroes)
779 // advances mLastWrite
781 STDMETHODIMP_(void) cDSndSample::SilenceFill( uint32 nBytes )
783 void *p1, *p2;
784 uint32 len1, len2;
785 uint8 silenceValue;
787 assert(mpSampleImp != NULL);
788 TLOG3( "Smp::SilenceFill %s, %ld bytes, writePos %ld", SAMPLE_NAME, nBytes, mLastWrite );
790 if ( nBytes == 0 ) {
791 return;
794 // 8-bit samples are unsigned - this will break if we ever have signed 8-bit samples...
795 silenceValue = ( mAttribs.bitsPerSample == 8 ) ? 0x80 : 0;
797 if ( LockBuffer( &p1, &len1, &p2, &len2, nBytes ) ) {
798 memset( p1, silenceValue, len1 );
799 if ( len2 != 0 ) {
800 memset( p2, silenceValue, len2 );
802 if ( mDumpFile != NULL ) {
803 // dump the data
804 fwrite( p1, 1, len1, mDumpFile );
805 if ( len2 != 0 ) {
806 fwrite( p2, 1, len2, mDumpFile );
809 UnlockBuffer( p1, len1, p2, len2 );
810 mLastWrite += nBytes;
811 if ( mLastWrite >= mBufferLen ) mLastWrite -= mBufferLen;
812 } else {
813 TBD( "what if LockBuffer fails? (silence fill)" );
818 /************************************************************
820 * 3 D S T U F F
825 // NOTE: DS3D's coordinate system have Y as up, and X & Z form the ground plane
826 // while LG's coords have Z as up, and X & Y form the ground plane:
828 // DS LG
830 // Y Z
831 // ^ Z ^ X
832 // | / | /
833 // | / | /
834 // |/ |/
835 // |------>X Y<----|
837 // X -Y
838 // Y Z
839 // Z X
842 ////////////////////////////////////
844 // Set3DPosition
847 STDMETHODIMP_(void)
848 cDSndSample::Set3DPosition( sSndVector *pPosition )
850 HRESULT res;
852 assert( pPosition != NULL );
853 TLOG3( "Smp::Set3DPosition %s %d %d",
854 SAMPLE_NAME, pPosition->x, pPosition->y );
855 m3DPosition = *pPosition;
856 switch( m3DMethod ) {
858 case kSnd3DMethodPanVol:
859 SetPanVol3D();
860 break;
862 case kSnd3DMethodNone:
863 break;
865 default:
866 if ( mp3DBuffer != NULL ) {
867 res = mp3DBuffer->SetPosition( -(pPosition->y), pPosition->z, pPosition->x,
868 mpMixer->Get3DDeferFlag() );
869 DSOUND_ERROR_CHECK( res, "Smp:Set3DPosition failed" );
871 break;
876 ////////////////////////////////////
878 // Set3DVelocity
881 STDMETHODIMP_(void)
882 cDSndSample::Set3DVelocity( sSndVector *pVelocity )
884 HRESULT res;
886 assert( pVelocity != NULL );
887 TLOG3( "Smp::Set3DVelocity %d %d %d",
888 pVelocity->x, pVelocity->y, pVelocity->z );
890 if ( mp3DBuffer != NULL ) {
891 res = mp3DBuffer->SetVelocity( -(pVelocity->y), pVelocity->z, pVelocity->x,
892 mpMixer->Get3DDeferFlag() );
893 DSOUND_ERROR_CHECK( res, "Smp:Set3DVelocity failed" );
895 m3DVelocity = *pVelocity;
899 ////////////////////////////////////
901 // Set3DConeAngles
904 STDMETHODIMP_(void)
905 cDSndSample::Set3DConeAngles( uint32 inside, uint32 outside )
907 HRESULT res;
909 TLOG3( "Smp::Set3DConeAngles %s inside %d outside %d", SAMPLE_NAME, inside, outside );
910 if ( mp3DBuffer != NULL ) {
911 res = mp3DBuffer->SetConeAngles( inside, outside,
912 mpMixer->Get3DDeferFlag() );
913 DSOUND_ERROR_CHECK( res, "Smp:Set3DConeAngles failed" );
915 m3DConeInnerAngle = inside;
916 m3DConeOuterAngle = outside;
920 ////////////////////////////////////
922 // Set3DOrientation
925 STDMETHODIMP_(void)
926 cDSndSample::Set3DConeOrientation( sSndVector *pOrientation )
928 HRESULT res;
930 assert( pOrientation != NULL );
931 TLOG3( "Smp::Set3DConeOrientation x100 %d %d %d",
932 pOrientation->x * 100, pOrientation->y * 100, pOrientation->z * 100 );
933 if ( mp3DBuffer != NULL ) {
934 res = mp3DBuffer->SetConeOrientation( -(pOrientation->y), pOrientation->z, pOrientation->x,
935 mpMixer->Get3DDeferFlag() );
936 DSOUND_ERROR_CHECK( res, "Smp:Set3DConeOrientation failed" );
938 m3DConeOrientation = *pOrientation;
942 ////////////////////////////////////
944 // Set3DDistanceRange
947 STDMETHODIMP_(void)
948 cDSndSample::Set3DDistanceRange( float dmin, float dmax )
950 HRESULT res;
952 TLOG3( "Smp::Set3DDistanceRange %s min %d max %d", SAMPLE_NAME, dmin, dmax );
953 if ( mp3DBuffer != NULL ) {
954 res = mp3DBuffer->SetMinDistance( dmin, DS3D_DEFERRED );
955 DSOUND_ERROR_CHECK( res, "Smp:Set3DDistanceRange Min failed" );
956 res = mp3DBuffer->SetMaxDistance( dmax, mpMixer->Get3DDeferFlag() );
957 DSOUND_ERROR_CHECK( res, "Smp:Set3DDistanceRange Max failed" );
959 m3DMinDistance = dmin;
960 m3DMaxDistance = dmax;
964 ////////////////////////////////////
966 // Set3DMode
969 STDMETHODIMP_(void)
970 cDSndSample::Set3DMode( eSnd3DMode dmode )
972 uint32 mode;
973 HRESULT res;
975 TLOG2( "Smp::Set3DMode %s %d", SAMPLE_NAME, dmode );
976 switch( dmode ) {
977 default:
978 case kSnd3DModeNone: mode = DS3DMODE_DISABLE; break;
979 case kSnd3DModeNormal: mode = DS3DMODE_NORMAL; break;
980 case kSnd3DModeHeadRelative: mode = DS3DMODE_HEADRELATIVE; break;
983 if ( mp3DBuffer != NULL ) {
984 res = mp3DBuffer->SetMode( mode, mpMixer->Get3DDeferFlag() );
985 DSOUND_ERROR_CHECK( res, "Smp:Set3DMode failed" );
988 m3DMode = dmode;
992 ////////////////////////////////////
994 // SetAmbientVolume
997 STDMETHODIMP_(void)
998 cDSndSample::SetAmbientVolume( int32 vol )
1000 HRESULT res;
1002 TLOG2( "Smp::SetAmbientVolume %s %d", SAMPLE_NAME, vol );
1003 if ( mp3DBuffer != NULL ) {
1004 res = mp3DBuffer->SetConeOutsideVolume( vol, mpMixer->Get3DDeferFlag() );
1005 DSOUND_ERROR_CHECK( res, "Smp:SetAmbientVolume failed" );
1007 mAmbientVolume = vol;
1011 ////////////////////////////////////
1013 // Set3DMethod
1016 STDMETHODIMP_(void)
1017 cDSndSample::Set3DMethod( eSnd3DMethod method )
1019 // assert if this is called after Init
1020 assert( mpSampleImp == NULL );
1021 TLOG2( "Smp::Set3DMethod %s %d", SAMPLE_NAME, method );
1023 m3DMethod = method;
1025 if ( m3DMethod == kSnd3DMethodPanVol ) {
1026 // tell mixer that pan/volume is in use
1027 ((cDSndMixer *) mpMixer)->UsePanVol();
1033 // redo pan/volume for audible samples
1034 // return FALSE if there are no samples using pan/vol for 3D
1036 BOOL
1037 cDSndSample::UpdatePanVol3D( void )
1039 cDSndSample *pSample, *pNext;
1040 BOOL usingPanVol;
1042 // This is invoked by the mixer when the listener position or orientation changes
1043 usingPanVol = FALSE;
1044 // we are the audible list head
1045 pSample = this;
1046 while ( pSample != NULL ) {
1047 pNext = (cDSndSample *) (pSample->mpNext);
1048 if ( pSample->m3DMethod == kSnd3DMethodPanVol ) {
1049 pSample->SetPanVol3D();
1050 // we found at least one audible sample using panvol, so
1051 // tell mixer to keep updating panvol when listener 3D changes
1052 usingPanVol = TRUE;
1054 pSample = pNext;
1057 return usingPanVol;