sfxProfile.cpp

Engine/source/sfx/sfxProfile.cpp

More...

Classes:

Public Functions

ConsoleDocClass(AudioProfile , "@brief Allows legacy <a href="/coding/class/classaudioprofile/">AudioProfile</a> datablocks <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be treated as <a href="/coding/class/classsfxprofile/">SFXProfile</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">datablocks.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxMisc\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Datablocks\n</a>" )
ConsoleDocClass(SFXProfile , "@brief Encapsulates <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> single sound <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> playback by the sound <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">system.\n\n</a>" "<a href="/coding/class/classsfxprofile/">SFXProfile</a> combines <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> sound description (<a href="/coding/class/classsfxdescription/">SFXDescription</a>) with <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> sound <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> such that it can be played " "by the sound system. To be able <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> play <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> sound file, the sound system will always require <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> profile " "<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> it <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be created. However, several of the <a href="/coding/file/sfxsystem_8h/#sfxsystem_8h_1a52e87f85ae30be82ffefd31b5c03e03d">SFX</a> functions(sfxPlayOnce(), sfxCreateSource()) perform " "this creation internally <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> convenience using temporary profile <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">objects.\n\n</a>" "Sound files can be in either OGG or WAV format. However, extended format support is available when using FMOD. " "See @ref <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">SFX_formats.\n\n</a>" " @section SFXProfile_loading Profile <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Loading\n\n</a>" "By default, the sound data referenced by <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> profile will be loaded when the profile is first played and the " "data then kept until either the profile is deleted or until the sound device on which the sound data is held " "is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">deleted.\n\n</a>" "This initial loading my incur <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> small delay when the sound is first played. To avoid this, <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> profile may be " "expicitly set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> load its sound data immediately when the profile is added <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the system. This is done by " "setting the #preload property <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">true.\n\n</a>" " @note Sounds using streamed playback(SFXDescription::isStreaming) cannot be preloaded and will thus " "ignore the #preload <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">flag.\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "datablock <a href="/coding/class/classsfxprofile/">SFXProfile</a>(Shore01Snd)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " fileName=\"art/sound/Lakeshore_mono_01\";\n" "   description  = Shore01Looping3d;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   preload      = true;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">SFX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Datablocks\n</a>" )
DefineEngineMethod(SFXProfile , getSoundDuration , F32 , () , "Return the length of the sound data in <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">seconds.\n\n</a>" "@return The length of the sound data in seconds or 0 <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the sound referenced by the profile could not be found." )

Detailed Description

Public Functions

ConsoleDocClass(AudioProfile , "@brief Allows legacy <a href="/coding/class/classaudioprofile/">AudioProfile</a> datablocks <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be treated as <a href="/coding/class/classsfxprofile/">SFXProfile</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">datablocks.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxMisc\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Datablocks\n</a>" )

ConsoleDocClass(SFXProfile , "@brief Encapsulates <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> single sound <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> playback by the sound <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">system.\n\n</a>" "<a href="/coding/class/classsfxprofile/">SFXProfile</a> combines <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> sound description (<a href="/coding/class/classsfxdescription/">SFXDescription</a>) with <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> sound <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> such that it can be played " "by the sound system. To be able <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> play <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> sound file, the sound system will always require <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> profile " "<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> it <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be created. However, several of the <a href="/coding/file/sfxsystem_8h/#sfxsystem_8h_1a52e87f85ae30be82ffefd31b5c03e03d">SFX</a> functions(sfxPlayOnce(), sfxCreateSource()) perform " "this creation internally <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> convenience using temporary profile <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">objects.\n\n</a>" "Sound files can be in either OGG or WAV format. However, extended format support is available when using FMOD. " "See @ref <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">SFX_formats.\n\n</a>" " @section SFXProfile_loading Profile <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Loading\n\n</a>" "By default, the sound data referenced by <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> profile will be loaded when the profile is first played and the " "data then kept until either the profile is deleted or until the sound device on which the sound data is held " "is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">deleted.\n\n</a>" "This initial loading my incur <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> small delay when the sound is first played. To avoid this, <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> profile may be " "expicitly set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> load its sound data immediately when the profile is added <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the system. This is done by " "setting the #preload property <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">true.\n\n</a>" " @note Sounds using streamed playback(SFXDescription::isStreaming) cannot be preloaded and will thus " "ignore the #preload <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">flag.\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "datablock <a href="/coding/class/classsfxprofile/">SFXProfile</a>(Shore01Snd)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " fileName=\"art/sound/Lakeshore_mono_01\";\n" "   description  = Shore01Looping3d;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   preload      = true;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">SFX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Datablocks\n</a>" )

DefineEngineMethod(SFXProfile , getSoundDuration , F32 , () , "Return the length of the sound data in <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">seconds.\n\n</a>" "@return The length of the sound data in seconds or 0 <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the sound referenced by the profile could not be found." )

IMPLEMENT_CO_DATABLOCK_V1(AudioProfile )

IMPLEMENT_CO_DATABLOCK_V1(SFXProfile )

  1
  2//-----------------------------------------------------------------------------
  3// Copyright (c) 2012 GarageGames, LLC
  4//
  5// Permission is hereby granted, free of charge, to any person obtaining a copy
  6// of this software and associated documentation files (the "Software"), to
  7// deal in the Software without restriction, including without limitation the
  8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9// sell copies of the Software, and to permit persons to whom the Software is
 10// furnished to do so, subject to the following conditions:
 11//
 12// The above copyright notice and this permission notice shall be included in
 13// all copies or substantial portions of the Software.
 14//
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 21// IN THE SOFTWARE.
 22//-----------------------------------------------------------------------------
 23
 24//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 25// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
 26// Copyright (C) 2015 Faust Logic, Inc.
 27//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 28
 29#include "platform/platform.h"
 30
 31#include "sfx/sfxProfile.h"
 32#include "sfx/sfxDescription.h"
 33#include "sfx/sfxSystem.h"
 34#include "sfx/sfxStream.h"
 35#include "sim/netConnection.h"
 36#include "core/stream/bitStream.h"
 37#include "core/resourceManager.h"
 38#include "console/engineAPI.h"
 39
 40using namespace Torque;
 41
 42IMPLEMENT_CO_DATABLOCK_V1( SFXProfile );
 43
 44
 45ConsoleDocClass( SFXProfile,
 46   "@brief Encapsulates a single sound file for playback by the sound system.\n\n"
 47   
 48   "SFXProfile combines a sound description (SFXDescription) with a sound file such that it can be played "
 49   "by the sound system.  To be able to play a sound file, the sound system will always require a profile "
 50   "for it to be created.  However, several of the SFX functions (sfxPlayOnce(), sfxCreateSource()) perform "
 51   "this creation internally for convenience using temporary profile objects.\n\n"
 52   
 53   "Sound files can be in either OGG or WAV format.  However, extended format support is available when using FMOD. "
 54   "See @ref SFX_formats.\n\n"
 55
 56   "@section SFXProfile_loading Profile Loading\n\n"
 57   
 58   "By default, the sound data referenced by a profile will be loaded when the profile is first played and the "
 59   "data then kept until either the profile is deleted or until the sound device on which the sound data is held "
 60   "is deleted.\n\n"
 61   
 62   "This initial loading my incur a small delay when the sound is first played.  To avoid this, a profile may be "
 63   "expicitly set to load its sound data immediately when the profile is added to the system.  This is done by "
 64   "setting the #preload property to true.\n\n"
 65   
 66   "@note Sounds using streamed playback (SFXDescription::isStreaming) cannot be preloaded and will thus "
 67      "ignore the #preload flag.\n\n"
 68      
 69   "@tsexample\n"
 70      "datablock SFXProfile( Shore01Snd )\n"
 71      "{\n"
 72      "   fileName     = \"art/sound/Lakeshore_mono_01\";\n"
 73      "   description  = Shore01Looping3d;\n"
 74      "   preload      = true;\n"
 75      "};\n"
 76   "@endtsexample\n\n"
 77      
 78   "@ingroup SFX\n"
 79   "@ingroup Datablocks\n"
 80);
 81
 82
 83//-----------------------------------------------------------------------------
 84
 85SFXProfile::SFXProfile()
 86   : mPreload( false )
 87{
 88}
 89
 90//-----------------------------------------------------------------------------
 91
 92SFXProfile::SFXProfile( SFXDescription* desc, const String& filename, bool preload )
 93   : Parent( desc ),
 94     mFilename( filename ),
 95     mPreload( preload )
 96{
 97}
 98
 99//-----------------------------------------------------------------------------
100
101
102//-----------------------------------------------------------------------------
103
104void SFXProfile::initPersistFields()
105{
106   addGroup( "Sound" );
107   
108      addField( "filename",    TypeStringFilename,        Offset( mFilename, SFXProfile ),
109         "%Path to the sound file.\n"
110         "If the extension is left out, it will be inferred by the sound system.  This allows to "
111         "easily switch the sound format without having to go through the profiles and change the "
112         "filenames there, too.\n" );
113      addField( "preload",     TypeBool,                  Offset( mPreload, SFXProfile ),
114         "Whether to preload sound data when the profile is added to system.\n"
115         "@note This flag is ignored by streamed sounds.\n\n"
116         "@ref SFXProfile_loading" );
117      
118   endGroup( "Sound" );
119
120   // disallow some field substitutions
121   disableFieldSubstitutions("description");
122
123   Parent::initPersistFields();
124}
125
126//-----------------------------------------------------------------------------
127
128bool SFXProfile::onAdd()
129{
130   if( !Parent::onAdd() )
131      return false;
132         
133   // If we're a streaming profile we don't preload
134   // or need device events.
135   if( SFX && !mDescription->mIsStreaming )
136   {
137      // If preload is enabled we load the resource
138      // and device buffer now to avoid a delay on
139      // first playback.
140      if( mPreload && !_preloadBuffer() )
141         Con::errorf( "SFXProfile(%s)::onAdd: The preload failed!", getName() );
142   }
143
144   _registerSignals();
145
146   return true;
147}
148
149//-----------------------------------------------------------------------------
150
151void SFXProfile::onRemove()
152{
153   _unregisterSignals();
154
155   Parent::onRemove();
156}
157
158//-----------------------------------------------------------------------------
159
160bool SFXProfile::preload( bool server, String &errorStr )
161{
162   if ( !Parent::preload( server, errorStr ) )
163      return false;
164
165   // TODO: Investigate how NetConnection::filesWereDownloaded()
166   // effects the system.
167
168   // Validate the datablock... has nothing to do with mPreload.
169   if(  !server &&
170        NetConnection::filesWereDownloaded() &&
171        ( mFilename.isEmpty() || !SFXResource::exists( mFilename ) ) )
172      return false;
173
174   return true;
175}
176
177//-----------------------------------------------------------------------------
178
179void SFXProfile::packData(BitStream* stream)
180{
181   Parent::packData( stream );
182
183   char buffer[256];
184   if ( mFilename.isEmpty() )
185      buffer[0] = 0;
186   else
187      dStrncpy( buffer, mFilename.c_str(), 256 );
188   stream->writeString( buffer );
189
190   stream->writeFlag( mPreload );
191}
192
193//-----------------------------------------------------------------------------
194
195void SFXProfile::unpackData(BitStream* stream)
196{
197   Parent::unpackData( stream );
198
199   char buffer[256];
200   stream->readString( buffer );
201   mFilename = buffer;
202
203   mPreload = stream->readFlag();
204}
205
206//-----------------------------------------------------------------------------
207
208bool SFXProfile::isLooping() const
209{
210   return getDescription()->mIsLooping;
211}
212
213//-----------------------------------------------------------------------------
214
215void SFXProfile::_registerSignals()
216{
217   SFX->getEventSignal().notify( this, &SFXProfile::_onDeviceEvent );
218   ResourceManager::get().getChangedSignal().notify( this, &SFXProfile::_onResourceChanged );
219}
220
221//-----------------------------------------------------------------------------
222
223void SFXProfile::_unregisterSignals()
224{
225   ResourceManager::get().getChangedSignal().remove( this, &SFXProfile::_onResourceChanged );
226   if( SFX )
227      SFX->getEventSignal().remove( this, &SFXProfile::_onDeviceEvent );
228}
229
230//-----------------------------------------------------------------------------
231
232void SFXProfile::_onDeviceEvent( SFXSystemEventType evt )
233{
234   switch( evt )
235   {
236      case SFXSystemEvent_CreateDevice:
237      {
238         if( mPreload && !mDescription->mIsStreaming && !_preloadBuffer() )
239            Con::errorf( "SFXProfile::_onDeviceEvent: The preload failed! %s", getName() );
240         break;
241      }
242      
243      default:
244         break;
245   }
246}
247
248//-----------------------------------------------------------------------------
249
250void SFXProfile::_onResourceChanged( const Torque::Path& path )
251{
252   if( path != Path( mFilename ) )
253      return;
254   
255   // Let go of the old resource and buffer.
256            
257   mResource = NULL;
258   mBuffer = NULL;
259      
260   // Load the new resource.
261      
262   getResource();
263      
264   if( mPreload && !mDescription->mIsStreaming )
265   {
266      if( !_preloadBuffer() )
267         Con::errorf( "SFXProfile::_onResourceChanged() - failed to preload '%s'", mFilename.c_str() );
268   }
269      
270   mChangedSignal.trigger( this );
271}
272
273//-----------------------------------------------------------------------------
274
275bool SFXProfile::_preloadBuffer()
276{
277   AssertFatal( !mDescription->mIsStreaming, "SFXProfile::_preloadBuffer() - must not be called for streaming profiles" );
278
279   mBuffer = _createBuffer();
280   return ( !mBuffer.isNull() );
281}
282
283//-----------------------------------------------------------------------------
284
285Resource<SFXResource>& SFXProfile::getResource()
286{
287   if( !mResource && !mFilename.isEmpty() )
288      mResource = SFXResource::load( mFilename );
289
290   return mResource;
291}
292
293//-----------------------------------------------------------------------------
294
295SFXBuffer* SFXProfile::getBuffer()
296{
297   if ( mDescription->mIsStreaming )
298   {
299      // Streaming requires unique buffers per 
300      // source, so this creates a new buffer.
301      if ( SFX )
302         return _createBuffer();
303
304      return NULL;
305   }
306
307   if ( mBuffer.isNull() )
308      _preloadBuffer();
309
310   return mBuffer;
311}
312
313//-----------------------------------------------------------------------------
314
315SFXBuffer* SFXProfile::_createBuffer()
316{
317   SFXBuffer* buffer = 0;
318   
319   // Try to create through SFXDevie.
320   
321   if( !mFilename.isEmpty() && SFX )
322   {
323      buffer = SFX->_createBuffer( mFilename, mDescription );
324      if( buffer )
325      {
326         #ifdef TORQUE_DEBUG
327         const SFXFormat& format = buffer->getFormat();
328         Con::printf( "%s SFX: %s (%i channels, %i kHz, %.02f sec, %i kb)",
329            mDescription->mIsStreaming ? "Streaming" : "Loaded", mFilename.c_str(),
330            format.getChannels(),
331            format.getSamplesPerSecond() / 1000,
332            F32( buffer->getDuration() ) / 1000.0f,
333            format.getDataLength( buffer->getDuration() ) / 1024 );
334         #endif
335      }
336   }
337   
338   // If that failed, load through SFXResource.
339   
340   if( !buffer )
341   {
342      Resource< SFXResource>& resource = getResource();
343      if( resource != NULL && SFX )
344      {
345         #ifdef TORQUE_DEBUG
346         const SFXFormat& format = resource->getFormat();
347         Con::printf( "%s SFX: %s (%i channels, %i kHz, %.02f sec, %i kb)",
348            mDescription->mIsStreaming ? "Streaming" : "Loading", resource->getFileName().c_str(),
349            format.getChannels(),
350            format.getSamplesPerSecond() / 1000,
351            F32( resource->getDuration() ) / 1000.0f,
352            format.getDataLength( resource->getDuration() ) / 1024 );
353         #endif
354
355         ThreadSafeRef< SFXStream> sfxStream = resource->openStream();
356         buffer = SFX->_createBuffer( sfxStream, mDescription );
357      }
358   }
359
360   return buffer;
361}
362
363//-----------------------------------------------------------------------------
364
365U32 SFXProfile::getSoundDuration()
366{
367   Resource< SFXResource>& resource = getResource();
368   if( resource != NULL )
369      return mResource->getDuration();
370   else
371      return 0;
372}
373
374//-----------------------------------------------------------------------------
375
376DefineEngineMethod( SFXProfile, getSoundDuration, F32, (),,
377   "Return the length of the sound data in seconds.\n\n"
378   "@return The length of the sound data in seconds or 0 if the sound referenced by the profile could not be found." )
379{
380   return ( F32 ) object->getSoundDuration() * 0.001f;
381}
382
383// enable this to help verify that temp-clones of AudioProfile are being deleted
384//#define TRACK_AUDIO_PROFILE_CLONES
385
386#ifdef TRACK_AUDIO_PROFILE_CLONES
387static int audio_prof_clones = 0;
388#endif
389
390SFXProfile::SFXProfile(const SFXProfile& other, bool temp_clone) : SFXTrack(other, temp_clone)
391{
392#ifdef TRACK_AUDIO_PROFILE_CLONES
393   audio_prof_clones++;
394   if (audio_prof_clones == 1)
395     Con::errorf("SFXProfile -- Clones are on the loose!");
396#endif
397   mResource = other.mResource;
398   mFilename = other.mFilename;
399   mPreload = other.mPreload;
400   mBuffer = other.mBuffer; // -- AudioBuffer loaded using mFilename
401   mChangedSignal = other.mChangedSignal;
402}
403
404SFXProfile::~SFXProfile()
405{
406  if (!isTempClone())
407    return;
408
409  // cleanup after a temp-clone
410
411  if (mDescription && mDescription->isTempClone())
412  {
413    delete mDescription;
414    mDescription = 0;
415  }
416
417#ifdef TRACK_AUDIO_PROFILE_CLONES
418  if (audio_prof_clones > 0)
419  {
420    audio_prof_clones--;
421    if (audio_prof_clones == 0)
422      Con::errorf("SFXProfile -- Clones eliminated!");
423  }
424  else
425    Con::errorf("SFXProfile -- Too many clones deleted!");
426#endif
427}
428
429// Clone and perform substitutions on the SFXProfile and on any SFXDescription
430// it references.
431SFXProfile* SFXProfile::cloneAndPerformSubstitutions(const SimObject* owner, S32 index)
432{
433  if (!owner)
434    return this;
435
436  SFXProfile* sub_profile_db = this;
437
438  // look for mDescriptionObject subs
439  SFXDescription* desc_db;
440  if (mDescription && mDescription->getSubstitutionCount() > 0)
441  {
442    SFXDescription* orig_db = mDescription;
443    desc_db = new SFXDescription(*orig_db, true);
444    orig_db->performSubstitutions(desc_db, owner, index);
445  }
446  else
447    desc_db = 0;
448
449  if (this->getSubstitutionCount() > 0 || desc_db)
450  {
451    sub_profile_db = new SFXProfile(*this, true);
452    performSubstitutions(sub_profile_db, owner, index);
453    if (desc_db)
454      sub_profile_db->mDescription = desc_db;
455  }
456
457  return sub_profile_db;
458}
459
460void SFXProfile::onPerformSubstitutions() 
461{ 
462   if ( SFX )
463   {
464      // If preload is enabled we load the resource
465      // and device buffer now to avoid a delay on
466      // first playback.
467      if ( mPreload && !_preloadBuffer() )
468         Con::errorf( "SFXProfile(%s)::onPerformSubstitutions: The preload failed!", getName() );
469
470      // We need to get device change notifications.
471      SFX->getEventSignal().notify( this, &SFXProfile::_onDeviceEvent );
472   }
473}
474// This allows legacy AudioProfile datablocks to be recognized as an alias
475// for SFXProfile. It is intended to ease the transition from older scripts
476// especially those that still need to support pre-1.7 applications.
477// (This maybe removed in future releases so treat as deprecated.)
478class AudioProfile : public SFXProfile
479{
480   typedef SFXProfile Parent;
481public:
482   DECLARE_CONOBJECT(AudioProfile);
483};
484
485IMPLEMENT_CO_DATABLOCK_V1(AudioProfile);
486
487ConsoleDocClass( AudioProfile,
488   "@brief Allows legacy AudioProfile datablocks to be treated as SFXProfile datablocks.\n\n"
489
490   "@ingroup afxMisc\n"
491   "@ingroup AFX\n"
492   "@ingroup Datablocks\n"
493);
494
495