1 ////////////////////////////////////////////////////////////////////////
2 // $Header: x:/prj/tech/libsrc/sound/RCS/qsample.cpp 1.1 1998/03/20 12:54:01 PATMAC Exp $
4 // (c) 1998 Looking Glass Technologies Inc.
8 // File name: sample.cpp
10 // Description: Basic digital sound playback thingie
12 ////////////////////////////////////////////////////////////////////////
23 // this is used as a sample ID in logging
24 #define SAMPLE_ID ((mGroup << 16) + mSerialNum)
26 // this is the sample name used in logging
27 #define SAMPLE_NAME ((long) mpName)
29 // state flag convenience macros
30 #define IS_MUTED (mStateFlags & kSndFlagMuted)
31 #define IS_PAUSED (mStateFlags & kSndFlagPaused)
32 #define IS_STREAM (mStateFlags & kSndFlagStream)
33 #define IS_LOOP (mStateFlags & kSndFlagLooped)
34 #define IS_AUDIBLE (mStateFlags & kSndFlagAudible)
35 #define IS_RUNNING (!IS_PAUSED && (mStateFlags == kSndStatePlaying))
37 // estimated sample position
38 #define ESTIMATED_POSITION( when ) \
39 ( (((float) mFrequency * ((when) - mBaseTime)) / 1000.0 ) + mBasePos )
41 #define ESTIMATED_POSITION_NOW ESTIMATED_POSITION( timeGetTime() )
43 // allow only one thread in critical sections
45 cMixerAutoLock __mixer_lock__( mpMixer->MutexRef() )
47 // internal set volume & pan routines
48 UINT
InternalSetVolume( HQMIXER pMixer
, int chan
, int32 mBVolume
);
49 UINT
InternalSetPan( HQMIXER pMixer
, int chan
, int32 pan
);
53 #define QSOUND_ERROR_CHECK( res, str ) if ( res ) DisplayQSError( res, str )
54 #define QSOUND_ERROR( str ) DisplayQSError( 0, str )
57 DisplayQSError( UINT res
,
63 QSWaveMixGetErrorText( res
, daText
, sizeof(daText
) );
64 mprintf( "Sample error from %s: %s\n", pStr
, daText
);
66 mprintf( "Sample error from %s\n", pStr
);
72 #define QSOUND_ERROR_CHECK( res, str )
73 #define QSOUND_ERROR( str )
77 ////////////////////////////////////
78 // IMPLEMENT_UNAGGREGATABLE
80 // This does all the implementations of IUnknown methods
83 IMPLEMENT_UNAGGREGATABLE_SELF_DELETE(cQSndSample
, ISndSample
);
85 ///////////////////////////////////
86 // cQSndSample constructor
88 // This just sets the state to something safe.
89 // Init will tell us if everything is ok.
92 cQSndSample::cQSndSample( cQSndMixer
*pMixer
,
93 eSndSampleCreateFlagSet flags
)
94 : cSndSample( (cSndMixer
*) pMixer
, flags
)
100 mpMixDevice
= pMixer
->GetQMixer();
102 mAmbientVolumeDB
= 0.0;
108 ///////////////////////////////////
109 // cQSndSample destructor
111 // make sure that the implementor is gone
115 cQSndSample::~cQSndSample( void )
120 if ( mChannel
>= 0 ) {
121 mpMixer
->DoTrace( (void *) mBufferLen
, kSndBufferFree
);
122 QSWaveMixCloseChannel( mpMixDevice
, mChannel
, 0 );
123 // tell the mixer we're gone & a channel is free
124 mpMixer
->FreeChannel();
126 if ( mpWave
!= NULL
) {
127 res
= QSWaveMixFreeWave( mpMixDevice
, mpWave
);
128 QSOUND_ERROR_CHECK( res
, "~Sample / FreeWave" );
129 // TBD: print error message if mpWave == NULL?
134 ///////////////////////////////////
136 // This stump is called from the QMixer and passes the call along to the
137 // app supplied refill callback
140 QStreamRefillCB( void *pBuffer
,
144 cQSndSample
*pSample
= (cQSndSample
*) pUserCBData
;
146 return pSample
->InvokeRefillCB( pBuffer
, nBytes
);
150 ///////////////////////////////////
152 // InvokeRefillCB - invoke user stream refill callback, called
153 // from QMixer refill callback. Save away the data destination
154 // ptr for LoadBufferIndirect to use later
156 // Return TRUE if stream is still passing data
159 cQSndSample::InvokeRefillCB( void *pBuffer
,
162 mpRefillDest
= pBuffer
;
164 assert( fnFillCB
!= NULL
);
166 (fnFillCB
) ( this, mpFillCBData
, nBytes
);
168 return ( (GetFlags() & kSndFlagEndOfData
) == 0 );
172 ///////////////////////////////////
174 // MakeAudible - allocate and init a low-level channel
175 // Return TRUE if it succeeded
178 cQSndSample::MakeAudible()
180 WAVEFORMATEX audioFormat
;
181 QMIXWAVEPARAMS waveInfo
;
186 TLOG3( "Smp::MakeAudible %s %ld audible %ld inaudible",
187 SAMPLE_NAME
, mNumAudible
, mNumInaudible
);
193 ///////////////////////////////////////////////
195 // allocate a QMixer channel to this sample
197 if ( mpMixer
->AllocChannel( this ) == FALSE
) {
198 // can't allocate a channel!
202 // there should be an available QMixer channel, really try to get it
203 mChannel
= QSWaveMixOpenChannel( mpMixDevice
, 1, QMIX_OPENAVAILABLE
);
204 if ( mChannel
< 0 ) {
205 // couldn't open a free channel
209 // set the data format
210 audioFormat
.wFormatTag
= WAVE_FORMAT_PCM
;
211 audioFormat
.nChannels
= (WORD
) mAttribs
.nChannels
;
212 audioFormat
.nSamplesPerSec
= mAttribs
.sampleRate
;
213 // the following assumes an integral bytes/sample (PCM, not ADPCM)
214 audioFormat
.nBlockAlign
= (WORD
) ((mAttribs
.nChannels
215 * mAttribs
.bitsPerSample
) / 8);
216 audioFormat
.nAvgBytesPerSec
= mAttribs
.sampleRate
* audioFormat
.nBlockAlign
;
217 audioFormat
.wBitsPerSample
= (WORD
) mAttribs
.bitsPerSample
;
218 audioFormat
.cbSize
= 0;
222 case kSndSampleStream
:
223 ///////////////////////////////////////////////
225 // create a stream wave to play sound with
227 waveInfo
.Stream
.Format
= &audioFormat
;
228 waveInfo
.Stream
.BlockBytes
= 4096;
229 waveInfo
.Stream
.Callback
= QStreamRefillCB
;
230 waveInfo
.Stream
.User
= (void *) this;
231 waveInfo
.Stream
.Tag
= (WORD
) mSerialNum
;
234 mpWave
= QSWaveMixOpenWaveEx( mpMixDevice
, &waveInfo
,
239 case kSndSampleNormal
:
240 ///////////////////////////////////////////////
242 // create a mem-resident wave to play sound with
245 // if no sound buffer has been allocated, do it now
246 // and load it with the oneshot/loop data
247 if ( mpAudioData
== NULL
) {
248 mpAudioData
= Malloc( mBufferLen
);
249 InvokeRefillCB( mpAudioData
, mBufferLen
);
252 // fill in the memory-resident sound descriptor
253 waveInfo
.Resident
.Format
= &audioFormat
;
254 waveInfo
.Resident
.Data
= (HPSTR
) mpAudioData
;
255 waveInfo
.Resident
.Bytes
= mBufferLen
;
256 waveInfo
.Resident
.Samples
= mNumSamples
;
257 waveInfo
.Resident
.Tag
= (WORD
) mSerialNum
;
259 // make the memory-resident wave
260 mpWave
= QSWaveMixOpenWaveEx( mpMixDevice
, &waveInfo
, QMIX_RESIDENT
);
265 if ( mpWave
!= NULL
) {
266 mpMixer
->DoTrace( (void *) mBufferLen
, kSndBufferAllocate
);
267 MoveToList( mpMixer
->AudibleHead() );
268 SetFlags( kSndFlagAudible
);
270 // load shadowed state into hardware
271 mGroupVolume
= mpMixer
->GetGroupVolume( mGroup
) + mpMixer
->GetMasterVolume();
272 res
= InternalSetVolume( mpMixDevice
, mChannel
, mVolume
+ mGroupVolume
);
273 QSOUND_ERROR_CHECK( res
, "MakeAudible / SetVolume" );
274 res
= InternalSetPan( mpMixDevice
, mChannel
, mPan
);
275 QSOUND_ERROR_CHECK( res
, "MakeAudible / SetPan" );
276 res
= QSWaveMixSetFrequency( mpMixDevice
, mChannel
, 0, mFrequency
);
277 QSOUND_ERROR_CHECK( res
, "MakeAudible / SetFrequency" );
279 // enable 3D processing if needed
280 switch ( m3DMethod
) {
281 case kSnd3DMethodHardware
: //TBD: how to do HW with QSound?
282 case kSnd3DMethodSoftware
:
283 res
= QSWaveMixEnableChannel( mpMixDevice
, mChannel
, 0, QMIX_CHANNEL_QSOUND
);
285 case kSnd3DMethodPanVol
:
286 res
= QSWaveMixEnableChannel( mpMixDevice
, mChannel
, 0, QMIX_CHANNEL_STEREO
);
289 if ( m3DMethod
!= kSnd3DMethodNone
) {
290 Set3DPosition( &m3DPosition
);
291 Set3DVelocity( &m3DVelocity
);
292 Set3DConeAngles( m3DConeInnerAngle
, m3DConeOuterAngle
);
293 Set3DConeOrientation( &m3DConeOrientation
);
294 Set3DDistanceRange( m3DMinDistance
, m3DMaxDistance
);
295 Set3DMode( m3DMode
);
296 SetAmbientVolume( mAmbientVolume
);
298 QSOUND_ERROR_CHECK( res
, "MakeAudible / Set 3D or Stereo" );
301 QSOUND_ERROR( "MakeAudible / CreateSoundBuffer" );
312 cQSndSample::LLStop( void )
316 res
= QSWaveMixStopChannel( mpMixDevice
, mChannel
, 0 );
317 QSOUND_ERROR_CHECK( res
, "LLStop" );
321 cQSndSample::LLRelease( void )
325 res
= QSWaveMixFreeWave( mpMixDevice
, mpWave
);
326 QSOUND_ERROR_CHECK( res
, "LLRelease" );
332 cQSndSample::LLInit( void )
334 if ( mFlags
== kSndSampleNormal
) {
335 // if audio data is supplied, allocate a buffer & copy data to it
336 if ( mpBuffer
!= NULL
) {
337 mpAudioData
= Malloc( mBufferLen
);
338 memcpy( mpAudioData
, mpBuffer
, mBufferLen
);
348 cQSndSample
*pSample
;
350 pSample
= (cQSndSample
*) userData
;
352 // this will cause sample to stop on next Mixer::Update
353 pSample
->SetFlags( kSndFlagPleaseStop
);
359 cQSndSample::LLStart( void )
363 QMIXPLAYPARAMS playParams
;
365 assert(mpWave
!= NULL
);
367 // setup play parameters
368 playParams
.dwSize
= sizeof(playParams
);
369 playParams
.lpImage
= NULL
;
370 playParams
.hwndNotify
= 0;
371 playParams
.callback
= QSEndCB
;
372 playParams
.dwUser
= (DWORD
) this;
373 playParams
.lStart
= 0;
375 playParams
.lStartLoop
= 0;
376 playParams
.lEndLoop
= 0;
377 if ( mInitBuffPos
!= -1 ) {
378 // user specified to start play at position in mInitBuffPos
379 playParams
.lStart
= mInitBuffPos
* mBytesPerSample
;
383 nLoops
= (mStateFlags
& kSndFlagLooped
) ? -1 : 0;
385 // play the sound on the assigned channel
386 res
= QSWaveMixPlayEx( mpMixDevice
, mChannel
,
387 QMIX_PLAY_NOTIFYSTOP
, mpWave
,
388 nLoops
, &playParams
);
389 QSOUND_ERROR_CHECK( res
, "Start / Play" );
391 return (res
) ? 1 : DS_OK
;
395 cQSndSample::LLPause( void )
399 assert(mpWave
!= NULL
);
400 res
= QSWaveMixPauseChannel( mpMixDevice
, mChannel
, 0 );
401 QSOUND_ERROR_CHECK( res
, "LLPause" );
402 SetFlags( kSndFlagReallyPaused
);
406 cQSndSample::LLResume( void )
410 if ( mStateFlags
& kSndFlagReallyPaused
) {
412 res
= QSWaveMixRestartChannel( mpMixDevice
, mChannel
, 0 );
413 QSOUND_ERROR_CHECK( res
, "LLResume" );
414 ClearFlags( kSndFlagReallyPaused
);
423 cQSndSample::LLUnMute( void )
430 cQSndSample::IsPlaying()
434 if ( mChannel
>= 0 ) {
435 isDone
= QSWaveMixIsChannelDone( mpMixDevice
, mChannel
);
445 // Internal set volume routine
448 InternalSetVolume( HQMIXER pMixer
,
450 int32 milliBelVolume
)
452 int qVolume
, powerOfTwo
;
453 // approximate conversion from exponential volume to linear using a 30-step
454 // per power of two table.
455 static int mbToLin
[] = {
456 32767, 32018, 31287, 30572, 29874, 29192, 28525, 27873, 27237, 26615,
457 26007, 25413, 24832, 24265, 23711, 23169, 22640, 22123, 21618, 21124,
458 20641, 20170, 19709, 19259, 18819, 18389, 17969, 17559, 17158, 16766 };
460 // convert from millibels -10000...-1 to linear volume 0...32767
461 if ( milliBelVolume
>= 0 ) {
463 } else if (milliBelVolume
<= -9000) {
467 qVolume
= ((-milliBelVolume
) % 600) / 20;
468 // tmp should now be in the range 0 .. 29
469 qVolume
= mbToLin
[qVolume
];
470 // tmp is now the linear volume, ignoring the power of two
472 powerOfTwo
= milliBelVolume
/ -600;
473 if ( powerOfTwo
!= 0 ) {
474 qVolume
>>= powerOfTwo
;
477 return QSWaveMixSetVolume( pMixer
, channel
, 0, qVolume
);
480 // DirectSound pan values are actually in -10K ... 10K, but usually
481 // only a portion of that range is used
482 #define PAN_LIMIT 3000
483 // TBD: see if PAN_LIMIT and 1 meter range work OK
486 // Internal set pan routine
487 // QMixer doesn't have a pan setting, we fake it
488 // by setting the position in polar coords
491 InternalSetPan( HQMIXER pMixer
,
498 if ( pan
> PAN_LIMIT
) {
500 } else if ( pan
< (-PAN_LIMIT
) ) {
503 polarPos
.azimuth
= 90.0 * ( (float) pan
/ (float) PAN_LIMIT
);
504 polarPos
.range
= 1.0; // 1 meter
505 polarPos
.elevation
= 0.0;
506 res
= QSWaveMixSetPolarPosition( pMixer
, channel
, 0, &polarPos
);
512 ////////////////////////////////////
514 // SetVolume - set the sample volume
518 cQSndSample::LLSetVolume( int32 vol
)
522 TLOG3( "Smp::SetVolume %s %d + group %d", SAMPLE_NAME
, mVolume
, mGroupVolume
);
523 if ( mpWave
!= NULL
) {
524 // negative values are used directly as milliBels (DirectSound units)
525 if ( vol
< kSndMinVolume
) vol
= kSndMinVolume
;
526 if ( vol
> kSndMaxVolume
) vol
= kSndMaxVolume
;
527 res
= InternalSetVolume( mpMixDevice
, mChannel
, vol
);
528 QSOUND_ERROR_CHECK( res
, "SetVolume / SetVolume" );
534 ////////////////////////////////////
536 // SetPan - Set the pan position of the sample -10000 (left) ... 10000 (right)
539 STDMETHODIMP_(void) cQSndSample::SetPan(int32 pan
)
545 TLOG2( "Smp::SetPan %s %d", SAMPLE_NAME
, mPan
);
546 if ( mpWave
!= NULL
) {
547 if ( pan
< kSndPanLeft
) {
549 } else if ( pan
> kSndPanRight
) {
552 res
= InternalSetPan( mpMixDevice
, mChannel
, pan
);
553 QSOUND_ERROR_CHECK( res
, "SetPan / SetPan" );
557 ////////////////////////////////////
559 // SetFrequency - Set the frequency of the sample
562 STDMETHODIMP_(void) cQSndSample::SetFrequency(uint32 freq
)
567 TLOG2( "Smp::SetFrequency %s %d", SAMPLE_NAME
, freq
);
568 // clamp frequency to dsound supported range
569 if ( freq
< kSndMinFrequency
) {
570 freq
= kSndMinFrequency
;
571 } else if ( freq
> kSndMaxFrequency
) {
572 freq
= kSndMaxFrequency
;
576 // muted & running - just recalculate current
577 // position before changing frequency
579 mBasePos
= ESTIMATED_POSITION( now
);
581 TLOG2( "Smp::SetFrequency - reset baseTime %ld basePos %ld", mBaseTime
, mBasePos
);
584 // pass frequency down to dsound
585 assert(mpWave
!= NULL
);
586 res
= QSWaveMixSetFrequency( mpMixDevice
, mChannel
, 0, freq
);
587 QSOUND_ERROR_CHECK( res
, "SetFrequency / SetFrequency" );
594 cQSndSample::LLSetPosition( uint32
)
596 // set the hardware play position
597 // TBD - there is no QMixer equivalent function...
600 ///////////////////////////////////
602 // GetPosition - return the play position(in samples)
606 cQSndSample::LLGetPosition()
612 assert(mpWave
!= NULL
);
613 res
= QSWaveMixGetPlayPosition( mpMixDevice
, mChannel
, &pWave
,
614 &playPos
, QMIX_POSITION_BYTES
);
615 QSOUND_ERROR_CHECK( res
, "GetPosition / GetCurrentPosition" );
616 TLOG3( "Smp::GetPosition %s hwpos = %d baseoff %d", SAMPLE_NAME
, playPos
, mBaseOffset
);
618 return playPos
/ mBytesPerSample
;
623 // helper for LoadBuffer which just copies memory
626 *memCopyFunk( void *pSrcData
,
630 memcpy( pDstData
, pSrcData
, numBytes
);
636 ////////////////////////////////////
639 // Attempt to lock the buffer and copy memory into it. Give the data and the
640 // length to be copied. This should be used for double buffered samples.
642 STDMETHODIMP_(eSndError
) cQSndSample::LoadBuffer(uint8
*data
, uint32 len
)
644 // ALERT! THIS FUNCTION IS NOT SUPPORTED!
646 return LoadBufferIndirect( memCopyFunk
, data
, len
);
649 ////////////////////////////////////
650 // LoadBufferIndirect
652 // Attempt to lock the buffer and call app-supplied function to copy memory
653 // into it. Give the data and the length to be copied. This should be used
654 // for streaming samples
656 STDMETHODIMP_(eSndError
)
657 cQSndSample::LoadBufferIndirect( SndLoadFunction funk
,
661 TLOG3( "Smp::LoadBufferIndirect %s, %d byte pos %d", SAMPLE_NAME
, len
, mLastWrite
+ mBaseOffset
);
665 if ( IS_STREAM
&& (len
== 0) ) {
667 // Our caller has passed len 0, indicating that it is done passing data
668 // to this stream, so start the shutdown of the stream
670 //TBD! how do we tell QMixer that stream is shutting down?
671 SetFlags( kSndFlagEndOfData
);
675 pReturnedData
= funk( pFunkData
, mpRefillDest
, len
);
676 if ( pReturnedData
!= mpRefillDest
) {
677 // getdata function put data somewhere other than mpRefillDest - move data
678 memcpy( mpRefillDest
, pReturnedData
, len
);
681 if ( mDumpFile
!= NULL
) {
683 fwrite( mpRefillDest
, 1, len
, mDumpFile
);
691 ////////////////////////////////////
694 // Takes: length of buffer
696 // Returns TRUE if buffer ready
701 cQSndSample::BufferReady( uint32
/* len */ )
703 // ALERT! THIS FUNCTION IS NOT SUPPORTED!
709 ////////////////////////////////////
712 // Internally used routine to try to lock the buffer
714 BOOL
cQSndSample::LockBuffer( void **, uint32
*, void **, uint32
*, uint32
)
716 // ALERT! THIS FUNCTION IS NOT SUPPORTED!
722 void cQSndSample::UnlockBuffer(void *, uint32
, void *, uint32
)
724 // ALERT! THIS FUNCTION IS NOT SUPPORTED!
728 ////////////////////////////////////
730 // AvailToWrite - return the number of bytes that can be written
731 // to a stream sample's buffer
733 STDMETHODIMP_(uint32
) cQSndSample::AvailToWrite( void )
735 // ALERT! THIS FUNCTION IS NOT SUPPORTED!
743 // CheckStream - stop stream if needed
746 cQSndSample::CheckStream()
751 // handle muted stream - look for end-of-data condition
753 TLOG2( "Smp::CheckStream %s MUTED, readPos %d", SAMPLE_NAME
, readPos
);
754 readPos
= ESTIMATED_POSITION_NOW
;
755 if ( readPos
>= mNumSamples
) {
756 // stream sample has reached its virtual end-of-data
763 assert( mpWave
!= NULL
);
766 case kSndStatePlaying
:
767 case kSndStateInited
:
768 if ( mStateFlags
& kSndFlagEndOfData
) {
769 // we are waiting for data already in buffer to be played
770 // before we can release sample
771 if ( QSWaveMixIsChannelDone( mpMixDevice
, mChannel
) ) {
772 TLOG1("Smp::CheckStream %s stopping \n", SAMPLE_NAME
);
778 case kSndStateCreated
:
779 case kSndStateDestroyed
:
780 case kSndStateStopped
:
788 // SilenceFill - fill nBytes with silence (zeroes)
789 // advances mLastWrite
792 cQSndSample::SilenceFill( uint32
/* nBytes */ )
794 // ALERT! THIS FUNCTION IS NOT SUPPORTED!
799 /************************************************************
806 ////////////////////////////////////
812 cQSndSample::Set3DPosition( sSndVector
*pPosition
)
815 float distanceFactor
;
818 assert( pPosition
!= NULL
);
819 m3DPosition
= *pPosition
;
821 if ( mChannel
>= 0 ) {
822 distanceFactor
= ((cQSndMixer
*) mpMixer
)->GetDistanceFactor();
823 qPosition
.x
= pPosition
->x
* distanceFactor
;
824 qPosition
.y
= (-pPosition
->y
) * distanceFactor
;
825 qPosition
.z
= pPosition
->z
* distanceFactor
;
827 res
= QSWaveMixSetSourcePosition( mpMixDevice
, mChannel
, 0, &qPosition
);
828 QSOUND_ERROR_CHECK( res
, "Set3DPosition" );
833 ////////////////////////////////////
839 cQSndSample::Set3DVelocity( sSndVector
*pVelocity
)
844 assert( pVelocity
!= NULL
);
845 m3DVelocity
= *pVelocity
;
847 if ( mChannel
>= 0 ) {
848 vel
.x
= pVelocity
->x
;
849 vel
.y
= (-pVelocity
->y
);
850 vel
.z
= pVelocity
->z
;
852 res
= QSWaveMixSetSourceVelocity( mpMixDevice
, mChannel
, 0, &vel
);
853 QSOUND_ERROR_CHECK( res
, "Set3DVelocity" );
858 ////////////////////////////////////
864 cQSndSample::Set3DConeAngles( uint32 inside
,
870 m3DConeInnerAngle
= inside
;
871 m3DConeOuterAngle
= outside
;
873 // TBD - the QMixer model only has 1 angle
874 if ( mChannel
>= 0 ) {
875 cone
.x
= m3DConeOrientation
.x
;
876 cone
.y
= (-m3DConeOrientation
.y
);
877 cone
.z
= m3DConeOrientation
.z
;
879 res
= QSWaveMixSetSourceCone( mpMixDevice
, mChannel
, 0, &cone
,
880 m3DConeInnerAngle
, mAmbientVolumeDB
);
881 QSOUND_ERROR_CHECK( res
, "Set3DConeAngles" );
886 ////////////////////////////////////
892 cQSndSample::Set3DConeOrientation( sSndVector
*pOrientation
)
897 assert( pOrientation
!= NULL
);
898 m3DConeOrientation
= *pOrientation
;
900 if ( mChannel
>= 0 ) {
901 cone
.x
= m3DConeOrientation
.x
;
902 cone
.y
= (-m3DConeOrientation
.y
);
903 cone
.z
= m3DConeOrientation
.z
;
905 res
= QSWaveMixSetSourceCone( mpMixDevice
, mChannel
, 0, &cone
,
906 m3DConeInnerAngle
, mAmbientVolumeDB
);
907 QSOUND_ERROR_CHECK( res
, "Set3DConeOrientation" );
912 ////////////////////////////////////
914 // Set3DDistanceRange
918 cQSndSample::Set3DDistanceRange( float dmin
, float dmax
)
921 QMIX_DISTANCES distances
;
923 m3DMinDistance
= dmin
;
924 m3DMaxDistance
= dmax
;
926 if ( mChannel
>= 0 ) {
927 distances
.cbSize
= sizeof( distances
);
928 distances
.minDistance
= dmin
;
929 distances
.maxDistance
= dmax
;
930 distances
.scale
= mRolloff
;
932 res
= QSWaveMixSetDistanceMapping( mpMixDevice
, mChannel
, 0, &distances
);
933 QSOUND_ERROR_CHECK( res
, "Set3DConeOrientation" );
938 ////////////////////////////////////
944 cQSndSample::Set3DMode( eSnd3DMode dmode
)
951 if ( mChannel
>= 0 ) {
954 case kSnd3DModeNormal
:
955 mode
= QMIX_CHANNEL_QSOUND
;
958 case kSnd3DModeHeadRelative
:
959 mode
= QMIX_CHANNEL_STEREO
;
963 res
= QSWaveMixEnableChannel( mpMixDevice
, mChannel
, 0, mode
);
964 QSOUND_ERROR_CHECK( res
, "Set3DMode" );
969 ////////////////////////////////////
975 cQSndSample::SetAmbientVolume( int32 vol
)
980 mAmbientVolume
= vol
;
981 mAmbientVolumeDB
= vol
/ 100.0;
982 if ( mChannel
>= 0 ) {
983 //TBD: should ambientDBs be negated??
984 cone
.x
= m3DConeOrientation
.x
;
985 cone
.y
= (-m3DConeOrientation
.y
);
986 cone
.z
= m3DConeOrientation
.z
;
988 res
= QSWaveMixSetSourceCone( mpMixDevice
, mChannel
, 0, &cone
,
989 m3DConeInnerAngle
, mAmbientVolumeDB
);
990 QSOUND_ERROR_CHECK( res
, "SetAmbientVolume" );
994 ////////////////////////////////////
1000 cQSndSample::Set3DMethod( eSnd3DMethod method
)