Use configured resolution for login/outgame/ingame
[ryzomcore.git] / nel / src / sound / driver / dsound / source_dsound.cpp
blob4f52812e1d76a06565441311eff867d5b58faeb9
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2010 Matt RAYKOWSKI (sfb) <matt.raykowski@gmail.com>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "stddsound.h"
22 #include "source_dsound.h"
23 #include "sound_driver_dsound.h"
24 #include "buffer_dsound.h"
25 #include "listener_dsound.h"
27 #ifdef DEBUG_NEW
28 #define new DEBUG_NEW
29 #endif
31 using namespace NLMISC;
32 using namespace std;
35 namespace NLSOUND {
38 #if NLSOUND_PROFILE
40 #define INITTIME(_var) TTicks _var = CTime::getPerformanceTime()
42 #define DEBUG_POSITIONS 1
44 #if DEBUG_POSITIONS
45 #define DBGPOS(_a) nldebug ## _a
46 #else
47 #define DBGPOS(_a)
48 #endif
50 #else
51 #define INITTIME(_var)
52 #define DBGPOS(_a)
53 #endif
57 const uint32 CSourceDSound::_SecondaryBufferSize = 0x10000;
58 const uint32 CSourceDSound::_SizeMask = 0xffff;
59 const uint32 CSourceDSound::_SwapCopySize = 0x8000;
60 const uint32 CSourceDSound::_UpdateCopySize = 0x4000;
61 const uint32 CSourceDSound::_XFadeSize = 64;
62 const uint CSourceDSound::_DefaultChannels = 1;
63 const uint CSourceDSound::_DefaultSampleRate = 22050;
64 const uint CSourceDSound::_DefaultSampleSize = 16;
68 #define NLSOUND_MIN(_a,_b) (((_a) < (_b)) ? (_a) : (_b))
69 #define NLSOUND_DISTANCE(_from, _to, _period) (((_to) > (_from)) ? (_to) - (_from) : (_period) + (_to) - (_from))
72 #if NLSOUND_PROFILE
74 // Static variables used for profiling
75 double CSourceDSound::_LastSwapTime = 0.0;
76 double CSourceDSound::_TotalSwapTime = 0.0;
77 double CSourceDSound::_MaxSwapTime = 0.0;
78 double CSourceDSound::_MinSwapTime = 1000000.0;
79 uint32 CSourceDSound::_SwapCount = 0;
80 double CSourceDSound::_PosTime = 0.0;
81 double CSourceDSound::_LockTime = 0.0;
82 double CSourceDSound::_CopyTime = 0.0;
83 double CSourceDSound::_UnlockTime = 0.0;
84 uint32 CSourceDSound::_CopyCount = 0;
85 double CSourceDSound::_TotalUpdateTime = 0.0;
86 double CSourceDSound::_MaxUpdateTime = 0.0;
87 double CSourceDSound::_MinUpdateTime = 1000000.0;
88 uint32 CSourceDSound::_UpdateCount = 0;
89 uint32 CSourceDSound::_TotalUpdateSize = 0;
90 #endif
93 uint32 getWritePosAndSpace(uint32 &nextWritePos, uint32 playPos, uint32 writePos, uint32 bufferSize);
96 // ******************************************************************
98 CSourceDSound::CSourceDSound( uint sourcename )
99 : ISource(),
100 _SourceName(sourcename)
102 #if EAX_AVAILABLE == 1
103 _EAXSource = 0;
104 #endif
105 _Sample = 0;
106 _SampleSize = 0;
107 _SampleOffset = 0;
108 _Format = Mono8;
109 _SampleFreq = _DefaultSampleRate;
110 _FillOffset = 0;
111 _State = source_stopped;
112 _PlayOffset = 0;
113 _LastPlayPos = 0;
114 _PosRelative= false;
116 // _BufferSize = 0;
117 // _SwapBuffer = 0;
118 _SecondaryBuffer = 0;
119 // _SecondaryBufferState = NL_DSOUND_SILENCED;
120 _3DBuffer = 0;
121 // _NextWritePos = 0;
122 // _BytesWritten = 0;
123 // _SilenceWritten = 0;
124 _Loop = false;
125 // _EndPosition = 0;
126 // _EndState = NL_DSOUND_TAIL1;
127 // _UserState = NL_DSOUND_STOPPED;
128 _Freq = 1.0f;
129 _SampleRate = _DefaultSampleRate;
130 // _IsUsed = false;
131 _Gain = 1.0f;
132 _Volume = 0;
133 _Alpha = 0.0;
134 InitializeCriticalSection(&_CriticalSection);
138 // ******************************************************************
140 CSourceDSound::~CSourceDSound()
142 nldebug("Destroying DirectSound source");
144 CSoundDriverDSound::instance()->removeSource(this);
146 EnterCriticalSection(&_CriticalSection);
148 // Release the DirectSound buffer within the critical zone
149 // to avoid a call to update during deconstruction
150 release();
152 LeaveCriticalSection(&_CriticalSection);
153 DeleteCriticalSection(&_CriticalSection);
157 // ******************************************************************
159 void CSourceDSound::release()
161 // _Buffer = 0;
163 #if EAX_AVAILABLE == 1
164 if (_EAXSource != 0)
166 _EAXSource->Release();
167 _EAXSource = 0;
169 #endif
171 if (_SecondaryBuffer != 0)
173 _SecondaryBuffer->Stop();
176 if (_3DBuffer != 0)
178 _3DBuffer->Release();
179 _3DBuffer = 0;
182 if (_SecondaryBuffer != 0)
184 _SecondaryBuffer->Release();
185 _SecondaryBuffer = 0;
191 uint32 CSourceDSound::getTime()
193 if (_Sample == 0)
194 return 0;
196 TSampleFormat format;
197 uint freq;
199 _Sample->getFormat(format, freq);
201 return uint32(1000.0f * (_PlayOffset+1) / (float)freq);
204 // ******************************************************************
206 void CSourceDSound::init(LPDIRECTSOUND directSound, bool useEax)
209 // Initialize the buffer format
210 WAVEFORMATEX format;
212 format.cbSize = sizeof(WAVEFORMATEX);
213 format.nChannels = _DefaultChannels;
214 format.wBitsPerSample = _DefaultSampleSize;
215 format.nSamplesPerSec = _DefaultSampleRate;
216 format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
217 format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
218 format.wFormatTag = WAVE_FORMAT_PCM;
221 // Initialize the buffer description
223 DSBUFFERDESC desc;
225 CSoundDriverDSound* driver = CSoundDriverDSound::instance();
228 ZeroMemory(&desc, sizeof(DSBUFFERDESC));
229 desc.dwSize = sizeof(DSBUFFERDESC);
230 desc.lpwfxFormat = &format;
231 desc.dwBufferBytes = _SecondaryBufferSize;
232 desc.dwReserved = 0;
234 if (driver->countHw3DBuffers() > 0)
236 //nldebug("Source: Allocating 3D buffer in hardware");
237 desc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_LOCHARDWARE | DSBCAPS_GETCURRENTPOSITION2
238 | DSBCAPS_CTRL3D | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY | DSBCAPS_MUTE3DATMAXDISTANCE;
240 else
242 nldebug("Failed to create a 3D Hardware DirectX secondary buffer. Try 3D software one");
244 if (useEax)
246 throw ESoundDriver("No 3d hardware sound buffer, but EAX support requested");
248 //nldebug("Source: Allocating 3D buffer in software");
249 desc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_LOCSOFTWARE | DSBCAPS_GETCURRENTPOSITION2
250 | DSBCAPS_CTRL3D | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY | DSBCAPS_MUTE3DATMAXDISTANCE;
251 desc.guid3DAlgorithm = DS3DALG_NO_VIRTUALIZATION;
252 //desc.guid3DAlgorithm = DS3DALG_HRTF_FULL;
256 // Allocate the secondary buffer
258 if (FAILED(directSound->CreateSoundBuffer(&desc, &_SecondaryBuffer, NULL)))
260 if (useEax)
262 throw ESoundDriver("Failed to create a 3d hardware sound buffer, but EAX support requested");
264 nlwarning("Source: Failed to create a buffer with 3D capabilities.");
266 ZeroMemory(&desc, sizeof(DSBUFFERDESC));
267 desc.dwSize = sizeof(DSBUFFERDESC);
268 desc.lpwfxFormat = &format;
269 desc.dwBufferBytes = _SecondaryBufferSize;
270 desc.dwReserved = 0;
272 if (driver->countHw2DBuffers() > 0)
274 //nldebug("Source: Allocating 2D buffer in hardware");
275 desc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_LOCHARDWARE | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY;
277 else
279 //nldebug("Source: Allocating 2D buffer in software");
280 desc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_LOCSOFTWARE | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY;
283 if (FAILED(directSound->CreateSoundBuffer(&desc, &_SecondaryBuffer, NULL)))
285 throw ESoundDriver("Failed to allocate the DirectSound secondary buffer");
290 nldebug("Created DirectX secondary buffer @ %p", _SecondaryBuffer);
292 // Fill the buffer with silence
293 LPVOID ptr;
294 DWORD bytes;
296 if (FAILED(_SecondaryBuffer->Lock(0, 0, &ptr, &bytes, NULL, NULL, DSBLOCK_ENTIREBUFFER)))
298 throw ESoundDriver("Failed to lock the DirectSound secondary buffer");
301 memset(ptr, 0, bytes);
303 _SecondaryBuffer->Unlock(ptr, bytes, 0, 0);
305 // Allocate the 3D interface, if necessary
307 if (FAILED(_SecondaryBuffer->QueryInterface(IID_IDirectSound3DBuffer, (LPVOID *) &_3DBuffer)))
309 throw ESoundDriver("Failed to allocate the DirectSound 3D buffer");
313 if (FAILED(_SecondaryBuffer->Play(0, 0, DSBPLAY_LOOPING)))
315 throw ESoundDriver("Play failed");
320 // ******************************************************************
322 void CSourceDSound::reset()
324 setPitch(1.0f);
325 setLooping(false);
326 setGain(1.0f);
329 /// Enable or disable streaming mode. Source must be stopped to call this.
330 void CSourceDSound::setStreaming(bool streaming)
332 if (streaming) throw ESoundDriverNoBufferStreaming();
335 // ******************************************************************
337 void CSourceDSound::setStaticBuffer( IBuffer *buffer )
339 EnterCriticalSection(&_CriticalSection);
341 if (_State == source_playing)
343 _State = source_swap_pending;
344 _Sample = 0;
345 _NextSample = buffer;
346 _SampleOffset = 0;
347 _PlayOffset = 0;
349 else
351 _Sample = buffer;
352 _NextSample = 0;
353 _SampleOffset = 0;
354 _PlayOffset = 0;
355 _ADPCMState.PreviousSample = 0;
356 _ADPCMState.StepIndex = 0;
357 if (buffer)
359 // _SampleSize = buffer->getSize();
360 buffer->getFormat(_Format, _SampleFreq);
361 switch(_Format)
363 case Mono8:
364 _SampleSize = buffer->getSize();
365 break;
366 case Mono16:
367 _SampleSize = buffer->getSize() / 2;
368 break;
369 case Mono16ADPCM:
370 _SampleSize = buffer->getSize() * 2;
371 break;
372 case Stereo8:
373 _SampleSize = buffer->getSize() / 2;
374 break;
375 case Stereo16:
376 _SampleSize = buffer->getSize() / 4;
377 break;
383 // If the user calls setStaticBuffer with a null buffer,
384 // stop the currently playing buffer and set it to null.
385 // Otherwise, store the buffer in the swap buffer variable.
386 // A crossfade between the current buffer and the swap buffer
387 // will be done when the user calls play.
388 if (buffer == 0)
390 stop();
391 _Buffer = 0;
392 _BufferSize = 0;
393 _BytesWritten = 0;
396 _SwapBuffer = _Buffer = buffer;
398 _ADPCMState.PreviousSample = 0;
399 _ADPCMState.StepIndex = 0;
401 LeaveCriticalSection(&_CriticalSection);
404 IBuffer *CSourceDSound::getStaticBuffer()
406 if (_State == source_swap_pending)
407 return _NextSample;
408 else
409 return _Sample;
413 /// Add a buffer to the streaming queue. A buffer of 100ms length is optimal for streaming.
414 /// Should be called by a thread which checks countStreamingBuffers every 100ms.
415 void CSourceDSound::submitStreamingBuffer(IBuffer * /* buffer */)
417 throw ESoundDriverNoBufferStreaming();
420 /// Return the amount of buffers in the queue (playing and waiting). 3 buffers is optimal.
421 uint CSourceDSound::countStreamingBuffers() const
423 throw ESoundDriverNoBufferStreaming();
426 void CSourceDSound::getCursors(TCursors &cursors)
428 _SecondaryBuffer->GetCurrentPosition((DWORD*)&cursors.PlayCursor, (DWORD*)&cursors.WriteCursor);
429 // add a security margin to the write cursor
430 /* cursors.WriteCursor += _UpdateCopySize;
431 if (cursors.WriteCursor > _SecondaryBufferSize)
432 cursors.WriteCursor -= _SecondaryBufferSize;
434 // compute the available write size
435 // cursors.WriteSize = std::min(_UpdateCopySize, cursors.PlayCursor + _SecondaryBufferSize - cursors.WriteCursor);
436 cursors.WriteSize = std::min(_UpdateCopySize, (cursors.PlayCursor - cursors.WriteCursor) & _SizeMask);
439 void CSourceDSound::fillData(sint16 *dst, uint nbSample)
441 nlassert((nbSample & 0xfffffffe) == nbSample);
442 if (_Sample != 0)
444 const void *data = ((CBufferDSound*) _Sample)->getData();
445 const sint8 *data8;
446 const uint8 *dataAdpcm;
447 const sint16 *data16;
448 uint i;
450 //nldebug("Filling from %p to %p (%p sample, %p bytes)", dst, dst+nbSample, nbSample, nbSample*2);
453 switch(_Format)
455 case Mono8:
456 data8 = (sint8*) data;
457 data8 += _SampleOffset;
458 // nldebug(" with data (Mono8) from %p to %p (sample : base = %p, size = %p)", data8, data8+nbSample, data, _SampleSize);
459 for (i=0; i<nbSample; ++i)
461 dst[i] = sint16(data8[i])*256;
463 _SampleOffset += nbSample;
464 break;
465 case Mono16ADPCM:
466 dataAdpcm = (uint8*) data;
467 dataAdpcm += _SampleOffset/2;
468 //nldebug("Filling ADPCM : %p => %p with %p to %p", dst, dst+nbSample, dataAdpcm - (uint8*)data, dataAdpcm + (nbSample/2)-(uint8*)data);
469 //nldebug(" with data (Mono16ADPCM) from %p to %p (sample : base = %p, size = %p)", dataAdpcm, dataAdpcm+(nbSample/2), data, _SampleSize*2);
470 IBuffer::decodeADPCM(dataAdpcm, dst, nbSample, _ADPCMState);
471 _SampleOffset += nbSample;
472 break;
473 case Mono16:
474 data16 = (sint16*)data;
475 data16 += _SampleOffset;
476 //nldebug("Filling Mono16 : %p => %p with %p to %p", dst, dst+nbSample, data16 - (sint16*)data, data16 + (nbSample)-(sint16*)data);
477 // nldebug(" with data (Mono16) from %p to %p (sample : base = %p, size = %p)", data16, data16+nbSample, data, _SampleSize/2);
478 CFastMem::memcpy(dst, data16, nbSample*2);
479 _SampleOffset += nbSample;
480 break;
481 case Stereo8:
482 data8 = (sint8*) data;
483 data8 += _SampleOffset*2;
484 for (i=0; i<nbSample; ++i)
486 dst[i] = (sint16(data8[i*2])*128) + (sint16(data8[i*2+1])*128);
488 _SampleOffset += nbSample*2;
489 break;
490 case Stereo16:
491 data16 = (sint16*) data;
492 data16 += _SampleOffset*2;
493 for (i=0; i<nbSample; ++i)
495 dst[i] = (data16[i*2]>>1) + (data16[i*2+1]>>1);
497 _SampleOffset += nbSample*2;
498 break;
501 /* if (_SampleOffset == _SampleSize)
503 _SampleOffset = 0;
504 _ADPCMState.PreviousSample = 0;
505 _ADPCMState.StepIndex = 0;
509 nlassert(_SampleOffset <= _SampleSize);
511 else
513 nldebug("Filling : NO DATA from %p to %p (%p sample, %p bytes)", dst, dst+nbSample, nbSample, nbSample*2);
515 // write silence in the dst.
516 while (nbSample)
518 *dst++ = 0;
519 --nbSample;
524 void CSourceDSound::fillData(const TLockedBufferInfo &lbi, int nbSample)
526 nlassert((nbSample & 0x1) == 0);
527 /* nlassert(lbi.Size1 != 0);
528 nlassert(lbi.Ptr1 != NULL);
529 */ uint size = std::min(uint32(nbSample), lbi.Size1>>1);
530 fillData(lbi.Ptr1, size);
531 nbSample -= size;
533 if (nbSample)
535 /* nlassert(lbi.Size2 != 0);
536 nlassert(lbi.Ptr2 != NULL);
537 */ size = min(uint32(nbSample), lbi.Size2>>1);
538 fillData(lbi.Ptr2, size);
539 nbSample -= size;
541 nlassert(nbSample == 0);
544 void CSourceDSound::fillSilence(const TLockedBufferInfo &lbi, int nbSample)
546 uint size = min(uint32(nbSample), lbi.Size1>>1);
547 uint tmp = size;
548 sint16 *ptr = lbi.Ptr1;
549 // nldebug("Silencing from %p to %p (%p sample, %p bytes)", ptr, ptr+size, size, size*2);
551 for (; size != 0; --size)
552 *ptr++ = 0;
553 nbSample -= tmp;
555 if (nbSample)
557 size = std::min(uint32(nbSample), lbi.Size2>>1);
558 tmp = size;
559 ptr = lbi.Ptr2;
560 // nldebug("Silencing from %p to %p (%p sample, %p bytes)", ptr, ptr+size, size, size*2);
561 for (; size != 0; --size)
562 *ptr++ = 0;
563 nbSample -= tmp;
565 nlassert(nbSample == 0);
569 void CSourceDSound::xfade(const TLockedBufferInfo &lbi, sint16 *src)
571 // do the XFade in integer fixed point arithmetic
573 nlassert((_XFadeSize & 0x1) == 0);
574 uint fade = _XFadeSize;
575 sint16 *ptr = lbi.Ptr1;
576 uint count = lbi.Size1 /2;
577 sint alpha, invAlpha;
579 while (fade && count)
581 alpha = (fade<<16) / _XFadeSize;
582 invAlpha = 0x10000 - alpha;
583 *ptr = (sint(*ptr)*alpha + sint(*src) * invAlpha) >> 16;
584 ++src;
585 ++ptr;
586 --count;
587 --fade;
590 ptr = lbi.Ptr2;
591 count = lbi.Size2 /2;
593 while (fade && count)
595 alpha = (fade<<16) / _XFadeSize;
596 invAlpha = 0x10000 - alpha;
597 *ptr = (sint(*ptr)*alpha + sint(*src) * invAlpha) >> 16;
598 ++src;
599 ++ptr;
600 --count;
601 --fade;
605 void CSourceDSound::fadeOut(const TLockedBufferInfo &lbi)
607 nlassert((_XFadeSize & 0x1) == 0);
608 uint fade = _XFadeSize;
609 sint16 *ptr = lbi.Ptr1;
610 uint count = lbi.Size1/2;
611 sint alpha;
613 while (fade && count)
615 alpha = (fade<<16) / _XFadeSize;
616 *ptr = (*ptr*alpha) >> 16;
617 ++ptr;
618 --count;
619 --fade;
622 ptr = lbi.Ptr2;
623 count = lbi.Size2/2;
625 while (fade && count)
627 alpha = (fade<<16) / _XFadeSize;
628 *ptr = (*ptr*alpha) >> 16;
629 ++ptr;
630 --count;
631 --fade;
634 void CSourceDSound::fadeIn(const TLockedBufferInfo &lbi)
636 // do the XFade in integer fixed point arithmetic
638 nlassert((_XFadeSize & 0x1) == 0);
639 uint fade = _XFadeSize;
640 sint16 *ptr = lbi.Ptr1;
641 uint count = lbi.Size1 /2;
642 sint alpha, invAlpha;
644 while (fade && count)
646 alpha = (fade<<16) / _XFadeSize;
647 invAlpha = 0x10000 - alpha;
648 *ptr = (*ptr*invAlpha) >> 16;
649 ++ptr;
650 --count;
651 --fade;
654 ptr = lbi.Ptr2;
655 count = lbi.Size2 /2;
657 while (fade && count)
659 alpha = (fade<<16) / _XFadeSize;
660 invAlpha = 0x10000 - alpha;
661 *ptr = (*ptr*invAlpha) >> 16;
662 ++ptr;
663 --count;
664 --fade;
669 void CSourceDSound::advanceFill(TLockedBufferInfo &lbi, uint nbSample)
671 uint32 size = nbSample * 2;
672 if (lbi.Size1 < size)
674 size -= lbi.Size1;
675 lbi.Size1 = lbi.Size2;
676 lbi.Size2 = 0;
677 lbi.Ptr1 = lbi.Ptr2;
678 lbi.Ptr2 = 0;
681 nlassert(lbi.Size1 >= size);
682 lbi.Size1 -= size;
683 lbi.Ptr1 += size/2;
685 _FillOffset += nbSample*2;
686 nlassert(_FillOffset == (_FillOffset & 0xfffffffC));
687 _FillOffset &= _SizeMask;
688 // if (_FillOffset >= _SecondaryBufferSize)
689 // _FillOffset -= _SecondaryBufferSize;
690 nlassert(_FillOffset < _SecondaryBufferSize);
695 bool CSourceDSound::play()
697 // nldebug("Play");
698 EnterCriticalSection(&_CriticalSection);
700 _SilenceWriten = 0;
702 // uint32 writeSize = checkFillCursor();
703 TCursors cursors;
704 getCursors(cursors);
706 // set a new filling point
707 _FillOffset = cursors.WriteCursor;
708 _FillOffset = (_FillOffset+3) & 0xfffffffC;
709 cursors.WriteCursor = _FillOffset;
711 TLockedBufferInfo lbi;
712 if (lock(_FillOffset, cursors.WriteSize, lbi))
714 TLockedBufferInfo unlockInfo(lbi);
715 // ok, the buffer is locked, write data
716 if (_State == source_swap_pending)
718 // we swap the buffer.
719 _Sample = _NextSample;
720 _NextSample = 0;
721 if (_Sample != 0)
723 _Sample->getFormat(_Format, _SampleFreq);
724 switch(_Format)
726 case Mono8:
727 _SampleSize = _Sample->getSize();
728 break;
729 case Mono16:
730 _SampleSize = _Sample->getSize() / 2;
731 break;
732 case Mono16ADPCM:
733 _SampleSize = _Sample->getSize() * 2;
734 break;
735 case Stereo8:
736 _SampleSize = _Sample->getSize() / 2;
737 break;
738 case Stereo16:
739 _SampleSize = _Sample->getSize() / 4;
740 break;
742 _State = source_playing;
744 else
746 _SampleSize = 0;
747 _State = source_silencing;
750 _LastPlayPos = cursors.PlayCursor;
751 _SampleOffset = 0;
752 _PlayOffset = 0;
753 _ADPCMState.PreviousSample = 0;
754 _ADPCMState.StepIndex = 0;
755 // Compute the size of data to write.
756 uint dataToFill = std::min(uint(cursors.WriteSize / 2), _SampleSize - _SampleOffset);
757 dataToFill &= 0xfffffffe;
758 // ok, the buffer is locked, write data
759 if (_State == source_playing || _State == source_silencing)
761 // we need a little XFade
762 sint16 fadeBuffer[_XFadeSize];
763 fillData(fadeBuffer, _XFadeSize);
764 xfade(lbi, fadeBuffer);
765 advanceFill(lbi, _XFadeSize);
766 cursors.WriteSize -= _XFadeSize*2;
767 dataToFill -= _XFadeSize;
769 else
771 // we need a little FadeIn
772 fillData(lbi, _XFadeSize);
773 fadeIn(lbi);
774 advanceFill(lbi, _XFadeSize);
775 cursors.WriteSize -= _XFadeSize*2;
776 dataToFill -= _XFadeSize;
778 fillData(lbi, dataToFill);
779 cursors.WriteSize -= dataToFill * 2;
780 advanceFill(lbi, dataToFill);
781 _State = source_playing;
782 if (_Loop)
784 while (cursors.WriteSize >= 4)
786 if (_SampleOffset == _SampleSize)
788 // rewind the sample
789 _SampleOffset = 0;
790 _ADPCMState.PreviousSample = 0;
791 _ADPCMState.StepIndex = 0;
793 nlassert(_SampleOffset < _SampleSize);
794 dataToFill = std::min(uint(cursors.WriteSize / 2), _SampleSize - _SampleOffset);
795 dataToFill &= 0xfffffffe;
796 fillData(lbi, dataToFill);
797 advanceFill(lbi, dataToFill);
798 cursors.WriteSize -= dataToFill*2;
801 else
803 if (_SampleOffset == _SampleSize)
805 // begin to write silence, but stil in play state until all sample are played
806 // _State = source_silencing;
807 fillSilence(lbi, cursors.WriteSize/2);
808 advanceFill(lbi, cursors.WriteSize/2);
809 _SilenceWriten = cursors.WriteSize;
810 cursors.WriteSize = 0;
812 // else
813 // _State = source_playing;
817 unlock(unlockInfo);
819 else
821 nlwarning("Couldn't lock the sound buffer for %u bytes", cursors.WriteSize);
824 // set the volume NOW
825 CListenerDSound* listener = CListenerDSound::instance();
827 updateVolume(listener->getPos());
829 LeaveCriticalSection(&_CriticalSection);
831 return true;
834 void CSourceDSound::stop()
836 // nldebug("Stop");
837 EnterCriticalSection(&_CriticalSection);
839 if (_State != source_stopped && _State != source_silencing)
841 // retreive the cursors;
842 TCursors cursors;
843 getCursors(cursors);
845 _FillOffset = cursors.WriteCursor;
846 _FillOffset = (_FillOffset+3) & 0xfffffffC;
848 TLockedBufferInfo lbi;
849 if (lock(_FillOffset, cursors.WriteSize, lbi))
851 TLockedBufferInfo unlockInfo(lbi);
853 fadeOut(lbi);
854 advanceFill(lbi, _XFadeSize);
855 cursors.WriteSize -= _XFadeSize*2;
856 fillSilence(lbi, cursors.WriteSize/2);
857 advanceFill(lbi, cursors.WriteSize/2);
858 _SilenceWriten = cursors.WriteSize;
860 _State = source_silencing;
862 unlock(unlockInfo);
866 LeaveCriticalSection(&_CriticalSection);
871 // ******************************************************************
873 void CSourceDSound::setLooping( bool l )
875 _Loop = l;
879 // ******************************************************************
881 bool CSourceDSound::getLooping() const
883 return _Loop;
887 // ******************************************************************
889 void CSourceDSound::swap()
891 _Buffer = _SwapBuffer;
892 _BufferSize = _Buffer->getSize();
893 _BytesWritten = 0;
894 _SwapBuffer = 0;
897 // ******************************************************************
899 bool CSourceDSound::play()
901 EnterCriticalSection(&_CriticalSection);
903 if (_Buffer == 0 || ((CBufferDSound*) _Buffer)->getData() == 0)
905 // the sample has been unloaded, can't play!
907 LeaveCriticalSection(&_CriticalSection);
908 return false;
912 DBGPOS(("[%p] PLAY: Enter, buffer state = %u", this, _SecondaryBufferState));
913 switch (_SecondaryBufferState)
915 case NL_DSOUND_FILLING:
916 if (_SwapBuffer != 0)
918 // crossfade to the new sound
919 DBGPOS(("[%p] PLAY: XFading 1", this));
920 crossFade();
922 break;
924 case NL_DSOUND_SILENCING:
925 if (_SwapBuffer != 0)
927 if ((_Buffer != 0) && (_UserState == NL_DSOUND_PLAYING))
929 // crossfade to the new sound
930 DBGPOS(("[%p] PLAY: XFading 2", this));
931 crossFade();
933 else
935 DBGPOS(("[%p] PLAY: Swap & fadein 1", this));
936 swap();
937 fadeIn();
940 else
942 DBGPOS(("[%p] PLAY: Fadein", this));
943 _BytesWritten = 0;
944 // start the old sound again
945 fadeIn();
948 break;
951 case NL_DSOUND_SILENCED:
952 if (_SwapBuffer != 0)
954 // fade in to the new sound
955 DBGPOS(("[%p] PLAY: Swap & fadein 2", this));
956 swap();
957 fadeIn();
959 else
961 DBGPOS(("[%p] PLAY: Fadein", this));
962 _BytesWritten = 0;
963 // start the old sound again
964 fadeIn();
968 _UserState = NL_DSOUND_PLAYING;
969 DBGPOS(("[%p] PLAY: PLAYING", this));
971 //nldebug ("NLSOUND: %p play", this);
973 LeaveCriticalSection(&_CriticalSection);
975 return true;
979 // ******************************************************************
981 void CSourceDSound::stop()
983 EnterCriticalSection(&_CriticalSection);
985 TSourceDSoundUserState old = _UserState;
987 _UserState = NL_DSOUND_STOPPED;
988 DBGPOS(("[%p] STOP: STOPPED", this));
990 //nldebug ("NLSOUND: %p stop", this);
992 if (old == NL_DSOUND_PLAYING)
994 fadeOut();
997 _BytesWritten = 0;
999 LeaveCriticalSection(&_CriticalSection);
1002 // ******************************************************************
1004 void CSourceDSound::pause()
1006 // TODO : recode this !
1007 nlassert(false);
1008 /* EnterCriticalSection(&_CriticalSection);
1010 TSourceDSoundUserState old = _UserState;
1012 _UserState = NL_DSOUND_PAUSED;
1013 DBGPOS(("[%p] PAUZ: PAUSED", this));
1015 //nldebug ("NLOUND: pause %p", this);
1017 if (old == NL_DSOUND_PLAYING)
1019 fadeOut();
1022 LeaveCriticalSection(&_CriticalSection);
1026 // ******************************************************************
1028 bool CSourceDSound::isPlaying() const
1030 // return (_UserState == NL_DSOUND_PLAYING);
1031 return _State == source_playing || _State == source_swap_pending;
1035 // ******************************************************************
1037 bool CSourceDSound::isPaused() const
1039 // TODO
1040 nlassert(false);
1041 return false;
1042 // return (_UserState == NL_DSOUND_PAUSED);
1046 // ******************************************************************
1048 bool CSourceDSound::isStopped() const
1050 return _State == source_silencing || _State == source_stopped;
1051 // return (_UserState == NL_DSOUND_STOPPED);
1055 // ******************************************************************
1057 bool CSourceDSound::needsUpdate()
1059 return _State == source_silencing || _State == source_playing || _State == source_swap_pending;
1064 // ******************************************************************
1066 bool CSourceDSound::update()
1068 H_AUTO(NLSOUND_SourceDSoundUpdate)
1069 bool updateDone = false;
1071 EnterCriticalSection(&_CriticalSection);
1073 // fill some data into the buffer
1074 TCursors cursors;
1075 getCursors(cursors);
1076 uint32 writeSize;
1078 // The total size available for fill (between fillOffset and play cusors)
1079 uint32 fillSize = (cursors.PlayCursor - _FillOffset) & _SizeMask;
1080 // The play margin (between play and write cursor)
1081 uint32 margin = (cursors.WriteCursor - cursors.PlayCursor) & _SizeMask;
1082 // The number of sample played since previous update
1083 uint32 samplePlayed = ((cursors.PlayCursor - _LastPlayPos) & _SizeMask) / 2;
1084 _LastPlayPos = cursors.PlayCursor;
1087 // if (_FillOffset < cursors.WriteCursor && _FillOffset >cursors.PlayCursor)
1088 if (fillSize + margin > _SecondaryBufferSize)
1090 // arg !
1091 nlwarning("FillOffset is between play and write cursor (P = %p F = %p W = %p!", cursors.PlayCursor, _FillOffset, cursors.WriteCursor);
1092 _FillOffset = cursors.WriteCursor;
1093 _FillOffset = (_FillOffset+3) & 0xfffffffC;
1094 _SilenceWriten = 0;
1095 nlassert(_FillOffset < _SecondaryBufferSize);
1098 // advance of the fill offset over the write cursor
1099 uint32 advance = (_FillOffset - cursors.WriteCursor) & _SizeMask;
1101 /* nldebug("Play = %p, Write = %p, Fill = %p, FillSize = %p, Margin = %p, Advance = %p",
1102 cursors.PlayCursor, cursors.WriteCursor, _FillOffset, fillSize, margin, advance);
1104 if (advance > _UpdateCopySize)
1106 // enougth data wrote, wait until next update
1107 cursors.WriteSize = 0;
1109 /* if (cursors.WriteSize)
1111 if (_FillOffset < cursors.WriteCursor)
1113 if (_FillOffset > cursors.WriteCursor + _UpdateCopySize - _SecondaryBufferSize )
1115 // nlwarning("Enougth data wrote");
1116 // already fill enough data
1117 cursors.WriteSize = 0;
1120 else
1122 if (_FillOffset > cursors.WriteCursor + _UpdateCopySize)
1124 // nlwarning("Enougth data wrote");
1125 // already fill enough data
1126 cursors.WriteSize = 0;
1131 // nldebug("Cursors : Play = %p, Write = %p, Fill = %p", cursors.PlayCursor, cursors.WriteCursor, _FillOffset);
1133 cursors.WriteCursor = _FillOffset;
1134 writeSize = cursors.WriteSize; // compute the real played sample offset
1136 // update the real number of played sample
1137 if (_State == source_playing)
1138 _PlayOffset += samplePlayed;
1140 if (writeSize >= _UpdateCopySize)
1142 // nldebug("Update %p bytes", _UpdateCopySize);
1143 writeSize = _UpdateCopySize;
1144 updateDone = true;
1145 TLockedBufferInfo lbi;
1146 if (lock(_FillOffset, writeSize, lbi))
1148 TLockedBufferInfo unlockInfo(lbi);
1149 if (_State == source_playing)
1151 nlassert(_SampleOffset <= _SampleSize);
1152 uint32 updateSize = min(_SampleSize - _SampleOffset, uint(writeSize/2));
1153 updateSize &= 0xfffffffe;
1154 fillData(lbi, updateSize);
1155 advanceFill(lbi, updateSize);
1156 writeSize -= updateSize*2;
1158 if (_Loop)
1160 while (_PlayOffset >= _SampleSize)
1161 _PlayOffset -= _SampleSize;
1163 // repeat until we have at least 2 sample to write
1164 while (writeSize >= 4)
1166 if (_SampleOffset == _SampleSize)
1168 // rewind the sample
1169 _SampleOffset = 0;
1170 _ADPCMState.PreviousSample = 0;
1171 _ADPCMState.StepIndex = 0;
1174 updateSize = min(_SampleSize - _SampleOffset, uint(writeSize/2));
1175 updateSize &= 0xfffffffe;
1176 fillData(lbi, updateSize);
1177 advanceFill(lbi, updateSize);
1178 writeSize -= updateSize*2;
1181 else
1183 if (_SampleOffset == _SampleSize)
1185 fillSilence(lbi, writeSize/2);
1186 advanceFill(lbi, writeSize/2);
1187 _SilenceWriten += writeSize;
1189 if (_PlayOffset >= _SampleSize)
1191 // all the sample is played, no we are in silencing state !
1192 _PlayOffset = _SampleSize;
1193 _State = source_silencing;
1198 else if (_State == source_swap_pending)
1200 // the sample has been changed but not replayed yet ? so we 'stop' the old buffer
1202 fadeOut(lbi);
1203 advanceFill(lbi, _XFadeSize);
1204 writeSize -= _XFadeSize*2;
1205 fillSilence(lbi, writeSize/2);
1206 advanceFill(lbi, writeSize/2);
1207 _SilenceWriten = writeSize;
1208 _State = source_silencing;
1210 else if (_State == source_silencing)
1212 // write silence into the buffer.
1213 uint32 updateSize = min(writeSize, _SecondaryBufferSize - _SilenceWriten);
1214 updateSize &= 0xfffffffC;
1215 fillSilence(lbi, updateSize/2);
1216 advanceFill(lbi, updateSize/2);
1217 _SilenceWriten += updateSize;
1219 if (_SilenceWriten == _SecondaryBufferSize)
1220 _State = source_stopped;
1222 else
1224 nlwarning("Update not needed !");
1227 unlock(unlockInfo);
1231 LeaveCriticalSection(&_CriticalSection);
1233 return updateDone;
1236 // ******************************************************************
1238 bool CSourceDSound::update2()
1240 bool res = false;
1244 INITTIME(start);
1247 // Enter critical region
1249 EnterCriticalSection(&_CriticalSection);
1252 switch (_SecondaryBufferState)
1256 case NL_DSOUND_SILENCED:
1258 // Just pretend were writing silence by advancing the next
1259 // write position.
1260 DWORD playPos, writePos;
1261 uint32 space;
1263 _SecondaryBuffer->GetCurrentPosition(&playPos, &writePos);
1265 if (playPos == _NextWritePos)
1267 LeaveCriticalSection(&_CriticalSection);
1268 return false;
1271 space = getWritePosAndSpace(_NextWritePos, playPos, writePos, _SecondaryBufferSize);
1273 // not enougth space available
1274 if (space < _UpdateCopySize)
1275 break;
1277 _NextWritePos += _UpdateCopySize;
1279 if (_NextWritePos >= _SecondaryBufferSize)
1281 _NextWritePos -= _SecondaryBufferSize;
1284 break;
1287 case NL_DSOUND_FILLING:
1288 res = fill();
1289 break;
1292 case NL_DSOUND_SILENCING:
1293 res = silence();
1294 break;
1300 // Leave critical region
1302 LeaveCriticalSection(&_CriticalSection);
1305 #if NLSOUND_PROFILE
1306 double dt = CTime::ticksToSecond(CTime::getPerformanceTime() - start);;
1307 _TotalUpdateTime += dt;
1308 _MaxUpdateTime = (dt > _MaxUpdateTime) ? dt : _MaxUpdateTime;
1309 _MinUpdateTime = (dt < _MinUpdateTime) ? dt : _MinUpdateTime;
1310 _UpdateCount++;
1311 #endif
1313 return res;
1318 // ******************************************************************
1320 void CSourceDSound::setPos( const NLMISC::CVector& pos, bool deferred )
1322 _Pos = pos;
1323 // Coordinate system: conversion from NeL to OpenAL/GL:
1324 if (_3DBuffer != NULL)
1326 if (_3DBuffer->SetPosition(pos.x, pos.z, pos.y, deferred ? DS3D_DEFERRED : DS3D_IMMEDIATE) != DS_OK)
1328 nlwarning ("SetPosition failed");
1330 else
1332 //nlwarning ("%p set source NEL(p:%.2f/%.2f/%.2f) DS(p:%.2f/%.2f/%.2f)", this, pos.x, pos.y, pos.z, pos.x, pos.z, pos.y);
1338 // ******************************************************************
1340 const NLMISC::CVector &CSourceDSound::getPos() const
1342 return _Pos;
1346 // ******************************************************************
1348 void CSourceDSound::setVelocity( const NLMISC::CVector& vel, bool deferred )
1350 if (_3DBuffer != NULL)
1352 if (_3DBuffer->SetVelocity(vel.x, vel.z, vel.y, deferred ? DS3D_DEFERRED : DS3D_IMMEDIATE) != DS_OK)
1354 nlwarning ("SetVelocity failed");
1360 // ******************************************************************
1362 void CSourceDSound::getVelocity( NLMISC::CVector& vel ) const
1364 if (_3DBuffer != NULL)
1366 D3DVECTOR v;
1368 if (_3DBuffer->GetVelocity(&v) != DS_OK)
1370 nlwarning ("GetVelocity failed");
1371 vel.set(0, 0, 0);
1373 else
1375 vel.set(v.x, v.z, v.y);
1378 else
1380 vel.set(0, 0, 0);
1385 // ******************************************************************
1387 void CSourceDSound::setDirection( const NLMISC::CVector& dir )
1389 if (_3DBuffer != 0)
1391 if (_3DBuffer->SetConeOrientation(dir.x, dir.z, dir.y, DS3D_DEFERRED) != DS_OK)
1393 nlwarning ("SetConeOrientation failed (x=%.2f, y=%.2f, z=%.2f)", dir.x, dir.y, dir.z);
1395 else
1397 //nlwarning ("NLSOUND: %p set source direction NEL(p:%.2f/%.2f/%.2f) DS(p:%.2f/%.2f/%.2f)", this, dir.x, dir.y, dir.z, dir.x, dir.z, dir.y);
1403 // ******************************************************************
1405 void CSourceDSound::getDirection( NLMISC::CVector& dir ) const
1407 if (_3DBuffer != NULL)
1409 D3DVECTOR v;
1411 if (_3DBuffer->GetConeOrientation(&v) != DS_OK)
1413 nlwarning("GetConeOrientation failed");
1414 dir.set(0, 0, 1);
1416 else
1418 dir.set(v.x, v.z, v.y);
1421 else
1423 dir.set(0, 0, 1);
1428 // ******************************************************************
1430 void CSourceDSound::setGain( float gain )
1432 clamp(gain, 0.00001f, 1.0f);
1433 _Gain = gain;
1435 /* convert from linear amplitude to hundredths of decibels */
1436 _Volume = (uint32)(100.0 * 20.0 * log10(gain));
1437 clamp(_Volume, DSBVOLUME_MIN, DSBVOLUME_MAX);
1439 //nlwarning ("set gain %f vol %d", gain, _Volume);
1442 if ((_SecondaryBuffer != 0) && (_SecondaryBuffer->SetVolume(_Volume) != DS_OK))
1444 nlwarning("SetVolume failed");
1451 * Get the gain
1453 float CSourceDSound::getGain() const
1455 return _Gain;
1459 // ******************************************************************
1461 void CSourceDSound::setPitch( float coeff )
1463 // _Freq = coeff;
1465 if ((_Sample != 0) && (_SecondaryBuffer != 0))
1467 TSampleFormat format;
1468 uint freq;
1470 _Sample->getFormat(format, freq);
1472 _SampleRate = (uint32) (coeff * (float) freq);
1474 //nlwarning("Freq=%d", newfreq);
1476 if (_SecondaryBuffer->SetFrequency(_SampleRate) != DS_OK)
1478 // nlwarning("SetFrequency failed (buffer freq=%d, NeL freq=%.5f, DSound freq=%d)", freq, coeff, newfreq);
1479 nlwarning("SetFrequency");
1485 // ******************************************************************
1487 float CSourceDSound::getPitch() const
1489 if ((_Sample != 0) && (_SecondaryBuffer != 0))
1491 TSampleFormat format;
1492 uint freq0;
1493 DWORD freq;
1495 _Sample->getFormat(format, freq0);
1497 if (_SecondaryBuffer->GetFrequency(&freq) != DS_OK)
1499 nlwarning("GetFrequency failed");
1500 return 1.0;
1503 return ((float) freq / (float) freq0);
1506 return 1.0;
1510 // ******************************************************************
1512 void CSourceDSound::setSourceRelativeMode( bool mode )
1514 if (_3DBuffer != 0)
1516 HRESULT hr;
1518 if (mode)
1520 hr = _3DBuffer->SetMode(DS3DMODE_HEADRELATIVE, DS3D_IMMEDIATE);
1522 else
1524 hr = _3DBuffer->SetMode(DS3DMODE_NORMAL, DS3D_IMMEDIATE);
1527 // cache
1528 if (hr == DS_OK)
1529 _PosRelative= mode;
1530 else
1531 nlwarning("SetMode failed");
1533 else
1535 nlwarning("Requested setSourceRelativeMode on a non-3D source");
1540 // ******************************************************************
1542 bool CSourceDSound::getSourceRelativeMode() const
1544 return _PosRelative;
1548 // ******************************************************************
1549 void CSourceDSound::setMinMaxDistances( float mindist, float maxdist, bool deferred )
1551 if (_3DBuffer != 0)
1553 if (_3DBuffer->SetMinDistance(std::max(DS3D_DEFAULTMINDISTANCE, mindist), deferred ? DS3D_DEFERRED : DS3D_IMMEDIATE) != DS_OK)
1555 nlwarning("SetMinDistance (%f) failed", mindist);
1557 if (_3DBuffer->SetMaxDistance(std::min(DS3D_DEFAULTMAXDISTANCE, maxdist), deferred ? DS3D_DEFERRED : DS3D_IMMEDIATE) != DS_OK)
1559 nlwarning("SetMaxDistance (%f) failed", maxdist);
1562 else
1564 nlwarning("Requested setMinMaxDistances on a non-3D source");
1569 // ******************************************************************
1571 void CSourceDSound::getMinMaxDistances( float& mindist, float& maxdist ) const
1573 if (_3DBuffer != 0)
1575 D3DVALUE min, max;
1577 if ((_3DBuffer->GetMinDistance(&min) != DS_OK)
1578 || (_3DBuffer->GetMaxDistance(&max) != DS_OK))
1580 mindist = 0.0f;
1581 maxdist = 0.0f;
1582 nlwarning("GetMinDistance or GetMaxDistance failed");
1584 else
1586 mindist = min;
1587 maxdist = max;
1590 else
1592 mindist = 0.0f;
1593 maxdist = 0.0f;
1594 nlwarning("Requested getMinMaxDistances on a non-3D source");
1598 // ******************************************************************
1599 void CSourceDSound::updateVolume( const NLMISC::CVector& listener )
1601 if (!CSoundDriverDSound::instance()->getOption(ISoundDriver::OptionManualRolloff))
1603 // API controlled rolloff => return (just set the volume)
1604 _SecondaryBuffer->SetVolume(_Volume);
1606 else // manual rolloff
1608 CVector pos = getPos();
1609 // make relative to listener (if not already!)
1610 if(!_PosRelative)
1611 pos -= listener;
1612 float sqrdist = pos.sqrnorm();
1614 float mindist, maxdist;
1615 getMinMaxDistances(mindist, maxdist);
1617 // attenuate the volume according to distance and alpha
1618 sint32 volumeDB = ISource::computeManualRollOff(_Volume, DSBVOLUME_MIN, DSBVOLUME_MAX, _Alpha, sqrdist, mindist, maxdist);
1620 // set attenuated volume
1621 _SecondaryBuffer->SetVolume(volumeDB);
1625 // ******************************************************************
1627 void CSourceDSound::setCone( float innerAngle, float outerAngle, float outerGain )
1629 if (_3DBuffer != 0)
1631 // Set the cone angles
1633 // Convert from radians to degrees
1634 DWORD inner = (DWORD)(180.0 * innerAngle / Pi);
1635 DWORD outer = (DWORD)(180.0 * outerAngle / Pi);
1638 // Sanity check: wrap the angles in the [0,360] interval
1639 if (outer < inner)
1641 outer = inner;
1644 while (inner < DS3D_MINCONEANGLE)
1646 inner += 360;
1649 while (inner > DS3D_MAXCONEANGLE)
1651 inner -= 360;
1654 while (outer < DS3D_MINCONEANGLE)
1656 outer += 360;
1659 while (outer > DS3D_MAXCONEANGLE)
1661 outer -= 360;
1664 if (_3DBuffer->SetConeAngles(inner, outer, DS3D_DEFERRED) != DS_OK)
1666 nlwarning("SetConeAngles failed");
1669 // Set the outside volume
1670 if (outerGain < 0.00001f)
1672 outerGain = 0.00001f;
1675 // convert from linear amplitude to hundredths of decibels
1676 LONG volume = (LONG)(100.0 * 20.0 * log10(outerGain));
1678 if (volume < DSBVOLUME_MIN)
1680 volume = DSBVOLUME_MIN;
1682 else if (volume > DSBVOLUME_MAX)
1684 volume = DSBVOLUME_MAX;
1687 if (_3DBuffer->SetConeOutsideVolume(volume, DS3D_DEFERRED) != DS_OK)
1689 nlwarning("SetConeOutsideVolume failed");
1693 else
1695 nlwarning("Requested setCone on a non-3D source");
1699 // ******************************************************************
1701 void CSourceDSound::getCone( float& innerAngle, float& outerAngle, float& outerGain ) const
1703 if (_3DBuffer != 0)
1705 DWORD inner, outer;
1706 LONG volume;
1708 if (_3DBuffer->GetConeAngles(&inner, &outer) != DS_OK)
1710 nlwarning("GetConeAngles failed");
1711 innerAngle = outerAngle = (float)(2.0 * Pi);
1713 else
1715 innerAngle = (float)(Pi * inner / 180.0);
1716 outerAngle = (float)(Pi * outer / 180.0);
1719 if (_3DBuffer->GetConeOutsideVolume(&volume) != DS_OK)
1721 nlwarning("GetConeOutsideVolume failed");
1722 outerGain = 0.0f;
1724 else
1726 outerGain = (float) pow((double)10, (double) volume / 20.0 / 100.0);
1729 else
1731 nlwarning("Requested getCone on a non-3D source");
1735 // ******************************************************************
1737 //void CSourceDSound::setEAXProperty( uint prop, void *value, uint valuesize )
1739 //#if EAX_AVAILABLE == 1
1740 // if (_EAXSource == 0)
1741 // {
1742 // _EAXSource = CSoundDriverDSound::instance()->createPropertySet(this);
1743 // }
1744 // if ( _EAXSource != NULL )
1745 // {
1746 // H_AUTO(NLSOUND_EAXPropertySet_Set)
1747 // HRESULT res = _EAXSource->Set( DSPROPSETID_EAX_BufferProperties, prop, NULL, 0, value, valuesize );
1748 // if (res != S_OK)
1749 // {
1750 //// nlwarning("Setting EAX Param #%u fail : %x", prop, res);
1751 // }
1752 // }
1753 //#endif
1756 /** Set the alpha value for the volume-distance curve
1758 * Useful only with OptionManualRolloff. value from -1 to 1 (default 0)
1760 * alpha.0: the volume will decrease linearly between 0dB and -100 dB
1761 * alpha = 1.0: the volume will decrease linearly between 1.0 and 0.0 (linear scale)
1762 * alpha = -1.0: the volume will decrease inversely with the distance (1/dist). This
1763 * is the default used by DirectSound/OpenAL
1765 * For any other value of alpha, an interpolation is be done between the two
1766 * adjacent curves. For example, if alpha equals 0.5, the volume will be halfway between
1767 * the linear dB curve and the linear amplitude curve.
1769 void CSourceDSound::setAlpha(double a)
1771 _Alpha = a;
1774 /// Enable or disable direct output [true/false], default: true
1775 void CSourceDSound::setDirect(bool /* enable */)
1780 /// Return if the direct output is enabled
1781 bool CSourceDSound::getDirect() const
1783 return true;
1786 /// Set the gain for the direct path
1787 void CSourceDSound::setDirectGain(float /* gain */)
1792 /// Get the gain for the direct path
1793 float CSourceDSound::getDirectGain() const
1795 return NLSOUND_DEFAULT_DIRECT_GAIN;
1798 /// Enable or disable the filter for the direct channel
1799 void CSourceDSound::enableDirectFilter(bool /* enable */)
1804 /// Check if the filter on the direct channel is enabled
1805 bool CSourceDSound::isDirectFilterEnabled() const
1807 return false;
1810 /// Set the filter parameters for the direct channel
1811 void CSourceDSound::setDirectFilter(TFilter /*filterType*/, float /*lowFrequency*/, float /*highFrequency*/, float /*passGain*/)
1816 /// Get the filter parameters for the direct channel
1817 void CSourceDSound::getDirectFilter(TFilter &filterType, float &lowFrequency, float &highFrequency, float &passGain) const
1819 filterType = FilterLowPass;
1820 lowFrequency = NLSOUND_DEFAULT_FILTER_PASS_LF;
1821 highFrequency = NLSOUND_DEFAULT_FILTER_PASS_HF;
1822 passGain = NLSOUND_DEFAULT_FILTER_PASS_GAIN;
1825 /// Set the direct filter gain
1826 void CSourceDSound::setDirectFilterPassGain(float /*passGain*/)
1831 /// Get the direct filter gain
1832 float CSourceDSound::getDirectFilterPassGain() const
1834 return 0.0f;
1837 /// Set the effect send for this source, NULL to disable. [IEffect], default: NULL
1838 void CSourceDSound::setEffect(IReverbEffect * /* reverbEffect */)
1843 /// Get the effect send for this source
1844 IEffect *CSourceDSound::getEffect() const
1846 return NULL;
1849 /// Set the gain for the effect path
1850 void CSourceDSound::setEffectGain(float /* gain */)
1855 /// Get the gain for the effect path
1856 float CSourceDSound::getEffectGain() const
1858 return NLSOUND_DEFAULT_EFFECT_GAIN;
1861 /// Enable or disable the filter for the effect channel
1862 void CSourceDSound::enableEffectFilter(bool /* enable */)
1867 /// Check if the filter on the effect channel is enabled
1868 bool CSourceDSound::isEffectFilterEnabled() const
1870 return false;
1873 /// Set the filter parameters for the effect channel
1874 void CSourceDSound::setEffectFilter(TFilter /*filterType*/, float /*lowFrequency*/, float /*highFrequency*/, float /*passGain*/)
1879 /// Get the filter parameters for the effect channel
1880 void CSourceDSound::getEffectFilter(TFilter &filterType, float &lowFrequency, float &highFrequency, float &passGain) const
1882 filterType = FilterLowPass;
1883 lowFrequency = NLSOUND_DEFAULT_FILTER_PASS_LF;
1884 highFrequency = NLSOUND_DEFAULT_FILTER_PASS_HF;
1885 passGain = NLSOUND_DEFAULT_FILTER_PASS_GAIN;
1888 /// Set the effect filter gain
1889 void CSourceDSound::setEffectFilterPassGain(float /*passGain*/)
1894 /// Get the effect filter gain
1895 float CSourceDSound::getEffectFilterPassGain() const
1897 return 0.0f;
1900 // ******************************************************************
1902 IBuffer *CSourceDSound::getBuffer()
1904 return _Sample;
1908 // ******************************************************************
1910 bool CSourceDSound::lock(uint32 offset, uint32 size, TLockedBufferInfo &lbi)
1912 HRESULT hr = _SecondaryBuffer->Lock(offset, size, (LPVOID*) &lbi.Ptr1, (DWORD*) &lbi.Size1, (LPVOID*) &lbi.Ptr2, (DWORD*) &lbi.Size2, 0);
1914 if (hr == DSERR_BUFFERLOST)
1916 // If the buffer got lost somehow, try to restore it.
1917 if (FAILED(_SecondaryBuffer->Restore()))
1919 nlwarning("Lock failed (1)");
1920 return false;
1922 if (FAILED(_SecondaryBuffer->Lock(offset, size, (LPVOID*) &lbi.Ptr1, (DWORD*)&lbi.Size1, (LPVOID*) &lbi.Ptr2, (DWORD*)&lbi.Size2, 0)))
1924 nlwarning("Lock failed (2)");
1925 return false;
1928 else if (hr != DS_OK)
1930 nlwarning("Lock failed (3)");
1931 return false;
1934 return true;
1938 bool CSourceDSound::lock(uint32 writePos, uint32 size, uint8* &ptr1, DWORD &bytes1, uint8* &ptr2, DWORD &bytes2)
1940 HRESULT hr = _SecondaryBuffer->Lock(writePos, size, (LPVOID*) &ptr1, &bytes1, (LPVOID*) &ptr2, &bytes2, 0);
1942 if (hr == DSERR_BUFFERLOST)
1944 // If the buffer got lost somehow, try to restore it.
1945 if (FAILED(_SecondaryBuffer->Restore()))
1947 nlwarning("Lock failed (1)");
1948 return false;
1950 if (FAILED(_SecondaryBuffer->Lock(_NextWritePos, _UpdateCopySize, (LPVOID*) &ptr1, &bytes1, (LPVOID*) &ptr2, &bytes2, 0)))
1952 nlwarning("Lock failed (2)");
1953 return false;
1956 else if (hr != DS_OK)
1958 nlwarning("Lock failed (3)");
1959 return false;
1962 return true;
1965 // ******************************************************************
1967 bool CSourceDSound::unlock(const TLockedBufferInfo &lbi)
1969 _SecondaryBuffer->Unlock(lbi.Ptr1, lbi.Size1, lbi.Ptr2, lbi.Size2);
1970 return true;
1975 bool CSourceDSound::unlock(uint8* ptr1, DWORD bytes1, uint8* ptr2, DWORD bytes2)
1977 _SecondaryBuffer->Unlock(ptr1, bytes1, ptr2, bytes2);
1978 return true;
1982 void CSourceDSound::copySampleTo16BitsTrack(void *dst, void *src, uint nbSample, TSampleFormat sourceFormat)
1984 if (sourceFormat == Mono8 || sourceFormat == Stereo8)
1986 nlassert("8 bit mixing is not supported now !");
1987 return;
1988 // convert sample from 8 to 16 inside the dst buffer
1989 sint8 *psrc = (sint8*)src;
1990 sint16 *pdst = (sint16*)dst;
1991 sint8 *endSrc = psrc + nbSample;
1993 for (; psrc != endSrc; psrc += 2)
1995 // write the high word
1996 *pdst++ = sint16(*psrc++) * 256;
1999 if (sourceFormat == Mono16ADPCM)
2001 // unpack ADPCM data
2002 nldebug("Mixing %u samples in ADPCM", nbSample);
2003 IBuffer::decodeADPCM((uint8*)src, (sint16*)dst, nbSample, _ADPCMState);
2005 else
2007 // use the fastmem copy buffer
2008 CFastMem::memcpy(dst, src, nbSample*2);
2012 /***************************************************************************
2015 Buffer operations
2017 There are five buffer operation: fill, silence, fadeOut, crossFade
2018 and fadeIn. fill and silence are called by the update function. The
2019 others are called by the user state functions (play, stop, pause).
2022 NW P W
2023 +--------+------+---+-----------------------+
2024 |........| |xxx|.......................|
2025 +--------+------+---+-----------------------+
2027 The source maintains the next write position (_NextWritePos, NW in figure
2028 above). That is the position at which new samples or silemce is written.
2029 DirectSound maintaines a play cursor and a write cursor (P and W in figure).
2030 The data between P and W is scheduled for playing and cannot be touched.
2031 The data between W and NW are unplayed sample data that the source copied
2032 into the DirectSound buffer.
2034 The update functions (fill, silence) refresh the buffer with new samples
2035 or silence. That insert the sample data at the next write position NW. This
2036 write position is maintained as closely behind the DirectSound play cursor
2037 as possible to keep the buffer filled with data.
2039 The user functions (the fades) modify the sample data that is right after
2040 the write cursor W maintained by DirectSound. The data has to be written
2041 after W to hear the changes as soon as possible. When a fade is done, the
2042 data already written in the buffer has to be overwritten. The function
2043 getFadeOutSize() helps to found out how many samples are writen between
2044 W and NW and to what section of the original audio data they correspond.
2046 All the buffer functions below have the same pattern:
2048 - get current play and write cursors (P and W)
2049 - lock the buffer
2050 - copy samples
2051 - unlock buffer
2052 - update state variables
2054 The differences between the functions are due to different operation
2055 (fades), position and size of the buffer to lock, handling of silence
2056 and looping.
2058 Enjoy!
2063 ************************************************************************/
2065 uint32 getWritePosAndSpace(uint32 &nextWritePos, uint32 playPos, uint32 writePos, uint32 bufferSize)
2067 uint32 space;
2068 if (playPos < writePos) //_NextWritePos)
2070 // the 'forbiden' interval is continuous
2071 if (nextWritePos > playPos && nextWritePos < writePos)
2073 // We have a problem here because our write pointer is in the forbiden zone
2074 // This is mainly due to cpu overload.
2075 nextWritePos = writePos;
2077 // space = playPos - _NextWritePos;
2079 else
2081 // The forbiden interval is wrapping
2082 if (nextWritePos > playPos || nextWritePos < writePos)
2084 // We have a problem here because our write pointer is in the forbiden zone
2085 // This is mainly due to cpu overload.
2086 nextWritePos = writePos;
2088 // space = _SecondaryBufferSize + playPos - _NextWritePos;
2091 // compute the available space to write to
2092 if (nextWritePos > playPos)
2094 space = bufferSize + playPos - nextWritePos;
2096 else
2098 space = playPos - nextWritePos;
2101 return space;
2105 bool CSourceDSound::fill()
2107 bool res = false;
2108 uint8 *ptr1, *ptr2;
2109 DWORD bytes1, bytes2;
2110 DWORD playPos, writePos;
2111 uint32 space;
2114 if (_Buffer == NULL)
2116 _SecondaryBufferState = NL_DSOUND_SILENCING;
2117 _UserState = NL_DSOUND_STOPPED;
2118 return false;
2121 if (_SecondaryBuffer == 0)
2123 return false;
2126 TSampleFormat sampleFormat;
2127 uint freq;
2128 _Buffer->getFormat(sampleFormat, freq);
2131 INITTIME(startPos);
2134 _SecondaryBuffer->GetCurrentPosition(&playPos, &writePos);
2136 if (playPos == _NextWritePos)
2138 return false;
2141 uint32 nextWritePos = _NextWritePos;
2142 space = getWritePosAndSpace(nextWritePos, playPos, writePos, _SecondaryBufferSize);
2144 // Don't bother if the number of samples that can be written is too small.
2145 if (space < _UpdateCopySize)
2147 return false;
2150 _NextWritePos = nextWritePos;
2152 uint8* data = ((CBufferDSound*) _Buffer)->getData();
2153 uint32 available = (_BytesWritten < _BufferSize) ? _BufferSize - _BytesWritten : 0;
2156 // The number of samples bytes that will be copied. If bytes is 0
2157 // than write silence to the buffer.
2158 uint32 bytes = std::min(_UpdateCopySize, available);
2159 // uint32 clear = _UpdateCopySize - available;
2162 // Lock the buffer
2164 INITTIME(startLock);
2167 if (!lock(_NextWritePos, _UpdateCopySize, ptr1, bytes1, ptr2, bytes2))
2169 return false;
2173 INITTIME(startCopy);
2175 // Start copying the samples
2178 if (bytes1 <= bytes) {
2180 // CFastMem::memcpy(ptr1, data + _BytesWritten, bytes1);
2181 copySampleTo16BitsTrack(ptr1, data + _BytesWritten, bytes1/2, sampleFormat);
2182 _BytesWritten += bytes1;
2183 bytes -= bytes1;
2185 if (ptr2)
2187 if (bytes > 0)
2189 // CFastMem::memcpy(ptr2, data + _BytesWritten, bytes);
2190 copySampleTo16BitsTrack(ptr2, data + _BytesWritten, bytes/2, sampleFormat);
2191 _BytesWritten += bytes;
2194 if (bytes < bytes2)
2196 if (_Loop)
2198 DBGPOS(("[%p] FILL: LOOP", this));
2200 //CFastMem::memcpy(ptr2 + bytes, data, bytes2 - bytes);
2201 copySampleTo16BitsTrack(ptr2 + bytes, data, (bytes2 - bytes)/2, sampleFormat);
2202 _BytesWritten = bytes2 - bytes;
2204 else
2206 memset(ptr2 + bytes, 0, bytes2 - bytes);
2207 _SilenceWritten = bytes2 - bytes;
2212 else
2214 if (bytes > 0)
2216 // CFastMem::memcpy(ptr1, data + _BytesWritten, bytes);
2217 copySampleTo16BitsTrack(ptr1, data + _BytesWritten, bytes/2, sampleFormat);
2218 _BytesWritten += bytes;
2221 if (_Loop)
2223 DBGPOS(("[%p] FILL: LOOP", this));
2225 // CFastMem::memcpy(ptr1 + bytes, data, bytes1 - bytes);
2226 copySampleTo16BitsTrack(ptr1 + bytes, data, (bytes1 - bytes)/2, sampleFormat);
2227 _BytesWritten = bytes1 - bytes;
2229 if (ptr2)
2231 // CFastMem::memcpy(ptr2, data + _BytesWritten, bytes2);
2232 copySampleTo16BitsTrack(ptr2, data + _BytesWritten, bytes2 / 2, sampleFormat);
2233 _BytesWritten += bytes2;
2237 else
2239 memset(ptr1 + bytes, 0, bytes1 - bytes);
2240 _SilenceWritten = bytes1 - bytes;
2242 if (ptr2)
2244 memset(ptr2, 0, bytes2);
2245 _SilenceWritten += bytes2;
2254 INITTIME(startUnlock);
2256 // Unlock the buffer
2257 _SecondaryBuffer->Unlock(ptr1, bytes1, ptr2, bytes2);
2260 // Update the state variables
2262 // Check if we've reached the end of the file
2263 if (_BytesWritten == _BufferSize)
2265 if (_Loop)
2267 // If we're looping, start all over again
2268 DBGPOS(("[%p] FILL: LOOP", this));
2269 _BytesWritten = 0;
2271 else
2273 _SecondaryBufferState = NL_DSOUND_SILENCING;
2275 // Keep track of where tha last sample was written and the position
2276 // of the play cursor relative to the end position. if the _EndState
2277 // is 0, the play cursor is after the end position, 1 otherwise.
2278 _EndPosition = writePos + bytes;
2279 if (_EndPosition >= _SecondaryBufferSize)
2281 _EndPosition -= _SecondaryBufferSize;
2284 _EndState = (playPos > _EndPosition)? NL_DSOUND_TAIL1 : NL_DSOUND_TAIL2;
2286 DBGPOS(("[%p] FILL: SILENCING", this));
2287 DBGPOS(("[%p] FILL: ENDSTATE=%d, E=%d, P=%d", this, (int) _EndState, _EndPosition, playPos));
2292 // Update the write pointer
2293 _NextWritePos += bytes1 + bytes2;
2294 if (_NextWritePos >= _SecondaryBufferSize)
2296 _NextWritePos -= _SecondaryBufferSize;
2299 DBGPOS(("[%p] FILL: P=%d, W=%d, NW=%d, SZ=%d, BW=%d, S=%d, B=%d", this, playPos, writePos, _NextWritePos, _BufferSize, _BytesWritten, _SilenceWritten, bytes1 + bytes2));
2302 #if NLSOUND_PROFILE
2303 _TotalUpdateSize += bytes1 + bytes2;
2304 _PosTime += CTime::ticksToSecond(startLock - startPos);
2305 _LockTime += CTime::ticksToSecond(startCopy - startLock);
2306 _CopyTime += CTime::ticksToSecond(startUnlock - startCopy);
2307 _UnlockTime += CTime::ticksToSecond(CTime::getPerformanceTime() - startUnlock);
2308 _CopyCount++;
2309 #endif
2312 return true;
2318 // ******************************************************************
2320 bool CSourceDSound::silence()
2322 uint8 *ptr1, *ptr2;
2323 DWORD bytes1, bytes2;
2324 DWORD playPos, writePos;
2325 uint32 space;
2327 if (_SecondaryBuffer == 0)
2329 return false;
2332 INITTIME(startPos);
2334 _SecondaryBuffer->GetCurrentPosition(&playPos, &writePos);
2336 if (playPos == _NextWritePos)
2338 return false;
2341 space = getWritePosAndSpace(_NextWritePos, playPos, writePos, _SecondaryBufferSize);
2343 /* else if (playPos > _NextWritePos)
2345 space = playPos - _NextWritePos;
2347 else
2349 space = _SecondaryBufferSize + playPos - _NextWritePos;
2352 /* // Don't bother if the number of samples that can be written is too small.
2353 if (space < _UpdateCopySize)
2355 return false;
2358 // Lock the buffer
2360 INITTIME(startLock);
2362 if (!lock(_NextWritePos, _UpdateCopySize, ptr1, bytes1, ptr2, bytes2))
2364 return false;
2368 INITTIME(startCopy);
2370 // Silence the buffer
2371 memset(ptr1, 0, bytes1);
2372 memset(ptr2, 0, bytes2);
2374 // Unlock the buffer
2376 INITTIME(startUnlock);
2378 _SecondaryBuffer->Unlock(ptr1, bytes1, ptr2, bytes2);
2381 // Update the next write position
2382 _NextWritePos += bytes1 + bytes2;
2383 if (_NextWritePos >= _SecondaryBufferSize)
2385 _NextWritePos -= _SecondaryBufferSize;
2389 // Check if all the samples in the buffer are played
2390 if ((playPos > _EndPosition) && (_EndState == NL_DSOUND_TAIL2))
2392 // The buffer played passed the last sample. Flag the source as stopped.
2393 _EndState = NL_DSOUND_ENDED;
2394 DBGPOS(("[%p] SLNC: ENDED", this));
2396 // If the buffer was playing, mark it as stopped
2397 if (_UserState == NL_DSOUND_PLAYING)
2399 _UserState = NL_DSOUND_STOPPED;
2400 DBGPOS(("[%p] SLNC: STOPPED", this));
2403 else if ((playPos < _EndPosition) && (_EndState == NL_DSOUND_TAIL1))
2405 // The play cursor wrapped around the buffer and is now before the end position
2406 _EndState = NL_DSOUND_TAIL2;
2407 DBGPOS(("[%p] FILL: ENDSTATE=%d, E=%d, P=%d", this, (int) _EndState, _EndPosition, playPos));
2411 // Update the amount of silence written
2412 _SilenceWritten += bytes1 + bytes2;
2413 if (_SilenceWritten >= _SecondaryBufferSize)
2415 // The buffer is now completely filled with silence
2416 _SecondaryBufferState = NL_DSOUND_SILENCED;
2417 DBGPOS(("[%p] SLNC: SILENCED", this));
2419 // If the buffer was playing, mark it as stopped
2420 if (_UserState == NL_DSOUND_PLAYING)
2422 _UserState = NL_DSOUND_STOPPED;
2423 DBGPOS(("[%p] SLNC: STOPPED*", this));
2427 DBGPOS(("[%p] SLNC: P=%d, W=%d, NW=%d, SZ=%d, BW=%d, S=%d, B=%d", this, playPos, writePos, _NextWritePos, _BufferSize, _BytesWritten, _SilenceWritten, bytes1 + bytes2));
2430 #if NLSOUND_PROFILE
2431 _TotalUpdateSize += bytes1 + bytes2;
2432 _PosTime += CTime::ticksToSecond(startLock - startPos);
2433 _LockTime += CTime::ticksToSecond(startCopy - startLock);
2434 _CopyTime += CTime::ticksToSecond(startUnlock - startCopy);
2435 _UnlockTime += CTime::ticksToSecond(CTime::getPerformanceTime() - startUnlock);
2436 _CopyCount++;
2437 #endif
2439 return true;
2443 // ******************************************************************
2446 * Calculate the size of the crossfade and set the pointer in the sample buffer
2447 * to the sample that comes after the write cursor in the DirectSound buffer.
2448 * This method also returns the number of samples that have been written after
2449 * the write cursor.
2453 void CSourceDSound::getFadeOutSize(uint32 writePos, uint32 &xfadeSize, sint16* &in1, uint32 &writtenTooMuch)
2455 // The number of samples over which we will do the crossfade
2456 xfadeSize = _XFadeSize;
2459 // The tricky part of this method is to figger out how many samples of the old
2460 // buffer were written after the write cursor and figger our with what position
2461 // in the old buffer the write cursor corresponds. We have to consider the following
2462 // cases:
2464 // - the old buffer just looped,
2465 // - the old buffer is finished, but stil has samples in the DirectSound buffer
2466 // - the default case, i.e. the old buffer is playing somewhere in the middle.
2469 // How many bytes did we write after the write position?
2471 writtenTooMuch = NLSOUND_DISTANCE(writePos, _NextWritePos, _SecondaryBufferSize);
2473 DBGPOS(("[%p] FADE: TOO=%d", this, writtenTooMuch));
2476 uint8* data = ((CBufferDSound*) _Buffer)->getData();
2478 // If the sound is finished and all the samples are written in the
2479 // buffer, it's possible that there are still samples after the write
2480 // position. If this is the case, we have to do a fade out. If there is
2481 // only silence, we only need to copy without fade.
2483 if (_SilenceWritten > 0)
2486 if (writtenTooMuch > _SilenceWritten)
2488 writtenTooMuch -= _SilenceWritten;
2489 in1 = (sint16*) (data + _BufferSize - writtenTooMuch) ;
2491 if (writtenTooMuch < 2 * xfadeSize)
2493 xfadeSize = writtenTooMuch / 2;
2496 else
2498 xfadeSize = 0;
2501 DBGPOS(("[%p] FADE: END, TOO=%d, S=%d, F=%d", this, writtenTooMuch, _SilenceWritten, xfadeSize));
2504 // If the sound looped, it's possible that the number of samples
2505 // written is small. In that case, the write cursor is still inside
2506 // the previous loop. All we have to do is fade out the last part
2507 // of the previous loop.
2509 else if (writtenTooMuch >= _BytesWritten)
2512 writtenTooMuch -= _BytesWritten;
2514 in1 = (sint16*) (data + _BufferSize - writtenTooMuch) ;
2516 if (writtenTooMuch < 2 * xfadeSize)
2518 xfadeSize = writtenTooMuch / 2;
2521 DBGPOS(("[%p] FADE: LOOPED, TOO=%d, F=%d", this, writtenTooMuch, xfadeSize));
2525 // This is the default case. Simply fade from the previous to the next buffer.
2526 // The variable writtenTooMuch tells us how much of the previous sound has
2527 // been written after the current write cursor. We just have to check there
2528 // are enough samples available for the fade.
2530 else
2532 in1 = (sint16*) (data + (sint32) _BytesWritten - writtenTooMuch);
2534 if (xfadeSize > _BufferSize - _BytesWritten)
2536 xfadeSize = _BufferSize - _BytesWritten;
2539 DBGPOS(("[%p] FADE: STD, TOO=%d, F=%d", this, writtenTooMuch, xfadeSize));
2545 // ******************************************************************
2547 void CSourceDSound::crossFade()
2549 uint8 *ptr1, *ptr2;
2550 DWORD bytes1, bytes2;
2551 DWORD playPos, writePos;
2552 uint32 i;
2556 if (_Buffer == NULL)
2558 return;
2561 if (_SecondaryBuffer == 0)
2563 return;
2567 INITTIME(start);
2570 EnterCriticalSection(&_CriticalSection);
2572 TSampleFormat sampleFormat;
2573 uint freq;
2574 _Buffer->getFormat(sampleFormat, freq);
2577 // The source is currently playing another buffer. We will do a hot
2578 // swap between the old and the new buffer. DirectSound maintains two
2579 // cursors into the buffer: the play cursor and the write cursor.
2580 // The write cursor indicates where we can start writing the new samples.
2581 // To avoid clicks, we have to do a cross fade between the old buffer and
2582 // the new buffer.
2584 _SecondaryBuffer->GetCurrentPosition(&playPos, &writePos);
2587 // The number of bytes we will write to the DirectSound buffer
2588 uint32 bytes = _SwapCopySize;
2589 if (bytes > _SwapBuffer->getSize())
2591 bytes = _SwapBuffer->getSize();
2595 // Lock the DirectSound buffer
2597 if (FAILED(_SecondaryBuffer->Lock(writePos, bytes, (LPVOID*) &ptr1, &bytes1, (LPVOID*) &ptr2, &bytes2, 0)))
2599 LeaveCriticalSection(&_CriticalSection);
2600 throw ESoundDriver("Failed to lock the DirectSound secondary buffer");
2604 sint16* in1;
2605 uint8* data1 = ((CBufferDSound*) _Buffer)->getData();
2606 uint8* data2 = ((CBufferDSound*) _SwapBuffer)->getData();
2607 sint16* in2 = (sint16*) data2;
2608 sint16* out = (sint16*) ptr1;
2610 // The number of samples over which we will do the crossfade
2611 uint32 xfadeSize;
2612 uint32 xfadeByteSize;
2613 uint32 writtenTooMuch;
2615 getFadeOutSize(writePos, xfadeSize, in1, writtenTooMuch);
2616 xfadeByteSize = 2 * xfadeSize;
2618 #define MIX 1
2621 #if MIX
2622 float incr, amp1, amp2;
2624 if (xfadeSize == 0)
2626 amp1 = 0.0f;
2627 amp2 = 1.0f;
2628 incr = 0.0f;
2630 else
2632 amp1 = 1.0f;
2633 amp2 = 0.0f;
2634 incr = 1.0f / xfadeSize;
2637 #else
2638 float incr, amp1;
2640 if (xfadeSize == 0)
2642 amp1 = 0.0f;
2643 incr = 0.0f;
2645 else
2647 amp1 = 1.0f;
2648 incr = 1.0f / xfadeSize;
2651 #endif
2654 // Start copying the samples
2657 // In the first case, the cross fade is completely contained in the first buffer
2658 // pointed to by ptr1.
2659 if (xfadeByteSize < bytes1)
2662 // Do cross fade
2664 for (i = 0; i < xfadeSize; i++)
2666 #if MIX
2667 out[i] = (sint16) (amp1 * in1[i] + amp2 * in2[i]);
2668 amp1 -= incr;
2669 amp2 += incr;
2670 #else
2671 out[i] = (sint16) (amp1 * in1[i]);
2672 amp1 -= incr;
2673 #endif
2676 // Copy remaining samples
2678 #if MIX
2679 // CFastMem::memcpy(ptr1 + xfadeByteSize, data2 + xfadeByteSize, bytes1 - xfadeByteSize);
2680 copySampleTo16BitsTrack(ptr1 + xfadeByteSize, data2 + xfadeByteSize, (bytes1 - xfadeByteSize)/2, sampleFormat);
2682 _BytesWritten = bytes1;
2683 #else
2684 // CFastMem::memcpy(ptr1 + xfadeByteSize, data2, bytes1 - xfadeByteSize);
2685 copySampleTo16BitsTrack(ptr1 + xfadeByteSize, data2, (bytes1 - xfadeByteSize)/2, sampleFormat);
2686 _BytesWritten = bytes1 - xfadeByteSize;
2687 #endif
2689 if (ptr2)
2691 //CFastMem::memcpy(ptr2, data2 + _BytesWritten, bytes2);
2692 copySampleTo16BitsTrack(ptr2, data2 + _BytesWritten, bytes2/2, sampleFormat);
2693 _BytesWritten += bytes2;
2698 // In the second case, the cross fade stretches over the first and the second buffers.
2699 else
2702 uint32 fade1 = bytes1 / 2;
2703 uint32 fade2 = xfadeSize - fade1;
2705 // Do cross fade
2707 // Part 1, start at ptr1
2708 for (i = 0; i < fade1; i++)
2710 #if MIX
2711 out[i] = (sint16) (amp1 * in1[i] + amp2 * in2[i]);
2712 amp1 -= incr;
2713 amp2 += incr;
2714 #else
2715 out[i] = (sint16) (amp1 * in1[i]);
2716 amp1 -= incr;
2717 #endif
2719 #if MIX
2720 _BytesWritten = bytes1;
2721 #else
2722 _BytesWritten = 0;
2723 #endif
2726 if (ptr2)
2728 out = (sint16*) ptr2;
2730 // Part 2, ontinue at ptr2
2731 for (uint32 k = 0; i < xfadeSize; i++, k++)
2733 #if MIX
2734 out[k] = (sint16) (amp1 * in1[i] + amp2 * in2[i]);
2735 amp1 -= incr;
2736 amp2 += incr;
2737 #else
2738 out[k] = (sint16) (amp1 * in1[i]);
2739 amp1 -= incr;
2740 #endif
2743 // Copy remaining samples
2744 #if MIX
2745 // CFastMem::memcpy(ptr2 + 2 * k, data2 + _BytesWritten + 2 * k, bytes2 - 2 * k);
2746 copySampleTo16BitsTrack(ptr2 + 2 * k, data2 + _BytesWritten + 2 * k, (bytes2 - 2 * k)/2, sampleFormat);
2747 _BytesWritten += bytes2;
2748 #else
2749 //CFastMem::memcpy(ptr2 + 2 * k, data2, bytes2 - 2 * k);
2750 copySampleTo16BitsTrack(ptr2 + 2 * k, data2, (bytes2 - 2 * k)/2, sampleFormat);
2751 _BytesWritten = bytes2 - 2 * k;
2752 #endif
2757 // Unlock the DirectSound buffer
2758 _SecondaryBuffer->Unlock(ptr1, bytes1, ptr2, bytes2);
2761 // Update the state variables
2763 _SilenceWritten = 0;
2765 _NextWritePos = (writePos + bytes1 + bytes2);
2766 if (_NextWritePos >= _SecondaryBufferSize)
2768 _NextWritePos -= _SecondaryBufferSize;
2773 _SecondaryBufferState = NL_DSOUND_FILLING;
2776 // Swap the two buffers
2777 _BufferSize = _SwapBuffer->getSize();
2778 _Buffer = _SwapBuffer;
2779 _SwapBuffer = 0;
2781 setPitch(_Freq);
2783 // Check if we've reached the end of the file
2784 if (_BytesWritten == _BufferSize)
2786 if (_Loop)
2788 _BytesWritten = 0;
2790 else
2792 _SecondaryBufferState = NL_DSOUND_SILENCING;
2797 DBGPOS(("[%p] XFADE", this));
2798 DBGPOS(("[%p] P=%d, W=%d, NW=%d, SZ=%d, BW=%d, B=%d", this, playPos, writePos, _NextWritePos, _BufferSize, _BytesWritten, bytes1 + bytes2));
2802 LeaveCriticalSection(&_CriticalSection);
2805 #if NLSOUND_PROFILE
2806 _LastSwapTime = CTime::ticksToSecond(CTime::getPerformanceTime() - start);
2807 _TotalSwapTime += _LastSwapTime;
2808 _MaxSwapTime = (_LastSwapTime > _MaxSwapTime) ? _LastSwapTime : _MaxSwapTime;
2809 _MinSwapTime = (_LastSwapTime < _MinSwapTime) ? _LastSwapTime : _MinSwapTime;
2810 _SwapCount++;
2811 #endif
2817 // ******************************************************************
2819 void CSourceDSound::fadeOut()
2821 uint8 *ptr1, *ptr2;
2822 DWORD bytes1, bytes2;
2823 DWORD playPos, writePos;
2824 uint32 i;
2827 if (_Buffer == NULL)
2829 _SecondaryBufferState = NL_DSOUND_SILENCING;
2830 _UserState = NL_DSOUND_STOPPED;
2831 return;
2834 if (_SecondaryBuffer == 0)
2836 return;
2839 INITTIME(start);
2842 _SecondaryBuffer->GetCurrentPosition(&playPos, &writePos);
2845 // Lock the DirectSound buffer
2847 if (FAILED(_SecondaryBuffer->Lock(writePos, _SwapCopySize, (LPVOID*) &ptr1, &bytes1, (LPVOID*) &ptr2, &bytes2, 0)))
2849 throw ESoundDriver("Failed to lock the DirectSound secondary buffer");
2854 // in1 points to the position in the old buffer where the fade out starts
2855 sint16* in1;
2856 sint16* out = (sint16*) ptr1;
2858 // The number of samples over which we will do the crossfade
2859 uint32 xfadeSize;
2860 uint32 xfadeByteSize;
2861 uint32 writtenTooMuch;
2863 getFadeOutSize(writePos, xfadeSize, in1, writtenTooMuch);
2864 xfadeByteSize = 2 * xfadeSize;
2866 float amp1, incr;
2868 if (xfadeSize == 0)
2870 amp1 = 0.0f;
2871 incr = 0.0f;
2873 else
2875 amp1 = 1.0f;
2876 incr = 1.0f / xfadeSize;
2880 if (writtenTooMuch > _BytesWritten)
2882 // The buffer looped. Count backwards from the end of the file.
2883 _BytesWritten = _BufferSize - writtenTooMuch;
2885 else
2887 _BytesWritten -= writtenTooMuch;
2891 // Start copying the samples
2894 // In the first case, the fade out is completely contained in the first buffer
2895 // pointed to by ptr1.
2896 if (xfadeByteSize < bytes1)
2899 // Do cross fade
2901 for (i = 0; i < xfadeSize; i++)
2903 out[i] = (sint16) (amp1 * in1[i]);
2904 amp1 -= incr;
2907 // Copy remaining samples
2909 memset(ptr1 + xfadeByteSize, 0, bytes1 - xfadeByteSize);
2910 _SilenceWritten = bytes1 - xfadeByteSize;
2912 if (ptr2)
2914 memset(ptr2, 0, bytes2);
2915 _SilenceWritten += bytes2;
2920 // In the second case, the fade out stretches over the first and the second buffers.
2921 else
2924 uint32 fade1 = bytes1 / 2;
2925 uint32 fade2 = xfadeSize - fade1;
2927 // Do cross fade
2929 // Part 1, start at ptr1
2930 for (i = 0; i < fade1; i++)
2932 out[i] = (sint16) (amp1 * in1[i]);
2933 amp1 -= incr;
2937 if (ptr2)
2939 out = (sint16*) ptr2;
2941 // Part 2, continue at ptr2
2942 for (uint32 k = 0; i < xfadeSize; i++, k++)
2944 out[k] = (sint16) (amp1 * in1[i]);
2945 amp1 -= incr;
2948 // Clear remaining samples
2949 memset(ptr2 + 2 * k, 0, bytes2 - 2 * k);
2950 _SilenceWritten = bytes2 - 2 * k;
2956 _BytesWritten += xfadeByteSize;
2959 // Unlock the DirectSound buffer
2960 _SecondaryBuffer->Unlock(ptr1, bytes1, ptr2, bytes2);
2963 // Update the next write position
2964 _NextWritePos = (writePos + bytes1 + bytes2);
2965 if (_NextWritePos >= _SecondaryBufferSize)
2967 _NextWritePos -= _SecondaryBufferSize;
2971 _SecondaryBufferState = NL_DSOUND_SILENCING;
2972 DBGPOS(("[%p] FDOU: SILENCING", this));
2974 // Keep track of where tha last sample was written and the position
2975 // of the play cursor relative to the end position. if the _EndState
2976 // is 0, the play cursor is after the end position, 1 otherwise.
2977 _EndPosition = writePos + xfadeSize;
2978 if (_EndPosition >= _SecondaryBufferSize)
2980 _EndPosition -= _SecondaryBufferSize;
2983 _EndState = (playPos > _EndPosition)? NL_DSOUND_TAIL1 : NL_DSOUND_TAIL2;
2984 DBGPOS(("[%p] FDOU: ENDSTATE=%d, E=%d, P=%d", this, (int) _EndState, _EndPosition, playPos));
2987 DBGPOS(("[%p] FDOU: P=%d, W=%d, NW=%d, SZ=%d, BW=%d, S=%d, B=%d", this, playPos, writePos, _NextWritePos, _BufferSize, _BytesWritten, _SilenceWritten, bytes1 + bytes2));
2991 #if NLSOUND_PROFILE
2992 _LastSwapTime = CTime::ticksToSecond(CTime::getPerformanceTime() - start);
2993 _TotalSwapTime += _LastSwapTime;
2994 _MaxSwapTime = (_LastSwapTime > _MaxSwapTime) ? _LastSwapTime : _MaxSwapTime;
2995 _MinSwapTime = (_LastSwapTime < _MinSwapTime) ? _LastSwapTime : _MinSwapTime;
2996 _SwapCount++;
2997 #endif
3001 // ******************************************************************
3003 void CSourceDSound::fadeIn()
3005 bool res = false;
3006 uint8 *ptr1, *ptr2;
3007 DWORD bytes1, bytes2;
3008 DWORD playPos, writePos;
3011 if (_Buffer == NULL)
3013 _SecondaryBufferState = NL_DSOUND_SILENCING;
3014 _UserState = NL_DSOUND_STOPPED;
3015 return;
3018 if (_SecondaryBuffer == 0)
3020 return;
3023 INITTIME(startPos);
3025 TSampleFormat sampleFormat;
3026 uint freq;
3027 _Buffer->getFormat(sampleFormat, freq);
3029 // Set the correct pitch for this sound
3030 // setPitch(_Freq);
3031 // setPitch(_SecondaryBuffer->GetFrequency());
3033 // Set the correct volume
3034 // FIXME: a bit of a hack
3035 const CVector &pos = CListenerDSound::instance()->getPos();
3036 updateVolume(pos);
3039 _SecondaryBuffer->GetCurrentPosition(&playPos, &writePos);
3041 uint8* data = ((CBufferDSound*) _Buffer)->getData();
3042 uint32 available = (_BytesWritten < _BufferSize) ? _BufferSize - _BytesWritten : 0;
3043 uint32 bytes = NLSOUND_MIN(_SwapCopySize, available);
3044 // uint32 clear = _SwapCopySize - available;
3047 _SilenceWritten = 0;
3049 // Lock the buffer
3051 INITTIME(startLock);
3054 if (!lock(writePos, _SwapCopySize, ptr1, bytes1, ptr2, bytes2))
3056 return;
3060 INITTIME(startCopy);
3062 // Start copying the samples
3064 if (bytes1 <= bytes) {
3066 // CFastMem::memcpy(ptr1, data + _BytesWritten, bytes1);
3067 copySampleTo16BitsTrack(ptr1, data + _BytesWritten, bytes1/2, sampleFormat);
3068 _BytesWritten += bytes1;
3069 bytes -= bytes1;
3071 if (ptr2)
3073 if (bytes > 0)
3075 //CFastMem::memcpy(ptr2, data + _BytesWritten, bytes);
3076 copySampleTo16BitsTrack(ptr2, data + _BytesWritten, bytes/2, sampleFormat);
3077 _BytesWritten += bytes;
3080 if (bytes < bytes2)
3082 if (_Loop)
3084 DBGPOS(("[%p] FDIN: LOOP", this));
3086 //CFastMem::memcpy(ptr2 + bytes, data, bytes2 - bytes);
3087 copySampleTo16BitsTrack(ptr2 + bytes, data, (bytes2 - bytes)/2, sampleFormat);
3088 _BytesWritten = bytes2 - bytes;
3090 else
3092 memset(ptr2 + bytes, 0, bytes2 - bytes);
3093 _SilenceWritten = bytes2 - bytes;
3098 else
3100 if (bytes > 0)
3102 //CFastMem::memcpy(ptr1, data + _BytesWritten, bytes);
3103 copySampleTo16BitsTrack(ptr1, data + _BytesWritten, bytes/2, sampleFormat);
3104 _BytesWritten += bytes;
3107 if (_Loop)
3109 DBGPOS(("[%p] FDIN: LOOP", this));
3111 //CFastMem::memcpy(ptr1 + bytes, data, bytes1 - bytes);
3112 copySampleTo16BitsTrack(ptr1 + bytes, data, (bytes1 - bytes) / 2, sampleFormat);
3113 _BytesWritten = bytes1 - bytes;
3115 if (ptr2)
3117 //CFastMem::memcpy(ptr2, data + _BytesWritten, bytes2);
3118 copySampleTo16BitsTrack(ptr2, data + _BytesWritten, bytes2/2, sampleFormat);
3119 _BytesWritten += bytes2;
3122 else
3124 memset(ptr1 + bytes, 0, bytes1 - bytes);
3125 _SilenceWritten = bytes1 - bytes;
3127 if (ptr2)
3129 memset(ptr2, 0, bytes2);
3130 _SilenceWritten += bytes2;
3136 INITTIME(startUnlock);
3138 // Unlock the buffer
3139 _SecondaryBuffer->Unlock(ptr1, bytes1, ptr2, bytes2);
3142 // Update the state variables
3144 _SecondaryBufferState = NL_DSOUND_FILLING;
3145 DBGPOS(("[%p] FDIN: FILLING", this));
3148 // Check if we've reached the end of the file
3149 if (_BytesWritten == _BufferSize)
3151 if (_Loop)
3153 // If we're looping, start all over again
3154 DBGPOS(("[%p] FDIN: LOOP", this));
3155 _BytesWritten = 0;
3157 else
3159 _SecondaryBufferState = NL_DSOUND_SILENCING;
3161 // Keep track of where tha last sample was written and the position
3162 // of the play cursor relative to the end position. if the _EndState
3163 // is NL_DSOUND_TAIL1, the play cursor is after the end position,
3164 // NL_DSOUND_TAIL2 otherwise.
3165 _EndPosition = writePos + bytes;
3166 if (_EndPosition >= _SecondaryBufferSize)
3168 _EndPosition -= _SecondaryBufferSize;
3171 _EndState = (playPos > _EndPosition)? NL_DSOUND_TAIL1 : NL_DSOUND_TAIL2;
3173 DBGPOS(("[%p] FDIN: SILENCING", this));
3174 DBGPOS(("[%p] FDIN: ENDSTATE=%d, E=%d, P=%d", this, (int) _EndState, _EndPosition, playPos));
3179 // Update the write pointer
3180 _NextWritePos = writePos + bytes1 + bytes2;
3181 if (_NextWritePos >= _SecondaryBufferSize)
3183 _NextWritePos -= _SecondaryBufferSize;
3186 DBGPOS(("[%p] FDIN: P=%d, W=%d, NW=%d, SZ=%d, BW=%d, S=%d, B=%d", this, playPos, writePos, _NextWritePos, _BufferSize, _BytesWritten, _SilenceWritten, bytes1 + bytes2));
3189 #if NLSOUND_PROFILE
3190 _TotalUpdateSize += bytes1 + bytes2;
3191 _PosTime += CTime::ticksToSecond(startLock - startPos);
3192 _LockTime += CTime::ticksToSecond(startCopy - startLock);
3193 _CopyTime += CTime::ticksToSecond(startUnlock - startCopy);
3194 _UnlockTime += CTime::ticksToSecond(CTime::getPerformanceTime() - startUnlock);
3195 _CopyCount++;
3196 #endif
3203 } // NLSOUND