home *** CD-ROM | disk | FTP | other *** search
/ Game Audio Programming / GameAudioProgramming.iso / Game_Audio / audio_sdk / src / AudioLib / Sound.cpp < prev    next >
C/C++ Source or Header  |  2002-07-15  |  25KB  |  937 lines

  1. /***********************************************************\
  2. Copyright (C) James Boer, 2002. 
  3. All rights reserved worldwide.
  4.  
  5. This software is provided "as is" without express or implied
  6. warranties. You may freely copy and compile this source into
  7. applications you distribute provided that the copyright text
  8. below is included in the resulting source code, for example:
  9. "Portions Copyright (C) James Boer, 2002"
  10. \***********************************************************/
  11.  
  12. #include "Sound.h"
  13. #include "AudioCommon.h"
  14. #include "AudioMgr.h"
  15. #include "Wave.h"
  16. #include "Vorbis.h"
  17. #include "WMA.h"
  18.  
  19. using namespace std;
  20. using namespace Audio;
  21.  
  22. const float STREAMING_BUFFER_SECONDS = 1.0f;
  23.  
  24. IMPLEMENT_POOL(Sound);
  25.  
  26. //------------------------------------------------------------------------//
  27. Sound::Sound()
  28. {
  29.     FN("Sound::Sound()");
  30.     Clear();
  31. }
  32.  
  33.  
  34. //------------------------------------------------------------------------//
  35. Sound::~Sound()
  36. {
  37.     FN("Sound::~Sound()");
  38.     Term();
  39. }
  40.  
  41.  
  42. //------------------------------------------------------------------------//
  43. void Sound::Clear()
  44. {
  45.     FN("Sound::Clear()");
  46.     m_bInitialized = false;
  47.     m_pDSBuffer = 0;
  48.     m_PropertySet.Clear();
  49.     m_Init.Clear();
  50.     m_b3DSound = false;
  51.     ZeroMemory(&m_WaveFormat, sizeof(WAVEFORMATEX));
  52.     ZeroMemory(&m_BufferDesc, sizeof(DSBUFFERDESC));
  53.     m_BufferDesc.dwSize = sizeof(DSBUFFERDESC);
  54.     ZeroMemory(&m_Caps, sizeof(DSBCAPS));
  55.     m_Caps.dwSize = sizeof(DSBCAPS);
  56.     m_nBytesPlayed = 0;
  57.     m_nLastReadPos = 0;
  58.     m_nDataCursor = 0;
  59.     m_bRemoveStream = true;
  60.     m_pLoader = 0;
  61.     m_bPaused = false;
  62.     m_nSourceSize = 0;
  63.     m_nLastTimePlayed = 0;
  64.     m_bQueuePlayback = false;
  65.     m_bLoading = false;
  66.     m_bLoaded = false;
  67.  
  68. }
  69.  
  70.  
  71.  
  72. //------------------------------------------------------------------------//
  73. bool Sound::Init(const SoundInit& init)
  74. {
  75.     FN("Sound::Init()");
  76.  
  77.     if(m_bInitialized)
  78.         return Error::Handle("Sound object is already initialized");
  79.  
  80.     // Set the audio definition
  81.     m_Init = init;
  82.  
  83.     // Extract the extension - decide on a loader based on extension name
  84.     string sExt = m_Init.m_sFileName.substr(m_Init.m_sFileName.size() - 3);
  85.  
  86.     // Use the Wave loader
  87.     if(stricmp(sExt.c_str(), "wav") == 0)
  88.         m_pLoader = Wave::CreateObject();
  89.     // Use the Vorbis loader
  90. #ifdef USE_VORBIS
  91.     else if(stricmp(sExt.c_str(), "ogg") == 0)
  92.         m_pLoader = Vorbis::CreateObject();
  93. #endif // USE_VORBIS
  94. #ifdef USE_WMA
  95.     else if(stricmp(sExt.c_str(), "wma") == 0)
  96.         m_pLoader = WMA::CreateObject();
  97.     else if(stricmp(sExt.c_str(), "mp3") == 0)
  98.         m_pLoader = WMA::CreateObject();
  99. #endif // USE_WMA
  100.     else
  101.         return Error::Handle("Unsupported file type");
  102.  
  103.     m_bInitialized = true;
  104.  
  105.     return true;
  106. }
  107.  
  108.  
  109. //------------------------------------------------------------------------//
  110. void Sound::Term()
  111. {
  112.     FN("Sound::Term()");
  113.     Unload();
  114.     m_PropertySet.Term();
  115.     if(m_pLoader)
  116.     {
  117.         m_pLoader->Destroy();
  118.         m_pLoader = 0;
  119.     }
  120.     Clear();
  121. }
  122.  
  123. //------------------------------------------------------------------------//
  124. void Sound::Destroy()
  125. {
  126.     FN("Sound::Destroy()");
  127.     Term();
  128.     Sound::DestroyObject(this);
  129. }
  130.  
  131. //------------------------------------------------------------------------//
  132. bool Sound::Load()
  133. {
  134.     FN("Sound::Load()");
  135.  
  136.     // Make sure we don't reload the sound
  137.     if(IsLoaded() || IsLoading())
  138.         return true;
  139.  
  140.     // If not loading asyncronously, load immediately and return
  141.     if(!DXAudioMgr()->LoadAsync())
  142.         return DoLoad();
  143.     
  144.     // Otherwise, schedule the audio manager to asynchronously load this sound
  145.     m_bLoading = true;
  146.     DXAudioMgr()->ScheduleLoad(this);
  147.  
  148.     return true;
  149. }
  150.  
  151.  
  152. //------------------------------------------------------------------------//
  153. bool Sound::DoLoad()
  154. {
  155.     FN("Sound::DoLoad()");
  156.  
  157.     // First prepare and load the source
  158.     uint32 nBufferSize = 0;
  159.     if(!LoadSource(nBufferSize))
  160.         return false;
  161.  
  162.     // Set this buffer's data size
  163.     m_nSourceSize = m_pLoader->GetSize();
  164.  
  165.     // Ensure we're not over the buffer limit
  166.     if(!m_b3DSound)
  167.     {
  168.         if(!DXAudioMgr()->CanAddSound())
  169.             DXAudioMgr()->RemoveSound(this);
  170.     }
  171.  
  172.     // Set the buffer creation flags depending on user preferences
  173.     uint32 nFlags =
  174.         DSBCAPS_GETCURRENTPOSITION2 | 
  175.         DSBCAPS_CTRLFREQUENCY | 
  176.         DSBCAPS_CTRLVOLUME |
  177.         DSBCAPS_GLOBALFOCUS;
  178.  
  179.     // Check for 3d sound
  180.     if(m_b3DSound)
  181.         nFlags |= DSBCAPS_CTRL3D | DSBCAPS_MUTE3DATMAXDISTANCE;
  182.  
  183.     // Check for pan flag
  184.     if((m_WaveFormat.nChannels == 1) && !m_b3DSound)
  185.         nFlags |= DSBCAPS_CTRLPAN;
  186.  
  187.     // Check for multichannel / 3d conflict
  188.     if((m_WaveFormat.nChannels != 1) && m_b3DSound)
  189.     {
  190.         Unload();
  191.         return Error::Handle("Can't create a 3d buffer with more than one channel in sound source.");
  192.     }
  193.     
  194.     // Determine if we should be in hardware or software
  195.     if(DXAudioMgr()->ForceSoftware(m_b3DSound))
  196.         nFlags |= DSBCAPS_LOCSOFTWARE;
  197.     else
  198.         nFlags |= DSBCAPS_LOCHARDWARE;
  199.  
  200.     // Create the actual sound buffer
  201.     m_BufferDesc.dwFlags = nFlags;
  202.     m_BufferDesc.lpwfxFormat = &m_WaveFormat;
  203.     m_BufferDesc.dwBufferBytes = nBufferSize;
  204.  
  205.     if(!DXAudioMgr()->GetBufferCache()->Acquire(m_BufferDesc, m_pDSBuffer, !m_Init.m_bLooping))
  206.         return false;
  207.  
  208.     // Get the caps to determine the actual size of the created buffer
  209.     HRESULT hr = m_pDSBuffer->GetCaps(&m_Caps);
  210.     if(FAILED(hr))
  211.     {
  212.         Unload();
  213.         return Error::Handle("Could not get buffer caps.  Error = %s", DXGetErrorString(hr));
  214.     }
  215.  
  216.     // Get the property set interface for this buffer if it's not a 3d sound buffer.
  217.     if(!m_b3DSound)
  218.         if(!m_PropertySet.OnLoad(m_pDSBuffer))
  219.             Error::Handle("Could not obtain IKsPropertySet interface");
  220.     
  221.     // Fill the entire buffer with source data
  222.     if(!FillBuffer())
  223.     {
  224.         Unload();
  225.         return false;
  226.     }
  227.  
  228.     // We no longer need the file or reader if it's a static buffer (non-streaming)
  229.     if(!m_Init.m_bStreaming)
  230.         m_pLoader->Close();
  231.  
  232.     DebugOut(3, "Loaded sound %s", m_Init.m_sFileName.c_str());
  233.  
  234.     // Insert into the appropriate load list if not a 3d sound.
  235.     // 3D sounds are loaded into their own list as appropriate
  236.     if(!m_b3DSound)
  237.         DXAudioMgr()->OnLoadSound(this);
  238.  
  239.     SetProperties(m_Init.m_Prop);
  240.  
  241.     m_bLoading = false;
  242.     m_bLoaded = true;
  243.  
  244.     if(m_bQueuePlayback)
  245.         Play();
  246.  
  247.     return true;
  248. }
  249.  
  250.  
  251. //------------------------------------------------------------------------//
  252. bool Sound::LoadSource(uint32& nBufferSize)
  253. {
  254.     FN("Sound::LoadSource()");
  255.  
  256.     if(!m_pLoader->Open(m_Init.m_sFileName))
  257.         return false;
  258.  
  259.     memcpy(&m_WaveFormat, m_pLoader->GetFormat(), sizeof(WAVEFORMATEX));
  260.  
  261.     // Check to see if we're streaming or now.  We handle things a bit differently
  262.     // in each case.
  263.     if(m_Init.m_bStreaming)
  264.     {
  265.         // Calculate the size of the streaming buffer.  Note that currently a one
  266.         // second buffer is being used.
  267.         nBufferSize = m_WaveFormat.nAvgBytesPerSec * STREAMING_BUFFER_SECONDS;
  268.  
  269.         // Make sure we don't try to stream a file that's smaller than the
  270.         // buffer size - this will not work, and is completely unnecessary anyhow.
  271.         if(nBufferSize > m_pLoader->GetSize())
  272.         {
  273.             // If so, just make it a static buffer and continue with a warning
  274.             DebugOut(2, "Warning!  Sound is too small for streaming - continuing as a static buffer.");
  275.             m_Init.m_bStreaming = false;
  276.             nBufferSize = m_pLoader->GetSize();
  277.             return true;
  278.         }
  279.     }
  280.     else
  281.     {
  282.         // calculate the size of the dsound buffer
  283.         nBufferSize = m_pLoader->GetSize();
  284.  
  285.         // Switch to a streaming buffer if auto stream caching is on
  286.         // and the file is large enough...
  287.         if(DXAudioMgr()->GetInit()->m_bAutoStream &&  
  288.             (nBufferSize >= (m_WaveFormat.nAvgBytesPerSec * STREAMING_BUFFER_SECONDS)))
  289.         {
  290.             m_Init.m_bStreaming = true;
  291.             nBufferSize = (m_WaveFormat.nAvgBytesPerSec * STREAMING_BUFFER_SECONDS);
  292.         }
  293.         // If buffer caching is on, force small non-looping buffers to use a
  294.         // minimum standard size.  This will allow us to efficiently
  295.         // reuse these buffers
  296.         else if(DXAudioMgr()->GetInit()->m_bCacheBuffers && 
  297.             (m_Init.m_bLooping == false) &&
  298.             (nBufferSize < (m_WaveFormat.nAvgBytesPerSec * STREAMING_BUFFER_SECONDS)))
  299.         {
  300.             nBufferSize = (m_WaveFormat.nAvgBytesPerSec * STREAMING_BUFFER_SECONDS);
  301.         }
  302.     }
  303.  
  304.     return true;
  305. }
  306.  
  307.  
  308. //------------------------------------------------------------------------//
  309. bool Sound::FillBuffer()
  310. {
  311.     FN("Sound::FillBuffer()");
  312.  
  313.     CRITICAL_FUNCTION(&DXAudioMgr()->GetUpdateCS());
  314.  
  315.     // Lock the buffer
  316.     void* pData;
  317.     uint32  nBytes;
  318.     HRESULT hr;
  319.      hr = m_pDSBuffer->Lock(
  320.         0,
  321.         0,
  322.         &pData,
  323.         &nBytes,
  324.         NULL,
  325.         NULL,
  326.         DSBLOCK_ENTIREBUFFER);
  327.     if(FAILED(hr))
  328.         return Error::Handle("Could not lock sound buffer.  Error = %s", DXGetErrorString(hr));
  329.  
  330.     // Fill the entire buffer with audio data from source
  331.     uint32 nBytesToRead;
  332.     uint32 nBytesRead;
  333.  
  334.     nBytesToRead = nBytes;
  335.  
  336.     // Note that in the case of errors, we'll still want to continue so we
  337.     // can properly unlock the buffer again.
  338.     if(!m_pLoader->Read((unsigned char*)pData, nBytesToRead, &nBytesRead))
  339.         Error::Handle("Could not read sound data source.");
  340.  
  341.     m_nDataCursor += nBytesRead;
  342.     m_nDataCursor %= nBytes;
  343.  
  344.     // If we read less than the entire buffer, fill the rest with silence
  345.     if(nBytesRead < nBytes)
  346.         memset(((unsigned char*)pData) + nBytesRead, GetSilenceData(), nBytes - nBytesRead);
  347.  
  348.     // Unlock buffer
  349.     hr = m_pDSBuffer->Unlock(pData, nBytes, NULL, 0);
  350.     if(FAILED(hr))
  351.         return Error::Handle("Could not unlock sound buffer.  Error = %s", DXGetErrorString(hr));
  352.  
  353.     return true;
  354. }
  355.  
  356.  
  357. //------------------------------------------------------------------------//
  358. bool Sound::Unload()
  359. {
  360.     FN("Sound::Unload()");
  361.  
  362.     // If already unloaded, don't bother
  363.     if(!IsLoaded())
  364.         return true;
  365.  
  366.     // Wait until a sound is completely loaded before unloading it
  367.     while(IsLoading());
  368.  
  369.     CRITICAL_FUNCTION(&DXAudioMgr()->GetUpdateCS());
  370.  
  371.     // Stop the buffer before unloading
  372.     Stop();
  373.  
  374.     // Remove from the appropriate load list
  375.     if(!m_b3DSound)
  376.         DXAudioMgr()->OnUnloadSound(this);
  377.  
  378.     // Release the source if it's a streaming buffer
  379.     if(m_Init.m_bStreaming)
  380.     {
  381.         m_pLoader->Close();
  382.     }
  383.  
  384.     // Release the propertyset interface
  385.     m_PropertySet.OnUnload();
  386.  
  387.     // Release the DirectSound buffer
  388.     DXAudioMgr()->GetBufferCache()->Free(m_pDSBuffer);
  389.     SAFE_RELEASE(m_pDSBuffer);
  390.  
  391.     // Mark the sound as unloaded
  392.     m_bLoaded = false;
  393.  
  394.     DebugOut(3, "Unloaded sound %s", m_Init.m_sFileName.c_str());
  395.     return true;
  396. }
  397.  
  398.  
  399. //------------------------------------------------------------------------//
  400. // For streaming audio, ServiceBuffer() is invoked five times per second for each
  401. // streaming buffer via a multimedia timer in order to fill and update the content 
  402. // of the circular buffer.
  403. void Sound::ServiceBuffer()
  404. {
  405.     FN("Sound::ServiceBuffer()");
  406.  
  407.     // If this flag has been set, it means that we should add this object to
  408.     // the list of those to be removed as soon as the service loop is completed.
  409.     if(m_bRemoveStream)
  410.     {
  411.         m_bRemoveStream = false;
  412.  
  413.         DXAudioMgr()->RemoveStream(this);
  414.  
  415.         if(IsPaused())
  416.             return;
  417.  
  418.         // Check to make sure we actually have a valid source, which should be the case.
  419.         // If not, we'll probably hear some garbage on the next Play() call.
  420.         if(!m_pLoader)
  421.             return;
  422.  
  423.         // Reset the source to play from the beginning of the file
  424.         m_pLoader->Reset();
  425.  
  426.         // Reset streaming variables
  427.         m_nBytesPlayed = 0;
  428.         m_nLastReadPos = 0;
  429.         m_nDataCursor = 0;
  430.         if(!m_pDSBuffer)
  431.             return;
  432.  
  433.         // Make sure the buffer plays from the beginning when it starts up again
  434.         m_pDSBuffer->SetCurrentPosition(0);
  435.  
  436.         // The next time this buffer plays, we want it set up properly with a 
  437.         // full buffer of valid source data.
  438.         if(!FillBuffer())
  439.             return;
  440.  
  441.         return;
  442.     }
  443.  
  444.     // If we don't have a buffer or source, no use continuing
  445.     if(!m_pDSBuffer || !m_pLoader)
  446.         return;
  447.  
  448.     // Get the current play and write cursors for the buffer
  449.     DWORD dwReadCursor;
  450.     DWORD dwWriteCursor;
  451.     HRESULT hr = m_pDSBuffer->GetCurrentPosition(&dwReadCursor, &dwWriteCursor);
  452.  
  453.     // Calculate how many bytes have played since the last update call
  454.     if(dwReadCursor > m_nLastReadPos)
  455.         m_nBytesPlayed += dwReadCursor - m_nLastReadPos;
  456.     else
  457.         m_nBytesPlayed += (m_Caps.dwBufferBytes - m_nLastReadPos) + dwReadCursor;
  458.  
  459.     // Have we played the entire sound?  If so, take appropriate action based on
  460.     // whether we're looping the file or not.
  461.     if(m_nBytesPlayed >= m_pLoader->GetSize())
  462.     {
  463.         if(m_Init.m_bLooping)
  464.         {
  465.             // If we're looping, just start the count back at the beginning
  466.             m_nBytesPlayed -= m_pLoader->GetSize();
  467.         }
  468.         else
  469.         {
  470.             // Otherwise, stop playing and processing the buffer
  471.             Stop();
  472.             return;
  473.         }
  474.     }
  475.  
  476.     // Calculate how much data can be copied to the buffer this update
  477.     DWORD dwDataToCopy;
  478.     if(m_nDataCursor < dwReadCursor)
  479.         dwDataToCopy = dwReadCursor - m_nDataCursor;
  480.     else
  481.         dwDataToCopy = (m_Caps.dwBufferBytes - m_nDataCursor) + dwReadCursor;
  482.  
  483.     // No need to allow more than 1/2 of the buffer to be read at a time.  We're
  484.     // reading five times a second, so this should keep up without overtaxing
  485.     // the readers.
  486.     if(dwDataToCopy > (m_Caps.dwBufferBytes / 2))
  487.         dwDataToCopy = m_Caps.dwBufferBytes / 2;
  488.  
  489.     // Lock the buffer into one or two buffers
  490.     LPVOID  pPtr1; 
  491.     DWORD   dwBytes1; 
  492.     LPVOID  pPtr2; 
  493.     DWORD   dwBytes2; 
  494.     hr = m_pDSBuffer->Lock(m_nDataCursor, dwDataToCopy, &pPtr1, 
  495.         &dwBytes1, &pPtr2, &dwBytes2, 0);
  496.     if(FAILED(hr))
  497.     {
  498.         Error::Handle("Error locking DSound streaming buffer!");
  499.         return;
  500.     }
  501.  
  502.     // If we're at the end of the wave data...
  503.     if(m_pLoader->IsEOF())
  504.     {
  505.         // Fill the buffer with silence - we're at the end of the file
  506.         memset(pPtr1, GetSilenceData(), dwBytes1);
  507.         if(pPtr2)
  508.             memset(pPtr2, GetSilenceData(), dwBytes2);
  509.         m_nDataCursor += (dwBytes1 + dwBytes2);
  510.     }
  511.     // Otherwise...
  512.     else
  513.     {
  514.         // Fill the buffer with wave data as needed
  515.         uint32 dwBytesRead = 0;
  516.         if(!m_pLoader->Read((unsigned char*)pPtr1, dwBytes1, &dwBytesRead))
  517.         {  Error::Handle("Error reading wave file!");  return;  }
  518.         m_nDataCursor += dwBytesRead;
  519.         if(pPtr2 && (dwBytes1 == dwBytesRead))
  520.         {
  521.             if(!m_pLoader->Read((unsigned char*)pPtr2, dwBytes2, &dwBytesRead))
  522.             {  Error::Handle("Error reading wave file!");  return;  }
  523.             m_nDataCursor += dwBytesRead;
  524.         }
  525.     }
  526.  
  527.     // Unlock the buffer now that we're done with it
  528.     m_pDSBuffer->Unlock(pPtr1, dwBytes1, pPtr2, dwBytes2);
  529.  
  530.     // If we want to loop the stream, reset the file to the beginning
  531.     if(m_Init.m_bLooping && m_pLoader->IsEOF())
  532.         m_pLoader->Reset();
  533.  
  534.     // Loop the write position around if it goes past the end of the buffer
  535.     m_nDataCursor %= m_Caps.dwBufferBytes;
  536.  
  537.     // Set the last play cursor position for next update calculation
  538.     m_nLastReadPos = dwReadCursor;
  539. }
  540.  
  541. //------------------------------------------------------------------------//
  542. // This function gets a single byte of "silence" data, which differs depending on
  543. // the number of bits per sample.
  544. uint8 Sound::GetSilenceData()
  545. {
  546.     FN("Sound::GetSilenceData()");
  547.     if(m_WaveFormat.wBitsPerSample == 8)
  548.         return 0x80;
  549.     else if(m_WaveFormat.wBitsPerSample == 16)
  550.         return 0x00;
  551.     return 0;
  552. }
  553.  
  554.  
  555. //------------------------------------------------------------------------//
  556. bool Sound::Play()
  557. {
  558.     FN("Sound::Play()");
  559.  
  560.     // Mark the play time for prioritization
  561.     m_nLastTimePlayed = timeGetTime();
  562.  
  563.     // Determine if we need to load this buffer before playing.  After the
  564.     // buffer is finished loading, playback will begin automatically.
  565.     if(IsLoading())
  566.     {
  567.         m_bQueuePlayback = true;
  568.         return true;
  569.     }
  570.     else if(!IsLoaded())
  571.     {
  572.         m_bQueuePlayback = true;
  573.         return Load();
  574.     }
  575.     // Clear queue play flag
  576.     m_bQueuePlayback = false;
  577.  
  578.     // Verify we actually have a DS buffer at this point
  579.     if(!m_pDSBuffer)
  580.         return false;
  581.  
  582.     // Check to see if we're already playing
  583.     if(IsPlaying())
  584.         return true;
  585.  
  586.     // If the sound is streaming, insert this buffer into a list managed
  587.     // by the audio mgr which periodically calls ServiceBuffer()
  588.     if(m_Init.m_bStreaming)
  589.     {
  590.         EnterCriticalSection(&DXAudioMgr()->GetUpdateCS());
  591.         m_bRemoveStream = false;
  592.         DXAudioMgr()->InsertStream(this);
  593.         LeaveCriticalSection(&DXAudioMgr()->GetUpdateCS());
  594.     }
  595.  
  596.     // Begin playing the buffer.  If the looping flag is set or if the
  597.     // buffer is streaming, begin looping playback
  598.     HRESULT hr;
  599.     if(m_Init.m_bStreaming || m_Init.m_bLooping)
  600.         hr = m_pDSBuffer->Play(0, 0, DSBPLAY_LOOPING);
  601.     else
  602.         hr = m_pDSBuffer->Play(0, 0, 0);
  603.     if(FAILED(hr))
  604.         return false;
  605.  
  606.     // Set the paused flag to off
  607.     m_bPaused = false;
  608.  
  609.     return true;
  610. }
  611.  
  612.  
  613. //------------------------------------------------------------------------//
  614. // Stops playback 
  615. bool Sound::Stop()
  616. {
  617.     FN("Sound::Stop()");
  618.  
  619.     m_bQueuePlayback = false;
  620.  
  621.     // If the sound is already stopped (and it's not paused), then just return
  622.     if(!IsPlaying() && !IsPaused())
  623.         return true;
  624.  
  625.     // Check to make sure we actually have a buffer
  626.     if(!m_pDSBuffer)
  627.         return false;
  628.  
  629.     // Stop the DirectSound buffer
  630.     m_pDSBuffer->Stop();
  631.  
  632.     // If the buffer is streaming, we have a bit of extra work to do
  633.     if(!m_Init.m_bStreaming)
  634.     {
  635.         // For an in-memory sound, simply reset the current position to the beginning
  636.         SetReadCursor(0);
  637.     }
  638.     // Flag the sound for removal from the managed stream list.  This flag will
  639.     // tell the stream to remove itself on the next call to ServiceBuffer().
  640.     m_bRemoveStream = true;
  641.  
  642.     return true;
  643. }
  644.  
  645.  
  646. //------------------------------------------------------------------------//
  647. bool Sound::Pause()
  648. {
  649.     FN("Sound::Pause()");
  650.  
  651.     // Ignore this call if the sound is already stopped or paused
  652.     if(!IsPlaying() || IsPaused())
  653.         return false;
  654.  
  655.     // If the sound is streaming, flag it for removal from the streaming list
  656.     m_bRemoveStream = true;
  657.  
  658.     // Set the paused flag
  659.     m_bPaused = true;
  660.  
  661.     // Stop the buffer if it's available
  662.     if(m_pDSBuffer)
  663.         m_pDSBuffer->Stop();
  664.  
  665.     return true;
  666. }
  667.  
  668.  
  669. //------------------------------------------------------------------------//
  670. bool Sound::IsPlaying() const 
  671. {
  672.     FN("Sound::IsPlaying()");
  673.     DWORD dwStatus;
  674.     if(m_pDSBuffer)
  675.     {
  676.         m_pDSBuffer->GetStatus(&dwStatus);
  677.         if(dwStatus & DSBSTATUS_PLAYING)
  678.             return true;
  679.     }
  680.     return m_bQueuePlayback;
  681. }
  682.  
  683.  
  684. //------------------------------------------------------------------------//
  685. bool Sound::IsPaused() const
  686. {
  687.     FN("Sound::IsPaused()");
  688.     return m_bPaused;
  689. }
  690.  
  691.  
  692. //------------------------------------------------------------------------//
  693. bool Sound::IsLooping() const
  694. {
  695.     FN("Sound::IsLooping()");
  696.     return m_Init.m_bLooping;
  697. }
  698.  
  699. //------------------------------------------------------------------------//
  700. bool Sound::SetProperties(const SoundProp& prop)
  701. {
  702.     m_Init.m_Prop = prop;
  703.     SetVolume(prop.m_fVolume);
  704.     SetPan(prop.m_fPan);
  705.     SetPitch(prop.m_fPitch);
  706.     return true;
  707. }
  708.  
  709. //------------------------------------------------------------------------//
  710. bool Sound::GetProperties(SoundProp& prop) const
  711. {
  712.     prop = m_Init.m_Prop;
  713.     return true;
  714. }
  715.  
  716.  
  717. //------------------------------------------------------------------------//
  718. bool Sound::SetVolume(float fVolume)
  719. {
  720.     FN("Sound::SetVolume(%f)", fVolume);
  721.  
  722.     m_Init.m_Prop.m_fVolume = Clamp<float>(fVolume, VOLUME_MIN, VOLUME_MAX);
  723.  
  724.     if(!m_pDSBuffer)
  725.         return true;
  726.  
  727.     float fMasterVol;
  728.     if(m_Init.m_bMusic)
  729.         AudioMgr()->GetMusicVolume(fMasterVol);
  730.     else
  731.         AudioMgr()->GetSoundVolume(fMasterVol);
  732.     int32 iVolume = LinearToLogVol(fVolume * fMasterVol);
  733.     HRESULT hr = m_pDSBuffer->SetVolume(iVolume);
  734.     if(FAILED(hr))
  735.         return Error::Handle("Could not set volume.  Error = %s.", DXGetErrorString(hr));
  736.  
  737.     return true;
  738. }
  739.  
  740.  
  741.  
  742. //------------------------------------------------------------------------//
  743. bool Sound::GetVolume(float& fVolume) const
  744. {
  745.     FN("Sound::GetVolume()");
  746.     fVolume = m_Init.m_Prop.m_fVolume;
  747.     return true;
  748. }
  749.  
  750.  
  751. //------------------------------------------------------------------------//
  752. bool Sound::SetPan(float fPan)
  753. {
  754.     FN("Sound::SetPan(%f)", fPan);
  755.     m_Init.m_Prop.m_fPan = Clamp<float>(fPan, PAN_LEFT, PAN_RIGHT);
  756.  
  757.     // Panning is not allowed for sounds with particular characteristics
  758.     if(m_b3DSound || (m_WaveFormat.nChannels != 1))
  759.         return false;
  760.     
  761.     if(m_pDSBuffer)
  762.     {
  763.         HRESULT hr = m_pDSBuffer->SetPan(static_cast<int32>(m_Init.m_Prop.m_fPan * 10000.0f));
  764.         if(FAILED(hr))
  765.             return Error::Handle("Could not set pan.  Error = %s.", DXGetErrorString(hr));
  766.     }
  767.     return true;
  768. }
  769.  
  770.  
  771. //------------------------------------------------------------------------//
  772. bool Sound::GetPan(float& fPan) const
  773. {
  774.     FN("Sound::GetPan()");
  775.     fPan = m_Init.m_Prop.m_fPan;
  776.     return true;
  777. }
  778.  
  779.  
  780. //------------------------------------------------------------------------//
  781. bool Sound::SetPitch(float fPitch)
  782. {
  783.     FN("Sound::SetPitch(%f)", fPitch);
  784.     m_Init.m_Prop.m_fPitch = fPitch;
  785.     if(!SetFrequency(uint32(float(m_WaveFormat.nSamplesPerSec) * m_Init.m_Prop.m_fPitch)))
  786.         return false;
  787.     return true;
  788. }
  789.  
  790.  
  791. //------------------------------------------------------------------------//
  792. bool Sound::GetPitch(float& fPitch) const
  793. {
  794.     FN("Sound::GetPitch()");
  795.     fPitch = m_Init.m_Prop.m_fPitch;
  796.     return true;
  797. }
  798.  
  799.  
  800. //------------------------------------------------------------------------//
  801. bool Sound::SetFrequency(uint32 nFrequency)
  802. {
  803.     FN("Sound::SetFrequency(%d)", nFrequency);
  804.  
  805.     if(m_pDSBuffer)
  806.     {
  807.         nFrequency = Clamp<uint32>(nFrequency, DXAudioMgr()->GetCaps().dwMinSecondarySampleRate,
  808.             DXAudioMgr()->GetCaps().dwMaxSecondarySampleRate);
  809.         HRESULT hr = m_pDSBuffer->SetFrequency(nFrequency);
  810.         if(FAILED(hr))
  811.             return Error::Handle("Could not set frequency.  Error = %s.", DXGetErrorString(hr));
  812.     }
  813.     return true;
  814. }
  815.  
  816.  
  817. //------------------------------------------------------------------------//
  818. bool Sound::SetReadCursor(uint32 nBytes)
  819. {
  820.     FN("Sound::SetReadCursor(%d)", nBytes);
  821.     // Note : Jumping to an arbitrary point in a stream is currently not supported,
  822.     // but could be added later if desired.
  823.     if(m_Init.m_bStreaming)
  824.         return Error::Handle("Can not perform this operation on an audio stream");
  825.     m_Init.m_Prop.m_nReadCursor = nBytes;
  826.     if(m_pDSBuffer)
  827.     {
  828.         HRESULT hr = m_pDSBuffer->SetCurrentPosition(m_Init.m_Prop.m_nReadCursor);
  829.         if(FAILED(hr))
  830.             return Error::Handle("Count not set position.  Error = %s.", DXGetErrorString(hr));
  831.     }
  832.     return true;
  833. }
  834.  
  835.  
  836. //------------------------------------------------------------------------//
  837. bool Sound::GetReadCursor(uint32& nBytes) const
  838. {
  839.     FN("Sound::GetReadCursor()");
  840.     if(m_Init.m_bStreaming)
  841.     {
  842.         nBytes = m_nBytesPlayed;
  843.     }
  844.     else
  845.     {
  846.         nBytes = m_Init.m_Prop.m_nReadCursor;
  847.         if(m_pDSBuffer)
  848.         {
  849.             HRESULT hr = m_pDSBuffer->GetCurrentPosition(&nBytes, NULL);
  850.             if(FAILED(hr))
  851.                 return Error::Handle("Could not get position.  Error = %s.", DXGetErrorString(hr));
  852.         }
  853.     }
  854.     return true;
  855. }
  856.  
  857.  
  858. //------------------------------------------------------------------------//
  859. bool Sound::GetSourceSize(uint32& nBytes) const
  860. {
  861.     FN("Sound::GetSourceSize()");
  862.     nBytes = m_nSourceSize;
  863.     return true;
  864. }
  865.  
  866.  
  867. //------------------------------------------------------------------------//
  868. uint32 Sound::GetLastPlayTime() const
  869. {
  870.     if(IsPlaying())
  871.         return 0xFFFFFFFF;
  872.     return m_nLastTimePlayed;
  873. }
  874.  
  875.  
  876. //------------------------------------------------------------------------//
  877. bool Sound::operator < (const Sound& snd) const
  878. {
  879.     int iScore = 0;
  880.  
  881.     // We compare three criteria in this priority test:
  882.     // user-defined priority , current play status, and 
  883.     // the last time played.
  884.     if(m_Init.m_nPriority < snd.m_Init.m_nPriority)
  885.         iScore--;
  886.     else if(m_Init.m_nPriority > snd.m_Init.m_nPriority)
  887.         iScore++;
  888.  
  889.     if(IsPlaying())
  890.         iScore++;
  891.     if(snd.IsPlaying())
  892.         iScore--;
  893.  
  894.     if(GetLastPlayTime() < snd.GetLastPlayTime())
  895.         iScore--;
  896.     else if(GetLastPlayTime() > snd.GetLastPlayTime())
  897.         iScore++;
  898.  
  899.     return (iScore < 0) ? true : false;
  900. }
  901.  
  902.  
  903. //------------------------------------------------------------------------//
  904. // Generic property support (for driver-specific extensions)
  905. bool Sound::QuerySupport(const GUID& guid, uint32 nID, uint32* pTypeSupport)
  906. {
  907.     FN("Sound::QuerySupport()");
  908.     return m_PropertySet.QuerySupport(guid, nID, pTypeSupport);
  909. }
  910.  
  911.  
  912. //------------------------------------------------------------------------//
  913. bool Sound::Get(const GUID& guidProperty, uint32 nID, void* pInstanceData,
  914.         uint32 nInstanceLength, void* pPropData, 
  915.     uint32 nPropLength, uint32* pBytesReturned)
  916. {
  917.     FN("Sound::Get()");
  918.     return m_PropertySet.Get(guidProperty, nID, pInstanceData, nInstanceLength,
  919.         pPropData, nPropLength, pBytesReturned);
  920. }
  921.  
  922.  
  923. //------------------------------------------------------------------------//
  924. bool Sound::Set(const GUID& guidProperty, uint32 nID, void* pInstanceData,
  925.     uint32 nInstanceLength, void* pPropData, uint32 nPropLength,
  926.     bool bStoreProperty)
  927. {
  928.     FN("Sound::Set()");
  929.     return m_PropertySet.Set(guidProperty, nID, pInstanceData, nInstanceLength, 
  930.         pPropData, nPropLength, bStoreProperty);
  931. }
  932.  
  933.  
  934.  
  935.  
  936.  
  937.