convert line ends
[canaan.git] / prj / tech / libsrc / sound / qsample.cpp
blob91628cd067b0f878d39e7402f7680886c9025334
1 ////////////////////////////////////////////////////////////////////////
2 // $Header: x:/prj/tech/libsrc/sound/RCS/qsample.cpp 1.1 1998/03/20 12:54:01 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 <qlgsndi.h>
19 #include <mprintf.h>
21 #include <mixerlck.h>
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
44 #define MIXER_MUTEX \
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 );
51 #ifndef SHIP
53 #define QSOUND_ERROR_CHECK( res, str ) if ( res ) DisplayQSError( res, str )
54 #define QSOUND_ERROR( str ) DisplayQSError( 0, str )
56 static void
57 DisplayQSError( UINT res,
58 char *pStr )
60 char daText[512];
62 if ( res ) {
63 QSWaveMixGetErrorText( res, daText, sizeof(daText) );
64 mprintf( "Sample error from %s: %s\n", pStr, daText );
65 } else {
66 mprintf( "Sample error from %s\n", pStr );
70 #else
72 #define QSOUND_ERROR_CHECK( res, str )
73 #define QSOUND_ERROR( str )
75 #endif
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 )
97 mChannel = -1;
98 mpWave = NULL;
99 mpMixer = pMixer;
100 mpMixDevice = pMixer->GetQMixer();
102 mAmbientVolumeDB = 0.0;
103 mRolloff = 1.0;
104 mpAudioData = NULL;
108 ///////////////////////////////////
109 // cQSndSample destructor
111 // make sure that the implementor is gone
115 cQSndSample::~cQSndSample( void )
117 UINT res;
118 MIXER_MUTEX;
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
139 static BOOL CALLBACK
140 QStreamRefillCB( void *pBuffer,
141 DWORD nBytes,
142 void *pUserCBData )
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
158 BOOL
159 cQSndSample::InvokeRefillCB( void *pBuffer,
160 uint32 nBytes )
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
177 BOOL
178 cQSndSample::MakeAudible()
180 WAVEFORMATEX audioFormat;
181 QMIXWAVEPARAMS waveInfo;
182 UINT res;
184 MIXER_MUTEX;
186 TLOG3( "Smp::MakeAudible %s %ld audible %ld inaudible",
187 SAMPLE_NAME, mNumAudible, mNumInaudible );
189 if ( IS_AUDIBLE ) {
190 return TRUE;
193 ///////////////////////////////////////////////
195 // allocate a QMixer channel to this sample
197 if ( mpMixer->AllocChannel( this ) == FALSE ) {
198 // can't allocate a channel!
199 return FALSE;
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
206 return FALSE;
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;
220 switch ( mFlags ) {
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;
233 // open the stream
234 mpWave = QSWaveMixOpenWaveEx( mpMixDevice, &waveInfo,
235 QMIX_INPUTSTREAM );
237 break;
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 );
261 break;
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 );
284 break;
285 case kSnd3DMethodPanVol:
286 res = QSWaveMixEnableChannel( mpMixDevice, mChannel, 0, QMIX_CHANNEL_STEREO );
287 break;
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" );
300 } else {
301 QSOUND_ERROR( "MakeAudible / CreateSoundBuffer" );
302 return FALSE;
305 mLastWrite = 0;
307 return TRUE;
311 void
312 cQSndSample::LLStop( void )
314 UINT res;
316 res = QSWaveMixStopChannel( mpMixDevice, mChannel, 0 );
317 QSOUND_ERROR_CHECK( res, "LLStop" );
320 void
321 cQSndSample::LLRelease( void )
323 UINT res;
325 res = QSWaveMixFreeWave( mpMixDevice, mpWave );
326 QSOUND_ERROR_CHECK( res, "LLRelease" );
327 mpWave = NULL;
331 void
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 );
343 static void CALLBACK
344 QSEndCB( int,
345 LPMIXWAVE,
346 DWORD userData )
348 cQSndSample *pSample;
350 pSample = (cQSndSample *) userData;
352 // this will cause sample to stop on next Mixer::Update
353 pSample->SetFlags( kSndFlagPleaseStop );
358 HRESULT
359 cQSndSample::LLStart( void )
361 UINT res;
362 int nLoops;
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;
374 playParams.lEnd = 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;
380 mInitBuffPos = -1;
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;
394 void
395 cQSndSample::LLPause( void )
397 UINT res;
399 assert(mpWave != NULL);
400 res = QSWaveMixPauseChannel( mpMixDevice, mChannel, 0 );
401 QSOUND_ERROR_CHECK( res, "LLPause" );
402 SetFlags( kSndFlagReallyPaused );
405 void
406 cQSndSample::LLResume( void )
408 UINT res;
410 if ( mStateFlags & kSndFlagReallyPaused ) {
412 res = QSWaveMixRestartChannel( mpMixDevice, mChannel, 0 );
413 QSOUND_ERROR_CHECK( res, "LLResume" );
414 ClearFlags( kSndFlagReallyPaused );
416 } else {
417 Start();
422 void
423 cQSndSample::LLUnMute( void )
425 MakeAudible();
429 BOOL
430 cQSndSample::IsPlaying()
432 BOOL isDone;
434 if ( mChannel >= 0 ) {
435 isDone = QSWaveMixIsChannelDone( mpMixDevice, mChannel );
436 if ( !isDone ) {
437 return TRUE;
441 return FALSE;
445 // Internal set volume routine
447 static UINT
448 InternalSetVolume( HQMIXER pMixer,
449 int channel,
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 ) {
462 qVolume = 32767;
463 } else if (milliBelVolume <= -9000) {
464 qVolume = 0;
465 } else {
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
490 static UINT
491 InternalSetPan( HQMIXER pMixer,
492 int channel,
493 int32 pan )
495 UINT res;
496 QSPOLAR polarPos;
498 if ( pan > PAN_LIMIT ) {
499 pan = PAN_LIMIT;
500 } else if ( pan < (-PAN_LIMIT) ) {
501 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 );
508 return res;
512 ////////////////////////////////////
514 // SetVolume - set the sample volume
517 void
518 cQSndSample::LLSetVolume( int32 vol )
520 HRESULT res;
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)
541 UINT res;
543 mPan = pan;
545 TLOG2( "Smp::SetPan %s %d", SAMPLE_NAME, mPan );
546 if ( mpWave != NULL ) {
547 if ( pan < kSndPanLeft ) {
548 pan = kSndPanLeft;
549 } else if ( pan > kSndPanRight ) {
550 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)
564 DWORD now;
565 UINT res;
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;
574 if ( !IS_AUDIBLE ) {
575 if ( IS_RUNNING ) {
576 // muted & running - just recalculate current
577 // position before changing frequency
578 now = timeGetTime();
579 mBasePos = ESTIMATED_POSITION( now );
580 mBaseTime = now;
581 TLOG2( "Smp::SetFrequency - reset baseTime %ld basePos %ld", mBaseTime, mBasePos );
583 } else {
584 // pass frequency down to dsound
585 assert(mpWave != NULL);
586 res = QSWaveMixSetFrequency( mpMixDevice, mChannel, 0, freq);
587 QSOUND_ERROR_CHECK( res, "SetFrequency / SetFrequency" );
589 mFrequency = freq;
593 void
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)
605 uint32
606 cQSndSample::LLGetPosition()
608 DWORD playPos;
609 UINT res;
610 LPMIXWAVE pWave;
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
625 static void
626 *memCopyFunk( void *pSrcData,
627 void *pDstData,
628 uint32 numBytes )
630 memcpy( pDstData, pSrcData, numBytes );
632 return pDstData;
636 ////////////////////////////////////
637 // LoadBuffer
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!
645 assert( 0 );
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,
658 void *pFunkData,
659 uint32 len )
661 TLOG3( "Smp::LoadBufferIndirect %s, %d byte pos %d", SAMPLE_NAME, len, mLastWrite + mBaseOffset );
663 void *pReturnedData;
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 );
672 return kSndOk;
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 ) {
682 // dump the data
683 fwrite( mpRefillDest, 1, len, mDumpFile );
686 mLastWrite += len;
688 return kSndOk;
691 ////////////////////////////////////
692 // BufferReady
694 // Takes: length of buffer
696 // Returns TRUE if buffer ready
700 STDMETHODIMP_(BOOL)
701 cQSndSample::BufferReady( uint32 /* len */ )
703 // ALERT! THIS FUNCTION IS NOT SUPPORTED!
704 assert( 0 );
706 return TRUE;
709 ////////////////////////////////////
710 // LockBuffer
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!
717 assert( 0 );
719 return TRUE;
722 void cQSndSample::UnlockBuffer(void *, uint32, void *, uint32)
724 // ALERT! THIS FUNCTION IS NOT SUPPORTED!
725 assert( 0 );
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!
736 assert( 0 );
738 return 0;
743 // CheckStream - stop stream if needed
745 STDMETHODIMP_(void)
746 cQSndSample::CheckStream()
748 uint32 readPos;
750 if ( IS_MUTED ) {
751 // handle muted stream - look for end-of-data condition
752 if ( IS_RUNNING ) {
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
757 DeferredStop();
760 return;
763 assert( mpWave != NULL );
765 switch( mState ) {
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 );
773 DeferredStop();
776 break;
778 case kSndStateCreated:
779 case kSndStateDestroyed:
780 case kSndStateStopped:
781 default:
782 break;
788 // SilenceFill - fill nBytes with silence (zeroes)
789 // advances mLastWrite
791 STDMETHODIMP_(void)
792 cQSndSample::SilenceFill( uint32 /* nBytes */ )
794 // ALERT! THIS FUNCTION IS NOT SUPPORTED!
795 assert( 0 );
799 /************************************************************
801 * 3 D S T U F F
806 ////////////////////////////////////
808 // Set3DPosition
811 STDMETHODIMP_(void)
812 cQSndSample::Set3DPosition( sSndVector *pPosition )
814 UINT res;
815 float distanceFactor;
816 QSVECTOR qPosition;
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 ////////////////////////////////////
835 // Set3DVelocity
838 STDMETHODIMP_(void)
839 cQSndSample::Set3DVelocity( sSndVector *pVelocity )
841 HRESULT res;
842 QSVECTOR vel;
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 ////////////////////////////////////
860 // Set3DConeAngles
863 STDMETHODIMP_(void)
864 cQSndSample::Set3DConeAngles( uint32 inside,
865 uint32 outside )
867 HRESULT res;
868 QSVECTOR cone;
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 ////////////////////////////////////
888 // Set3DOrientation
891 STDMETHODIMP_(void)
892 cQSndSample::Set3DConeOrientation( sSndVector *pOrientation )
894 HRESULT res;
895 QSVECTOR cone;
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
917 STDMETHODIMP_(void)
918 cQSndSample::Set3DDistanceRange( float dmin, float dmax )
920 HRESULT res;
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 ////////////////////////////////////
940 // Set3DMode
943 STDMETHODIMP_(void)
944 cQSndSample::Set3DMode( eSnd3DMode dmode )
946 int mode;
947 HRESULT res;
949 m3DMode = dmode;
951 if ( mChannel >= 0 ) {
952 switch( dmode ) {
953 default:
954 case kSnd3DModeNormal:
955 mode = QMIX_CHANNEL_QSOUND;
956 break;
957 case kSnd3DModeNone:
958 case kSnd3DModeHeadRelative:
959 mode = QMIX_CHANNEL_STEREO;
960 break;
963 res = QSWaveMixEnableChannel( mpMixDevice, mChannel, 0, mode );
964 QSOUND_ERROR_CHECK( res, "Set3DMode" );
969 ////////////////////////////////////
971 // SetAmbientVolume
974 STDMETHODIMP_(void)
975 cQSndSample::SetAmbientVolume( int32 vol )
977 HRESULT res;
978 QSVECTOR cone;
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 ////////////////////////////////////
996 // Set3DMethod
999 STDMETHODIMP_(void)
1000 cQSndSample::Set3DMethod( eSnd3DMethod method )
1002 m3DMethod = method;