//===========================================================================// // File: AudioRenderer.cpp // // Project: MUNGA Brick: Video Renderer // // Contents: Abstracted Base Video Renderer // //---------------------------------------------------------------------------// // Date Who Modification // // -------- --- ---------------------------------------------------------- // // 03/04/97 JTR Initial coding. // //---------------------------------------------------------------------------// // Copyright (C) 1997, Virtual World Entertainment, Inc. // // All Rights reserved worldwide // // This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL // //===========================================================================// #include "AdeptHeaders.hpp" #include "AudioCommand.hpp" #include "AudioRenderer.hpp" #include "AudioChannel.hpp" #include "AudioSample.hpp" //########################################################################## //############################ AudioCommand ########################## //########################################################################## AudioCommand::ClassData* AudioCommand::DefaultData = NULL; MemoryBlock* AudioCommand::s_AllocatedMemory = NULL; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AudioCommand::InitializeClass( size_t block_count, size_t block_delta ) { Verify(!s_AllocatedMemory); s_AllocatedMemory = new MemoryBlock( sizeof(AudioCommand), block_count, block_delta, "AudioCommand", g_LibraryHeap ); Register_Object(s_AllocatedMemory); Verify(!DefaultData); DefaultData = new ClassData( AudioCommandClassID, "Adept::AudioCommand", BaseClass::DefaultData ); Register_Object(DefaultData); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AudioCommand::TerminateClass() { Unregister_Object(DefaultData); delete DefaultData; DefaultData = NULL; Unregister_Object(s_AllocatedMemory); delete s_AllocatedMemory; s_AllocatedMemory = NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AudioCommand::TestInstance() const { Verify(IsDerivedFrom(DefaultData)); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // AudioCommand* AudioCommand::Create( int type, const ResourceID &hint_ID, Stuff::Scalar priority, Stuff::Scalar volume, Stuff::Scalar min_restart, Stuff::Scalar min_replace ) { return new AudioCommand( DefaultData, type, hint_ID, priority, volume, min_restart, min_replace ); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // AudioCommand* AudioCommand::Create( int type, const char *name, Stuff::Scalar priority, Stuff::Scalar volume, Stuff::Scalar min_restart, Stuff::Scalar min_replace ) { Resource hint(MString("Audio\\")+name); if (!hint.DoesResourceExist()) return NULL; return new AudioCommand( DefaultData, type, hint.GetResourceID(), priority, volume, min_restart, min_replace ); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // AudioCommand::AudioCommand( ClassData *class_data, int type, const ResourceID &sample_ID, Stuff::Scalar priority, Stuff::Scalar volume, Stuff::Scalar min_restart, Stuff::Scalar min_replace ): Plug(class_data), m_type(type), m_handleResourceID(sample_ID), m_priority(priority), m_volume(volume), m_minRestartDelay(min_restart), m_minReplaceDelay(min_replace), m_channel(NULL), m_playMode(gosAudio_Stop) { Check_Pointer(this); Verify((unsigned)type < AudioRenderer::Instance->m_channelTypes.GetLength()); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // AudioCommand::~AudioCommand() { Check_Object(this); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AudioCommand::Play(gosAudio_PlayMode play_mode) { Check_Object(this); Check_Object(AudioRenderer::Instance); // //--------------------------------------------------------------------- // Grab the sample (loading it if necessary), and figure out the delays //--------------------------------------------------------------------- // m_playMode = play_mode; AudioSample *sample = AudioRenderer::Instance->UseSample(m_handleResourceID); Check_Object(sample); sample->m_commands.Add(this); // //------------------------------------------------------------------------------------ // If this is going to be a loop, add it to the loop list. If nothing is available to // play it now, just return //------------------------------------------------------------------------------------ // if (play_mode == gosAudio_Loop) { if (!AddLoop()) return; } // //--------------------------------------------- // Put the loop command in the audio buffer now //--------------------------------------------- // AudioRenderer::Instance->AddNewCommand(this); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AudioCommand::Stop() { Check_Object(this); // //-------------------------------------------------------------------------------------------- // If we still have a channel, check the play mode. If we are play once, stopping the channel // will delete the command //-------------------------------------------------------------------------------------------- // if (m_channel) { if (m_playMode == gosAudio_PlayOnce) { Check_Object(m_channel); m_channel->Stop(); } // //--------------------------------------------------------------------------------------------- // We are a loop, so the stop command just put us on the loop queue. We need to kill ourselves // to truly stop the sound //--------------------------------------------------------------------------------------------- // else { Verify(m_playMode == gosAudio_Loop); Check_Object(m_channel); m_channel->Stop(); Check_Object(this); delete this; } } // //----------------------------------------------------------- // We don't have a channel, so we need to destroy the command //----------------------------------------------------------- // else delete this; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // bool AudioCommand::ConnectToChannel(AudioSample *sample) { Check_Object(this); Check_Object(sample); AUDIO_RENDER("Execute::NewCommands::AudioCommand"); // //--------------------------------------------------------------------------------- // If this is a queued type, go ahead and queue it immediately if there are no free // channels //--------------------------------------------------------------------------------- // Check_Object(AudioRenderer::Instance); ChannelType &type = AudioRenderer::Instance->m_channelTypes[m_type]; if (type.IsQueued() && type.m_inactiveChannels.IsEmpty()) { type.m_queuedCommands.Add(this); return false; } // //--------------------------- // Compute the command energy //--------------------------- // Time now = gos_GetElapsedTime(); AudioChannel *candidate = NULL; Scalar energy = m_energy = ComputeEnergy(NULL); // //---------------------------------------------------------------------------- // First look through the active channels to see if we can ignore this command //---------------------------------------------------------------------------- // AudioChannel *channel; if (m_playMode == gosAudio_PlayOnce) { ChainIteratorOf active_channels(&sample->m_activeChannels); while ((channel = active_channels.ReadAndNext()) != NULL) { Check_Object(channel); AudioCommand *existing_command = channel->m_command; Check_Object(existing_command); // //--------------------------------------------- // Can we merge the sound with an existing one? //--------------------------------------------- // if ( existing_command->m_volume >= m_volume && existing_command->m_priority <= m_priority && channel->m_startTime + existing_command->m_minRestartDelay >= now ) { delete this; return false; } // //------------------------------------------------------------ // Check to see if this channel is a candidate for replacement //------------------------------------------------------------ // if ( existing_command->m_volume <= m_volume && existing_command->m_priority >= m_priority && channel->m_startTime + existing_command->m_minReplaceDelay <= now ) { Scalar temp = channel->m_energy; if (temp <= energy) { candidate = channel; energy = temp; } } } } // //------------------------------------------------------ // Look for an inactive channel hooked up to this sample //------------------------------------------------------ // ChainIteratorOf inactive_channels(&sample->m_inactiveChannels); if ((channel = inactive_channels.GetCurrent()) != NULL) { channel->Activate(this); Verify(channel->m_startTime == now); return true; } // //------------------------------------------- // Replace the best candidate if there is one //------------------------------------------- // if (candidate) { Check_Object(candidate); candidate->Stop(); candidate->Activate(this); Verify(candidate->m_startTime == now); return true; } // //------------------------------------------------------------------------------------ // Now we search the inactive channel list for our sound type and use the earliest one //------------------------------------------------------------------------------------ // ChainIteratorOf free_channels(&type.m_inactiveChannels); if ((channel = free_channels.GetCurrent()) != NULL) { Check_Object(channel); channel->SetSample(sample); channel->Activate(this); Verify(channel->m_startTime == now); return true; } // //---------------------------------------------------------------------------------- // Now we search the active channel list for our sound type and use the earliest one // that also matches the playmode - loops steal loops and one-shots steal one-shots //---------------------------------------------------------------------------------- // ChainIteratorOf used_channels(&type.m_activeChannels); while ((channel = used_channels.ReadAndNext()) != NULL) { Check_Object(channel); if (channel->m_playMode == m_playMode) { channel->Stop(); if (channel->m_sample != sample) channel->SetSample(sample); channel->Activate(this); Verify(channel->m_startTime == now); return true; } } // //---------------------------------------------------------------------------------- // Now we search the active channel list for our sound type and use the earliest one //---------------------------------------------------------------------------------- // used_channels.First(); channel = used_channels.GetCurrent(); Check_Object(channel); channel->Stop(); if (channel->m_sample != sample) channel->SetSample(sample); channel->Activate(this); Verify(channel->m_startTime == now); return true; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // bool AudioCommand::AddLoop() { Check_Object(this); // //------------------------------------------------- // Hook us up to the loop chain of our channel type //------------------------------------------------- // Check_Object(AudioRenderer::Instance); ChannelType &type = AudioRenderer::Instance->m_channelTypes[m_type]; type.m_loopCommands.Add(this); return true; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Stuff::Scalar AudioCommand::ComputeEnergy(AudioChannel *channel) { Check_Object(this); // //-------------------------------------------------------------- // If we haven't been assigned a channel yet, our energy is full //-------------------------------------------------------------- // if (!channel) return 1.0f; // //---------------------------------------------------------------- // Otherwise, our energy decreases over the lifetime of the sample //---------------------------------------------------------------- // Check_Object(channel); AudioSample *sample = channel->m_sample; Check_Object(sample); return 1.0f - (static_cast(gos_GetElapsedTime() - channel->m_startTime)/sample->m_sampleInfo.fDuration); }