home *** CD-ROM | disk | FTP | other *** search
/ Game Audio Programming / GameAudioProgramming.iso / Game_Audio / audio_sdk / src / AudioLib / Segment.cpp < prev    next >
C/C++ Source or Header  |  2002-09-10  |  13KB  |  495 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 "Segment.h"
  13. #include "AudioCommon.h"
  14. #include "AudioMgr.h"
  15.  
  16. using namespace std;
  17. using namespace Audio;
  18.  
  19. IMPLEMENT_POOL(Segment);
  20.  
  21. //------------------------------------------------------------------------//
  22. Segment::Segment()
  23. {
  24.     FN("Segment::Segment()");
  25.     Clear();
  26. }
  27.  
  28.  
  29. //------------------------------------------------------------------------//
  30. Segment::~Segment()
  31. {
  32.     FN("Segment::~Segment()");
  33.     Term();
  34. }
  35.  
  36.  
  37. //------------------------------------------------------------------------//
  38. void Segment::Clear()
  39. {
  40.     FN("Segment::Clear()");
  41.     m_pSegment = NULL;
  42.     m_pSegState = NULL;
  43.     m_Init.Clear();
  44.     m_bInitialized = false;
  45.     m_bPaused = false;
  46.     m_b3DSegment = false;
  47.     m_iPlayStartTime = 0;
  48.     m_iPauseTime = 0;
  49.  
  50.     m_nLastTimePlayed = 0;
  51.  
  52.     m_bQueuePlayback = false;
  53.     m_bLoading = false;
  54.     m_bLoaded = false;
  55. }
  56.  
  57.  
  58.  
  59. //------------------------------------------------------------------------//
  60. bool Segment::Init(const SegmentInit& init)
  61. {
  62.     FN("Segment::Init()");
  63.  
  64.     // Set the audio definition
  65.     m_Init = init;
  66.  
  67.     m_bInitialized = true;
  68.  
  69.     return true;
  70. }
  71.  
  72.  
  73. //------------------------------------------------------------------------//
  74. void Segment::Term()
  75. {
  76.     FN("Segment::Term()");
  77.     Unload();
  78. }
  79.  
  80.  
  81. //------------------------------------------------------------------------//
  82. void Segment::Destroy()
  83. {
  84.     FN("Segment::Destroy()");
  85.     Term();
  86.     Segment::DestroyObject(this);
  87. }
  88.  
  89. //------------------------------------------------------------------------//
  90. bool Segment::Load()
  91. {
  92.     FN("Segment::Load()");
  93.  
  94.     // Make sure we don't reload the sound
  95.     if(IsLoaded() || IsLoading())
  96.         return true;
  97.  
  98.     // If not loading asyncronously, load immediately and return
  99.     if(!DXAudioMgr()->LoadAsync())
  100.         return DoLoad();
  101.     
  102.     // Otherwise, schedule the audio manager to asynchronously load this sound
  103.     m_bLoading = true;
  104.     DXAudioMgr()->ScheduleLoad(this);
  105.  
  106.     return true;
  107. }
  108.  
  109.  
  110. //------------------------------------------------------------------------//
  111. bool Segment::DoLoad()
  112. {
  113.  
  114.     // If already loaded, just return success
  115.     if(IsLoaded())
  116.         return true;
  117.  
  118.     // Check to see if we're allowed to create another segment
  119.     if(!DXAudioMgr()->CanAddSegment())
  120.     {
  121.         // If not, attempt to remove one segment to make room for it
  122.         if(!DXAudioMgr()->RemoveSegment(this))
  123.             return false;
  124.     }
  125.  
  126.     IAudioStream* pStream;
  127.     CreateAudioStream cas(pStream);
  128.     if(!pStream)
  129.         return false;
  130.     
  131.     HRESULT hr = pStream->Open(m_Init.m_sFileName);
  132.     if(FAILED(hr))
  133.         return Error::Handle("Failed to open segment file %s.", m_Init.m_sFileName.c_str());
  134.     
  135.     DMUS_OBJECTDESC      ObjDesc;
  136.     ZeroMemory(&ObjDesc, sizeof(DMUS_OBJECTDESC));
  137.     ObjDesc.dwSize = sizeof(DMUS_OBJECTDESC);
  138.     ObjDesc.guidClass = CLSID_DirectMusicSegment;
  139.     ObjDesc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_STREAM;
  140.     ObjDesc.pStream = pStream;
  141.  
  142.     // Load the data and retrieve the segment interface
  143.     hr = DXAudioMgr()->Loader()->GetObject(
  144.             &ObjDesc, 
  145.             IID_IDirectMusicSegment8, 
  146.             (void**) &m_pSegment );
  147.     if(FAILED(hr))
  148.     {    
  149.         Unload();
  150.         return Error::Handle("Could not load audio segment %s. Error = %s.", 
  151.             m_Init.m_sFileName.c_str(), DXGetErrorString(hr));
  152.     }
  153.  
  154.     // We must release the object from the cache since we are reusing
  155.     // the same IStream object to load different data.  This will otherwise
  156.     // confuse the cache system, resulting in errors.
  157.     hr = DXAudioMgr()->Loader()->ReleaseObjectByUnknown(m_pSegment);
  158.  
  159.     // Instruct the loader to clear out unused memory.
  160.     DXAudioMgr()->Loader()->CollectGarbage();
  161.  
  162.     // Download it
  163.     hr = m_pSegment->Download(DXAudioMgr()->Performance(m_Init.m_bMusic));
  164.     if(FAILED(hr))
  165.     {
  166.         Unload();
  167.         return Error::Handle("Could not download audio segment %s. Error = %s.", 
  168.             m_Init.m_sFileName.c_str(), DXGetErrorString(hr));
  169.     }
  170.  
  171.     // Insert the loaded segment into the audio list for management
  172.     DXAudioMgr()->OnLoadSegment(this);
  173.  
  174.     // Lock the segment's associate DLS file, if it exists
  175.     if(m_Init.m_pDLS)
  176.         m_Init.m_pDLS->Lock();
  177.  
  178.     // Set load status and play if needed
  179.     m_bLoading = false;
  180.     m_bLoaded = true;
  181.  
  182.     if(m_bQueuePlayback)
  183.         Play();
  184.  
  185.     return true;
  186. }
  187.  
  188. //------------------------------------------------------------------------//
  189. bool Segment::Unload()
  190. {
  191.     FN("Segment::Unload()");
  192.  
  193.     if(!IsLoaded())
  194.         return true;
  195.  
  196.     // Wait until a sound is completely loaded before unloading it
  197.     while(IsLoading());
  198.  
  199.     if(!Stop())
  200.     {  DebugOut(1, "Error stopping sound segment before unloading.");  }
  201.  
  202.     // Release the current segment state
  203.     SAFE_RELEASE(m_pSegState);
  204.  
  205.     if(m_pSegment)
  206.     {
  207.         HRESULT hr;
  208.         hr = m_pSegment->Unload(DXAudioMgr()->Performance(m_Init.m_bMusic));
  209.         if(FAILED(hr))
  210.         {  Error::Handle("Error unloading sound segment from performance.");  }
  211.  
  212.         // Unload the associated DLS file, or at least reduce the ref count
  213.         if(m_Init.m_pDLS)
  214.             m_Init.m_pDLS->Unlock();
  215.  
  216.         // Since we're loading and unloading segments dynamically, it is 
  217.         // important to release the loader's internal reference to it.
  218.         //hr = DXAudioMgr()->Loader()->ReleaseObjectByUnknown(m_pSegment);
  219.         if(FAILED(hr))
  220.         {  Error::Handle("Error unloading sound segment from loader.");  }
  221.  
  222.         // Remove the segment from the master list
  223.         DXAudioMgr()->OnUnloadSegment(this);
  224.  
  225.         // Instruct the loader to clear out unused memory.
  226.         //DXAudioMgr()->Loader()->CollectGarbage();
  227.  
  228.         // Now release the actual segment
  229.         SAFE_RELEASE(m_pSegment);
  230.     }
  231.  
  232.     // Mark the segment as unloaded
  233.     m_bLoaded = false;
  234.  
  235.     return true;
  236. }
  237.  
  238. //------------------------------------------------------------------------//
  239. /*
  240. Segment::Play() actually performs two actions.  If the segment is a non
  241. musical segment, it simply returns the result of Segment::PlaySegment(),
  242. which is a straightforward segment playing function.  If it is a musical
  243. segment, however, Play() attempts to properly place the segment as the
  244. next segment to play if there is one playing immediately, or will start
  245. playing immediately if no segments are currently playing.
  246. */
  247. bool Segment::Play()
  248. {
  249.     FN("Segment::Play()");
  250.  
  251.     // Get the time played for prioritization purposes
  252.     m_nLastTimePlayed = timeGetTime();
  253.  
  254.     // Determine if we need to load this buffer before playing.  After the
  255.     // buffer is finished loading, playback will begin automatically.
  256.     if(IsLoading())
  257.     {
  258.         m_bQueuePlayback = true;
  259.         return true;
  260.     }
  261.     else if(!IsLoaded())
  262.     {
  263.         m_bQueuePlayback = true;
  264.         return Load();
  265.     }
  266.     // Clear queue play flag
  267.     m_bQueuePlayback = false;
  268.  
  269.     // If this is a non-musical segment, play it immediately as a secondary segment
  270.     if(!m_Init.m_bMusic)
  271.         return DoPlay();
  272.  
  273.     // If there is a current segment, but it is stopped, clear it out in
  274.     // preparation for the next test.
  275.     if(DXAudioMgr()->GetCurrentSegment() && !DXAudioMgr()->GetCurrentSegment()->IsPlaying())
  276.         DXAudioMgr()->SetCurrentSegment(0);
  277.     
  278.     // If there is no current segment playing, set this segment
  279.     // as current and begin playing immediately.
  280.     if(!DXAudioMgr()->GetCurrentSegment())
  281.     {
  282.         DXAudioMgr()->SetCurrentSegment(this);
  283.         DXAudioMgr()->SetNextSegment(0);
  284.         return DoPlay();
  285.     }
  286.     // otherwise, queue this segment for playing after the current
  287.     // segment is finished.
  288.     else
  289.     {
  290.         DXAudioMgr()->SetNextSegment(this);
  291.     }
  292.     
  293.  
  294.     return true;
  295. }
  296.  
  297. //------------------------------------------------------------------------//
  298. // DoPlay() is used to actually start the segment playing.  The Play() 
  299. // function, in contrast, may simply queue a segment for playing next.
  300. bool Segment::DoPlay()
  301. {
  302.     FN("Segment::DoPlay()");
  303.  
  304.     if(!m_pSegment)
  305.         return false;
  306.  
  307.     // Set the flags based on the type of content (music or sound fx)
  308.     DWORD dwFlags = 0;
  309.     if(m_Init.m_bMusic)
  310.         dwFlags = DMUS_SEGF_QUEUE | DMUS_SEGF_DEFAULT;
  311.     else
  312.         dwFlags = DMUS_SEGF_SECONDARY;
  313.  
  314.     // If paused, start from the paused time
  315.     if(m_bPaused)
  316.         m_pSegment->SetStartPoint(m_iPauseTime);
  317.  
  318.     // Now play the segment
  319.     IDirectMusicSegmentState* pSegState = 0;
  320.     HRESULT hr;
  321.     hr = DXAudioMgr()->Performance(m_Init.m_bMusic)->PlaySegmentEx(
  322.         m_pSegment,                        // Segment to play.
  323.         NULL,                            // Used for songs; not implemented.
  324.         NULL,                            // For transitions. 
  325.         dwFlags,                        // Flags.
  326.         0,                                // Start time; 0 is immediate.
  327.         &pSegState,                        // Pointer that receives segment state.
  328.         NULL,                            // Object to stop.
  329.         NULL                            // Use default audiopath
  330.     );  
  331.     if(FAILED(hr))
  332.         return Error::Handle("Could not play audio segment %s. Error = %s.", 
  333.             m_Init.m_sFileName.c_str(), DXGetErrorString(hr));
  334.  
  335.     // Reset the new start time to the beginning of the segment
  336.     if(m_bPaused)
  337.     {
  338.         m_iPauseTime = 0;
  339.         m_pSegment->SetStartPoint(m_iPauseTime);
  340.     }
  341.  
  342.     // Not paused any more...
  343.     m_bPaused = false;
  344.  
  345.     // Get the segment state object
  346.     if(!pSegState)
  347.         return Error::Handle("Could not get segment state.  Some functions may not work");
  348.     hr = pSegState->QueryInterface(IID_IDirectMusicSegmentState8, (void**)&m_pSegState);
  349.     if(FAILED(hr))
  350.         return Error::Handle("Could not get segment state.  Some functions may not work");
  351.  
  352.     return true;
  353. }
  354.  
  355.  
  356. //------------------------------------------------------------------------//
  357. bool Segment::Stop()
  358. {
  359.     FN("Segment::Stop()");
  360.  
  361.     m_bQueuePlayback = false;
  362.  
  363.     // If the sound is already stopped (and it's not paused), then just return
  364.     if(!IsPlaying() && !IsPaused())
  365.         return true;
  366.  
  367.     // Stop the segment if it is available
  368.     if(m_pSegment)
  369.         DXAudioMgr()->Performance(m_Init.m_bMusic)->StopEx(m_pSegment, 0, 0);
  370.  
  371.     if(m_Init.m_bMusic)
  372.     {
  373.         DXAudioMgr()->SetCurrentSegment(0);
  374.         DXAudioMgr()->SetNextSegment(0);
  375.     }
  376.  
  377.     // Release the current segment state
  378.     SAFE_RELEASE(m_pSegState);
  379.  
  380.     return true;
  381. }
  382.  
  383.  
  384. //------------------------------------------------------------------------//
  385. bool Segment::Pause()
  386. {
  387.     FN("Segment::Pause()");
  388.     if(IsPlaying())
  389.     {
  390.         m_bPaused = true;
  391.         if(m_pSegState)
  392.             m_pSegState->GetSeek(&m_iPauseTime);
  393.  
  394.         if(m_pSegment)
  395.             DXAudioMgr()->Performance(m_Init.m_bMusic)->StopEx(m_pSegment, 0, 0);
  396.  
  397.         if(m_Init.m_bMusic)
  398.         {
  399.             DXAudioMgr()->SetCurrentSegment(0);
  400.             DXAudioMgr()->SetNextSegment(0);
  401.         }
  402.  
  403.     }
  404.  
  405.     // Release the current segment state
  406.     SAFE_RELEASE(m_pSegState);
  407.  
  408.     return true;
  409. }
  410.  
  411.  
  412. //------------------------------------------------------------------------//
  413. bool Segment::IsPlaying() const
  414. {
  415.     FN("Segment::IsPlaying()");
  416.     if(m_pSegment)
  417.     {
  418.         HRESULT hr = DXAudioMgr()->Performance(m_Init.m_bMusic)->IsPlaying(m_pSegment, NULL);
  419.         if(FAILED(hr))
  420.             return Error::Handle("Could not check playing status for segment.  Error = %s", DXGetErrorString(hr));
  421.         if(hr == S_OK)
  422.             return true;
  423.     }
  424.     return m_bQueuePlayback;
  425. }
  426.  
  427. //------------------------------------------------------------------------//
  428. bool Segment::IsPaused() const
  429. {
  430.     FN("Segment::IsPaused()");
  431.     return m_bPaused;
  432. }
  433.  
  434.  
  435. //------------------------------------------------------------------------//
  436. bool Segment::IsLooping() const
  437. {
  438.     FN("Segment::IsLooping()");
  439.     return m_Init.m_bLooping;
  440. }
  441.  
  442. //------------------------------------------------------------------------//
  443. uint32 Segment::GetLastPlayTime() const
  444. {
  445.     if(IsPlaying())
  446.         return 0xFFFFFFFF;
  447.     return m_nLastTimePlayed;
  448. }
  449.  
  450. //------------------------------------------------------------------------//
  451. bool Segment::operator < (const Segment& seg) const
  452. {
  453.     // The only criteria we compare for segments is
  454.     // the last time played
  455.     if(GetLastPlayTime() < seg.GetLastPlayTime())
  456.         return true;
  457.     return false;
  458. }
  459.  
  460.  
  461. // Property sets are not supported in this version of the library for segments
  462.  
  463. //------------------------------------------------------------------------//
  464. // Generic property support (for driver-specific extensions)
  465. bool Segment::QuerySupport(const GUID& guid, uint32 nID, uint32* pTypeSupport)
  466. {
  467.     FN("Segment::QuerySupport()");
  468.     return false;
  469. }
  470.  
  471. //------------------------------------------------------------------------//
  472. bool Segment::Get(const GUID& guidProperty, uint32 nID, void* pInstanceData,
  473.         uint32 nInstanceLength, void* pPropData, 
  474.     uint32 nPropLength, uint32* pBytesReturned)
  475. {
  476.     FN("Segment::Get()");
  477.     return false;
  478. }
  479.  
  480. //------------------------------------------------------------------------//
  481. bool Segment::Set(const GUID& guidProperty, uint32 nID, void* pInstanceData,
  482.         uint32 nInstanceLength, void* pPropData, 
  483.     uint32 nPropLength, bool bStoreProperty)
  484. {
  485.     FN("Segment::Set()");
  486.     return false;
  487. }
  488.  
  489.  
  490.  
  491.  
  492.  
  493.  
  494.  
  495.