1 ////////////////////////////////////////////////////////////////////////
2 // $Header: x:/prj/tech/libsrc/sound/RCS/dsample.cpp 1.1 1998/03/20 11:45:53 PATMAC Exp $
4 // (c) 1998 Looking Glass Technologies Inc.
8 // File name: sample.cpp
10 // Description: Basic digital sound playback thingie
12 ////////////////////////////////////////////////////////////////////////
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
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
)
71 // End DirectSound stuff
72 m3DMethod
= kSnd3DMethodNone
;
76 ///////////////////////////////////
77 // cDSndSample destructor
79 // make sure that the implementor is gone
82 cDSndSample::~cDSndSample()
86 if ( mpSampleImp
!= NULL
) {
87 mpMixer
->DoTrace( (void *) mBufferLen
, kSndBufferFree
);
88 mpSampleImp
->Release();
90 // tell the mixer we're gone & a channel is free
91 mpMixer
->FreeChannel();
93 if ( mp3DBuffer
!= NULL
) {
94 mp3DBuffer
->Release();
99 ///////////////////////////////////
101 // MakeAudible - allocate and init a direct sound buffer
102 // Return TRUE if it succeeded
105 cDSndSample::MakeAudible()
109 IDirectSoundBuffer
*pDSB
;
112 cDSndMixer
*pMixer
= (cDSndMixer
*) mpMixer
;
114 TLOG3( "Smp::MakeAudible %s %ld audible %ld inaudible",
115 SAMPLE_NAME
, mNumAudible
, mNumInaudible
);
121 // allocate a DirectSoundBuffer to this sample
122 if ( mpMixer
->AllocChannel( this ) == FALSE
) {
123 // couldn't allocate a channel!
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
;
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
);
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
:
167 // tell mixer that pan/volume is in use
168 ((cDSndMixer
*) mpMixer
)->UsePanVol();
171 case kSnd3DMethodSoftware
:
172 case kSnd3DMethodHardware
:
173 // request DirectSound 3D support
174 res
= mpSampleImp
->QueryInterface( IID_IDirectSound3DBuffer
,
175 (void **) &mp3DBuffer
);
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
);
195 mpSampleImp
->SetFrequency( mFrequency
);
198 DSOUND_ERROR( res
, "MakeAudible / CreateSoundBuffer" );
209 cDSndSample::LLStop( void )
213 assert(mpSampleImp
!= NULL
);
214 res
= mpSampleImp
->Stop();
215 DSOUND_ERROR_CHECK( res
, "LLStop" );
220 cDSndSample::LLRelease( void )
222 mpSampleImp
->Release();
224 if ( mp3DBuffer
!= NULL
) {
225 mp3DBuffer
->Release();
231 cDSndSample::LLInit( void )
237 cDSndSample::LLStart( void )
243 if ( mStateFlags
& (kSndFlagStream
| kSndFlagLooped
) ) {
244 // resume in loop mode
245 flags
= DSBPLAY_LOOPING
;
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();
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
);
266 res
= mpSampleImp
->Play(0,0,flags
);
267 DSOUND_ERROR_CHECK( res
, "LLStart" );
273 cDSndSample::LLPause( void )
277 assert(mpSampleImp
!= NULL
);
278 res
= mpSampleImp
->Stop();
279 DSOUND_ERROR_CHECK( res
, "LLPause" );
283 cDSndSample::LLResume( void )
289 cDSndSample::LLUnMute( void )
291 uint32 playPos
, avail
;
293 if ( MakeAudible() ) {
295 avail
= AvailToWrite();
296 (fnFillCB
)( this, mpFillCBData
, avail
);
298 // for one-shots, we must fill the entire buffer, so set the player position
299 // to 0 so it will start filling there
302 (fnFillCB
)( this, mpFillCBData
, mBufferLen
);
303 SetPosition( playPos
);
310 cDSndSample::IsPlaying()
313 if ( mpSampleImp
== NULL
) {
316 mpSampleImp
->GetStatus( &stat
);
317 return stat
& DSBSTATUS_PLAYING
;
321 ////////////////////////////////////
323 // SetVolume - set the sample volume
327 cDSndSample::LLSetVolume( int32 vol
)
331 TLOG3( "Smp::SetVolume %s %d + group %d", SAMPLE_NAME
, mVolume
, mGroupVolume
);
332 if ( mpSampleImp
!= NULL
) {
334 if ( m3DMethod
== kSnd3DMethodPanVol
) {
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
352 cDSndSample::SetPanVol3D( void )
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
) {
369 } else if ( 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
)
390 TLOG2( "Smp::SetPan %s %d", SAMPLE_NAME
, mPan
);
391 if ( mpSampleImp
!= NULL
) {
392 if ( pan
< kSndPanLeft
) {
394 } else if ( 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
)
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
;
421 // muted & running - just recalculate current
422 // position before changing frequency
424 mBasePos
= ESTIMATED_POSITION( now
);
426 TLOG2( "Smp::SetFrequency - reset baseTime %ld basePos %ld", mBaseTime
, mBasePos
);
429 // pass frequency down to dsound
430 assert(mpSampleImp
!= NULL
);
431 res
= mpSampleImp
->SetFrequency(freq
);
432 DSOUND_ERROR_CHECK( res
, "SetFrequency / SetFrequency" );
439 cDSndSample::LLSetPosition( uint32 pos
)
443 // set the hardware play position
444 res
= mpSampleImp
->SetCurrentPosition( pos
* mBytesPerSample
);
445 DSOUND_ERROR_CHECK( res
, "SetPosition / SetCurrentPosition" );
450 cDSndSample::LLGetPosition( void )
452 uint32 writePos
, playPos
;
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
466 playPos
+= (mBaseOffset
- mBufferLen
);
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
,
487 assert(mpSampleImp
!= NULL
);
488 TLOG3( "Smp::LoadBufferIndirect %s, %d byte pos %d", SAMPLE_NAME
, len
, mLastWrite
+ mBaseOffset
);
490 void *p1
, *p2
, *p11
, *p22
;
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
);
514 if ( LockBuffer( &p1
, &sz1
, &p2
, &sz2
, len
) ) {
515 p11
= funk( pFunkData
, p1
, sz1
);
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
);
523 p22
= funk( pFunkData
, p2
, sz2
);
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
) {
534 fwrite( p1
, 1, sz1
, mDumpFile
);
536 fwrite( p2
, 1, sz2
, mDumpFile
);
539 UnlockBuffer(p1
, sz1
, p2
, sz2
);
543 if ( IS_STREAM
&& (mLastWrite
>= mBufferLen
) ) {
544 mLastWrite
-= mBufferLen
;
545 mBaseOffset
+= mBufferLen
;
548 if ( (sz1
+ sz2
) != len
) {
549 // DEBUG! spot for a breakpoint...
556 // DEBUG! spot for a breakpoint...
557 TBD( "what if LockBuffer fails?" );
558 return kSndUnknownError
;
560 ////////////////////////////////////
563 // Takes: length of buffer
565 // Returns TRUE if buffer ready
570 cDSndSample::BufferReady( uint32 len
)
572 // we're always ready for nothing
577 if ( mpSampleImp
== NULL
) {
581 // now see if we're reading where we want to be writing...
582 return ( len
> AvailToWrite() ) ? FALSE
: TRUE
;
585 ////////////////////////////////////
588 // Internally used routine to try to lock the buffer
590 BOOL
cDSndSample::LockBuffer(void **p1
, uint32
*sz1
, void **p2
, uint32
*sz2
, uint32 len
)
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
);
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
));
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)
642 cDSndSample::AvailToWrite( uint32 readPos
)
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
;
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
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
);
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
;
685 // don't have a DirectSound buffer - can't load any data
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
700 cDSndSample::CheckStream()
702 uint32 prevReadPos
= mLastRead
;
709 // handle muted stream - look for end-of-data condition
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
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
);
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
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
);
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
);
755 // fill region which has been played since previous call with silence
756 SilenceFill( avail
);
761 // still really playing - let's move some data!
762 if ( (fnFillCB
!= NULL
) && (avail
!= 0) ) {
764 (fnFillCB
)( this, mpFillCBData
, avail
);
769 case kSndStateCreated
:
770 case kSndStateDestroyed
:
771 case kSndStateStopped
:
778 // SilenceFill - fill nBytes with silence (zeroes)
779 // advances mLastWrite
781 STDMETHODIMP_(void) cDSndSample::SilenceFill( uint32 nBytes
)
787 assert(mpSampleImp
!= NULL
);
788 TLOG3( "Smp::SilenceFill %s, %ld bytes, writePos %ld", SAMPLE_NAME
, nBytes
, mLastWrite
);
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
);
800 memset( p2
, silenceValue
, len2
);
802 if ( mDumpFile
!= NULL
) {
804 fwrite( p1
, 1, len1
, mDumpFile
);
806 fwrite( p2
, 1, len2
, mDumpFile
);
809 UnlockBuffer( p1
, len1
, p2
, len2
);
810 mLastWrite
+= nBytes
;
811 if ( mLastWrite
>= mBufferLen
) mLastWrite
-= mBufferLen
;
813 TBD( "what if LockBuffer fails? (silence fill)" );
818 /************************************************************
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:
842 ////////////////////////////////////
848 cDSndSample::Set3DPosition( sSndVector
*pPosition
)
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
:
862 case kSnd3DMethodNone
:
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" );
876 ////////////////////////////////////
882 cDSndSample::Set3DVelocity( sSndVector
*pVelocity
)
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 ////////////////////////////////////
905 cDSndSample::Set3DConeAngles( uint32 inside
, uint32 outside
)
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 ////////////////////////////////////
926 cDSndSample::Set3DConeOrientation( sSndVector
*pOrientation
)
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
948 cDSndSample::Set3DDistanceRange( float dmin
, float dmax
)
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 ////////////////////////////////////
970 cDSndSample::Set3DMode( eSnd3DMode dmode
)
975 TLOG2( "Smp::Set3DMode %s %d", SAMPLE_NAME
, dmode
);
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" );
992 ////////////////////////////////////
998 cDSndSample::SetAmbientVolume( int32 vol
)
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 ////////////////////////////////////
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
);
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
1037 cDSndSample::UpdatePanVol3D( void )
1039 cDSndSample
*pSample
, *pNext
;
1042 // This is invoked by the mixer when the listener position or orientation changes
1043 usingPanVol
= FALSE
;
1044 // we are the audible list head
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