Initial commit of Command & Conquer Generals and Command & Conquer Generals Zero Hour source code.
This commit is contained in:
parent
2e338c00cb
commit
3d0ee53a05
6072 changed files with 2283311 additions and 0 deletions
848
Generals/Code/GameEngine/Source/Common/Audio/AudioEventRTS.cpp
Normal file
848
Generals/Code/GameEngine/Source/Common/Audio/AudioEventRTS.cpp
Normal file
|
@ -0,0 +1,848 @@
|
|||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: AudioEventRTS.cpp
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* EA Pacific */
|
||||
/* Confidential Information */
|
||||
/* Copyright (C) 2001 - All Rights Reserved */
|
||||
/* DO NOT DISTRIBUTE */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Project: RTS3 */
|
||||
/* File name: AudioEventRTS.cpp */
|
||||
/* Created: John K. McDonald, Jr., 3/21/2002 */
|
||||
/* Desc: AudioEventRTS constructors and assignment operator, etc. */
|
||||
/* Revision History: */
|
||||
/* 3/21/2002 : Initial creation */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/AudioEventRTS.h"
|
||||
|
||||
#include "Common/AudioEventInfo.h"
|
||||
#include "Common/AudioRandomValue.h"
|
||||
#include "Common/AudioSettings.h"
|
||||
#include "Common/File.h"
|
||||
#include "Common/FileSystem.h"
|
||||
#include "Common/GameSounds.h"
|
||||
#include "Common/GlobalData.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/Registry.h"
|
||||
|
||||
#include "GameLogic/GameLogic.h" // For getObjectByID
|
||||
#include "GameLogic/LogicRandomValue.h"
|
||||
#include "GameLogic/Object.h"
|
||||
|
||||
#include "GameClient/Drawable.h" // For getPosition
|
||||
#include "GameClient/GameClient.h" // For getDrawableByID
|
||||
|
||||
#ifdef _INTERNAL
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AudioEventRTS::AudioEventRTS()
|
||||
: m_eventName(AsciiString::TheEmptyString),
|
||||
m_priority(AP_NORMAL),
|
||||
m_volume(-1.0),
|
||||
m_timeOfDay(TIME_OF_DAY_AFTERNOON),
|
||||
m_ownerType(OT_INVALID),
|
||||
m_shouldFade(false),
|
||||
m_isLogicalAudio(false),
|
||||
m_filenameToLoad(AsciiString::TheEmptyString),
|
||||
m_eventInfo(NULL),
|
||||
m_playingHandle(0),
|
||||
m_killThisHandle(0),
|
||||
m_pitchShift(1.0),
|
||||
m_volumeShift(0.0),
|
||||
m_loopCount(1),
|
||||
m_playingAudioIndex(-1),
|
||||
m_allCount(0),
|
||||
m_playerIndex(-1),
|
||||
m_delay(0.0f),
|
||||
m_uninterruptable(FALSE)
|
||||
{
|
||||
m_attackName.clear();
|
||||
m_decayName.clear();
|
||||
m_positionOfAudio.zero();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AudioEventRTS::AudioEventRTS( const AsciiString& eventName )
|
||||
: m_eventName(eventName),
|
||||
m_priority(AP_NORMAL),
|
||||
m_volume(-1.0),
|
||||
m_timeOfDay(TIME_OF_DAY_AFTERNOON),
|
||||
m_ownerType(OT_INVALID),
|
||||
m_shouldFade(false),
|
||||
m_isLogicalAudio(false),
|
||||
m_filenameToLoad(AsciiString::TheEmptyString),
|
||||
m_eventInfo(NULL),
|
||||
m_playingHandle(0),
|
||||
m_killThisHandle(0),
|
||||
m_pitchShift(1.0),
|
||||
m_volumeShift(0.0),
|
||||
m_loopCount(1),
|
||||
m_playingAudioIndex(-1),
|
||||
m_allCount(0),
|
||||
m_playerIndex(-1),
|
||||
m_delay(0.0f),
|
||||
m_uninterruptable(FALSE)
|
||||
{
|
||||
m_attackName.clear();
|
||||
m_decayName.clear();
|
||||
m_positionOfAudio.zero();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AudioEventRTS::AudioEventRTS( const AsciiString& eventName, ObjectID ownerID )
|
||||
: m_eventName(eventName),
|
||||
m_priority(AP_NORMAL),
|
||||
m_volume(-1.0),
|
||||
m_timeOfDay(TIME_OF_DAY_AFTERNOON),
|
||||
m_objectID(ownerID),
|
||||
m_ownerType(OT_INVALID),
|
||||
m_shouldFade(false),
|
||||
m_isLogicalAudio(false),
|
||||
m_filenameToLoad(AsciiString::TheEmptyString),
|
||||
m_eventInfo(NULL),
|
||||
m_playingHandle(0),
|
||||
m_killThisHandle(0),
|
||||
m_pitchShift(1.0),
|
||||
m_volumeShift(0.0),
|
||||
m_loopCount(1),
|
||||
m_playingAudioIndex(-1),
|
||||
m_allCount(0),
|
||||
m_playerIndex(-1),
|
||||
m_delay(0.0f),
|
||||
m_uninterruptable(FALSE)
|
||||
{
|
||||
m_attackName.clear();
|
||||
m_decayName.clear();
|
||||
|
||||
if( m_objectID )
|
||||
{
|
||||
m_ownerType = OT_Object;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_objectID = INVALID_ID;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AudioEventRTS::AudioEventRTS( const AsciiString& eventName, DrawableID drawableID )
|
||||
: m_eventName(eventName),
|
||||
m_priority(AP_NORMAL),
|
||||
m_volume(-1.0),
|
||||
m_timeOfDay(TIME_OF_DAY_AFTERNOON),
|
||||
m_drawableID(drawableID),
|
||||
m_ownerType(OT_INVALID),
|
||||
m_shouldFade(false),
|
||||
m_isLogicalAudio(false),
|
||||
m_filenameToLoad(AsciiString::TheEmptyString),
|
||||
m_eventInfo(NULL),
|
||||
m_playingHandle(0),
|
||||
m_killThisHandle(0),
|
||||
m_pitchShift(1.0),
|
||||
m_volumeShift(0.0),
|
||||
m_loopCount(1),
|
||||
m_playingAudioIndex(-1),
|
||||
m_allCount(0),
|
||||
m_playerIndex(-1),
|
||||
m_delay(0.0f),
|
||||
m_uninterruptable(FALSE)
|
||||
{
|
||||
m_attackName.clear();
|
||||
m_decayName.clear();
|
||||
|
||||
if( m_drawableID )
|
||||
{
|
||||
m_ownerType = OT_Drawable;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_drawableID = INVALID_DRAWABLE_ID;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AudioEventRTS::AudioEventRTS( const AsciiString& eventName, const Coord3D *positionOfAudio )
|
||||
: m_eventName(eventName),
|
||||
m_priority(AP_NORMAL),
|
||||
m_volume(-1.0),
|
||||
m_timeOfDay(TIME_OF_DAY_AFTERNOON),
|
||||
m_ownerType(OT_Positional),
|
||||
m_shouldFade(false),
|
||||
m_isLogicalAudio(false),
|
||||
m_filenameToLoad(AsciiString::TheEmptyString),
|
||||
m_eventInfo(NULL),
|
||||
m_playingHandle(0),
|
||||
m_killThisHandle(0),
|
||||
m_pitchShift(1.0),
|
||||
m_volumeShift(0.0),
|
||||
m_loopCount(1),
|
||||
m_playingAudioIndex(-1),
|
||||
m_allCount(0),
|
||||
m_playerIndex(-1),
|
||||
m_delay(0.0f),
|
||||
m_uninterruptable(FALSE)
|
||||
{
|
||||
m_positionOfAudio.set( positionOfAudio );
|
||||
m_attackName.clear();
|
||||
m_decayName.clear();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AudioEventRTS::AudioEventRTS( const AudioEventRTS& right )
|
||||
{
|
||||
m_filenameToLoad = right.m_filenameToLoad;
|
||||
m_eventInfo = right.m_eventInfo;
|
||||
m_playingHandle = right.m_playingHandle;
|
||||
m_killThisHandle = right.m_killThisHandle;
|
||||
m_eventName = right.m_eventName;
|
||||
m_priority = right.m_priority;
|
||||
m_volume = right.m_volume;
|
||||
m_timeOfDay = right.m_timeOfDay;
|
||||
m_ownerType = right.m_ownerType;
|
||||
m_shouldFade = right.m_shouldFade;
|
||||
m_isLogicalAudio = right.m_isLogicalAudio;
|
||||
m_pitchShift = right.m_pitchShift;
|
||||
m_volumeShift = right.m_volumeShift;
|
||||
m_loopCount = right.m_loopCount;
|
||||
m_playingAudioIndex = right.m_playingAudioIndex;
|
||||
m_allCount = right.m_allCount;
|
||||
m_playerIndex = right.m_playerIndex;
|
||||
m_delay = right.m_delay;
|
||||
m_attackName = right.m_attackName;
|
||||
m_decayName = right.m_decayName;
|
||||
m_portionToPlayNext = right.m_portionToPlayNext;
|
||||
m_uninterruptable = right.m_uninterruptable;
|
||||
|
||||
if( m_ownerType == OT_Positional || m_ownerType == OT_Dead )
|
||||
{
|
||||
m_positionOfAudio.set( &right.m_positionOfAudio );
|
||||
}
|
||||
else if( m_ownerType == OT_Drawable )
|
||||
{
|
||||
m_drawableID = right.m_drawableID;
|
||||
}
|
||||
else if( m_ownerType == OT_Object )
|
||||
{
|
||||
m_objectID = right.m_objectID;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AudioEventRTS& AudioEventRTS::operator=( const AudioEventRTS& right )
|
||||
{
|
||||
m_filenameToLoad = right.m_filenameToLoad;
|
||||
m_eventInfo = right.m_eventInfo;
|
||||
m_playingHandle = right.m_playingHandle;
|
||||
m_killThisHandle = right.m_killThisHandle;
|
||||
m_eventName = right.m_eventName;
|
||||
m_priority = right.m_priority;
|
||||
m_volume = right.m_volume;
|
||||
m_timeOfDay = right.m_timeOfDay;
|
||||
m_ownerType = right.m_ownerType;
|
||||
m_shouldFade = right.m_shouldFade;
|
||||
m_isLogicalAudio = right.m_isLogicalAudio;
|
||||
m_pitchShift = right.m_pitchShift;
|
||||
m_volumeShift = right.m_volumeShift;
|
||||
m_loopCount = right.m_loopCount;
|
||||
m_playingAudioIndex = right.m_playingAudioIndex;
|
||||
m_allCount = right.m_allCount;
|
||||
m_playerIndex = right.m_playerIndex;
|
||||
m_delay = right.m_delay;
|
||||
m_attackName = right.m_attackName;
|
||||
m_decayName = right.m_decayName;
|
||||
m_portionToPlayNext = right.m_portionToPlayNext;
|
||||
m_uninterruptable = right.m_uninterruptable;
|
||||
|
||||
if( m_ownerType == OT_Positional || m_ownerType == OT_Dead )
|
||||
{
|
||||
m_positionOfAudio.set( &right.m_positionOfAudio );
|
||||
}
|
||||
else if( m_ownerType == OT_Drawable )
|
||||
{
|
||||
m_drawableID = right.m_drawableID;
|
||||
}
|
||||
else if( m_ownerType == OT_Object )
|
||||
{
|
||||
m_objectID = right.m_objectID;
|
||||
}
|
||||
return *this;
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AudioEventRTS::~AudioEventRTS()
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setEventName( AsciiString name )
|
||||
{
|
||||
if ((name != m_eventName) && m_eventInfo != NULL) {
|
||||
// Clear out the audio event info, cause its not valid for the new event.
|
||||
m_eventInfo = NULL;
|
||||
}
|
||||
|
||||
m_eventName = name;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::generateFilename( void )
|
||||
{
|
||||
// A Logic Random Value is used because we may ask "How long will it take to play this sound?"
|
||||
// In that case, we need the same answer across all pcs.
|
||||
|
||||
if (!m_eventInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_filenameToLoad = generateFilenamePrefix(m_eventInfo->m_soundType, false);
|
||||
|
||||
Int which = 0;
|
||||
|
||||
if (m_eventInfo->m_soundType == AT_Music || m_eventInfo->m_soundType == AT_Streaming) {
|
||||
m_filenameToLoad.concat(m_eventInfo->m_filename);
|
||||
adjustForLocalization(m_filenameToLoad);
|
||||
return;
|
||||
} else {
|
||||
if (m_eventInfo->m_sounds.size() == 0) {
|
||||
m_filenameToLoad = AsciiString::TheEmptyString;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (BitTest(m_eventInfo->m_control, AC_RANDOM))
|
||||
{
|
||||
if (m_isLogicalAudio)
|
||||
{
|
||||
which = GameLogicRandomValue(0, m_eventInfo->m_sounds.size() - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
which = GameAudioRandomValue(0, m_eventInfo->m_sounds.size() - 1);
|
||||
}
|
||||
|
||||
if (which == m_playingAudioIndex && m_eventInfo->m_sounds.size() > 2)
|
||||
which = ( which + 1 ) % m_eventInfo->m_sounds.size();
|
||||
|
||||
m_playingAudioIndex = which;//caching random choice to compare next call
|
||||
|
||||
}
|
||||
else
|
||||
which = (++m_playingAudioIndex) % m_eventInfo->m_sounds.size();
|
||||
|
||||
|
||||
}
|
||||
|
||||
m_filenameToLoad.concat(m_eventInfo->m_sounds[which]);
|
||||
m_filenameToLoad.concat(generateFilenameExtension(m_eventInfo->m_soundType));
|
||||
adjustForLocalization(m_filenameToLoad);
|
||||
|
||||
// Note: Also generate Delay when generating a filename, cause
|
||||
// we want delay to apply between every loop of a sound.
|
||||
m_delay = GameAudioRandomValueReal(m_eventInfo->m_delayMin, m_eventInfo->m_delayMax);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AsciiString AudioEventRTS::getFilename( void )
|
||||
{
|
||||
return m_filenameToLoad;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::generatePlayInfo( void )
|
||||
{
|
||||
m_pitchShift = GameAudioRandomValueReal(m_eventInfo->m_pitchShiftMin, m_eventInfo->m_pitchShiftMax);
|
||||
m_volumeShift = GameAudioRandomValueReal(1.0f + m_eventInfo->m_volumeShift, 1.0f); // volume shifts are between 0 and 1
|
||||
m_loopCount = m_eventInfo->m_loopCount;
|
||||
|
||||
m_portionToPlayNext = PP_Attack;
|
||||
Int attackSize = m_eventInfo->m_attackSounds.size();
|
||||
if (attackSize > 0) {
|
||||
m_attackName = generateFilenamePrefix(m_eventInfo->m_soundType, false);
|
||||
// needs to be logic because it needs to be the same on all systems.
|
||||
Int attackToPlay;
|
||||
if (m_isLogicalAudio) {
|
||||
attackToPlay = GameLogicRandomValue(0, attackSize - 1);
|
||||
} else {
|
||||
attackToPlay = GameAudioRandomValue(0, attackSize - 1);
|
||||
}
|
||||
|
||||
m_attackName.concat(m_eventInfo->m_attackSounds[attackToPlay]);
|
||||
m_attackName.concat(generateFilenameExtension(m_eventInfo->m_soundType));
|
||||
adjustForLocalization(m_attackName);
|
||||
} else {
|
||||
m_portionToPlayNext = PP_Sound;
|
||||
}
|
||||
|
||||
Int decaySize = m_eventInfo->m_decaySounds.size();
|
||||
if (decaySize > 0) {
|
||||
m_decayName = generateFilenamePrefix(m_eventInfo->m_soundType, false);
|
||||
// needs to be logic because it needs to be the same on all systems.
|
||||
Int decayToPlay;
|
||||
if (m_isLogicalAudio) {
|
||||
decayToPlay = GameLogicRandomValue(0, decaySize - 1);
|
||||
} else {
|
||||
decayToPlay = GameAudioRandomValue(0, decaySize - 1);
|
||||
}
|
||||
|
||||
m_decayName.concat(m_eventInfo->m_decaySounds[decayToPlay]);
|
||||
m_decayName.concat(generateFilenameExtension(m_eventInfo->m_soundType));
|
||||
adjustForLocalization(m_decayName);
|
||||
}
|
||||
|
||||
m_isLogicalAudio = FALSE;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Real AudioEventRTS::getPitchShift( void ) const
|
||||
{
|
||||
return m_pitchShift;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Real AudioEventRTS::getVolumeShift( void ) const
|
||||
{
|
||||
return m_volumeShift;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AsciiString AudioEventRTS::getAttackFilename( void ) const
|
||||
{
|
||||
return m_attackName;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AsciiString AudioEventRTS::getDecayFilename( void ) const
|
||||
{
|
||||
return m_decayName;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Real AudioEventRTS::getDelay( void ) const
|
||||
{
|
||||
return m_delay;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::decrementDelay( Real timeToDecrement )
|
||||
{
|
||||
m_delay -= timeToDecrement;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
PortionToPlay AudioEventRTS::getNextPlayPortion( void ) const
|
||||
{
|
||||
return m_portionToPlayNext;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::advanceNextPlayPortion( void )
|
||||
{
|
||||
switch (m_portionToPlayNext)
|
||||
{
|
||||
case PP_Attack:
|
||||
m_portionToPlayNext = PP_Sound;
|
||||
break;
|
||||
case PP_Sound:
|
||||
if (m_eventInfo && BitTest(m_eventInfo->m_control, AC_ALL))
|
||||
{
|
||||
if (m_allCount == m_eventInfo->m_sounds.size()) {
|
||||
m_portionToPlayNext = PP_Decay;
|
||||
}
|
||||
|
||||
// Advance the all count so that we move to the next sound.
|
||||
++m_allCount;
|
||||
}
|
||||
if (!m_decayName.isEmpty()) {
|
||||
m_portionToPlayNext = PP_Decay;
|
||||
} else {
|
||||
m_portionToPlayNext = PP_Done;
|
||||
}
|
||||
break;
|
||||
case PP_Decay:
|
||||
m_portionToPlayNext = PP_Done;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setNextPlayPortion( PortionToPlay ptp )
|
||||
{
|
||||
m_portionToPlayNext = ptp;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::decreaseLoopCount( void )
|
||||
{
|
||||
if (m_loopCount == 1) {
|
||||
m_loopCount = -1;
|
||||
} else if (m_loopCount > 1) {
|
||||
--m_loopCount;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool AudioEventRTS::hasMoreLoops( void ) const
|
||||
{
|
||||
return (m_loopCount >= 0);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setAudioEventInfo( const AudioEventInfo *eventInfo ) const
|
||||
{
|
||||
m_eventInfo = eventInfo;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
const AudioEventInfo *AudioEventRTS::getAudioEventInfo( void ) const
|
||||
{
|
||||
if (m_eventInfo) {
|
||||
if (m_eventInfo->m_audioName == m_eventName) {
|
||||
return m_eventInfo;
|
||||
} else {
|
||||
m_eventInfo = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return m_eventInfo;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setPlayingHandle( AudioHandle handle )
|
||||
{
|
||||
m_playingHandle = handle;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AudioHandle AudioEventRTS::getPlayingHandle( void )
|
||||
{
|
||||
return m_playingHandle;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setPosition( const Coord3D *pos )
|
||||
{
|
||||
if (!pos) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(m_ownerType == OT_Positional || m_ownerType == OT_INVALID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_positionOfAudio = *pos;
|
||||
m_ownerType = OT_Positional;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
const Coord3D* AudioEventRTS::getPosition( void )
|
||||
{
|
||||
if( m_ownerType != OT_INVALID )
|
||||
{
|
||||
return &m_positionOfAudio;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setObjectID( ObjectID objID )
|
||||
{
|
||||
if (!(m_ownerType == OT_Object || m_ownerType == OT_INVALID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_objectID = objID;
|
||||
m_ownerType = OT_Object;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
ObjectID AudioEventRTS::getObjectID( void )
|
||||
{
|
||||
if (m_ownerType == OT_Object) {
|
||||
return m_objectID;
|
||||
}
|
||||
|
||||
return INVALID_ID;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setDrawableID( DrawableID drawID )
|
||||
{
|
||||
if (!(m_ownerType == OT_Drawable || m_ownerType == OT_INVALID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_drawableID = drawID;
|
||||
m_ownerType = OT_Drawable;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
DrawableID AudioEventRTS::getDrawableID( void )
|
||||
{
|
||||
if (m_ownerType == OT_Drawable) {
|
||||
return m_drawableID;
|
||||
}
|
||||
|
||||
return INVALID_DRAWABLE_ID;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setTimeOfDay( TimeOfDay tod )
|
||||
{
|
||||
m_timeOfDay = tod;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
TimeOfDay AudioEventRTS::getTimeOfDay( void ) const
|
||||
{
|
||||
return m_timeOfDay;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setHandleToKill( AudioHandle handleToKill )
|
||||
{
|
||||
m_killThisHandle = handleToKill;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AudioHandle AudioEventRTS::getHandleToKill( void ) const
|
||||
{
|
||||
return m_killThisHandle;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setShouldFade( Bool shouldFade )
|
||||
{
|
||||
m_shouldFade = shouldFade;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool AudioEventRTS::getShouldFade( void ) const
|
||||
{
|
||||
return m_shouldFade;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setIsLogicalAudio( Bool isLogicalAudio )
|
||||
{
|
||||
m_isLogicalAudio = isLogicalAudio;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool AudioEventRTS::getIsLogicalAudio( void ) const
|
||||
{
|
||||
return m_isLogicalAudio;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool AudioEventRTS::isPositionalAudio( void ) const
|
||||
{
|
||||
if( m_eventInfo )
|
||||
{
|
||||
if( !BitTest( m_eventInfo->m_type, ST_WORLD ) )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
if( m_ownerType != OT_INVALID )
|
||||
{
|
||||
if( m_drawableID != INVALID_DRAWABLE_ID || m_objectID != INVALID_ID || m_ownerType == OT_Positional )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool AudioEventRTS::isCurrentlyPlaying( void ) const
|
||||
{
|
||||
return TheAudio->isCurrentlyPlaying(m_playingHandle);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AudioPriority AudioEventRTS::getAudioPriority( void ) const
|
||||
{
|
||||
return m_priority;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setAudioPriority( AudioPriority newPriority )
|
||||
{
|
||||
m_priority = newPriority;
|
||||
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Real AudioEventRTS::getVolume( void ) const
|
||||
{
|
||||
if (m_volume == -1.0f) {
|
||||
if (m_eventInfo) {
|
||||
return m_eventInfo->m_volume;
|
||||
}
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
return m_volume;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setVolume( Real vol )
|
||||
{
|
||||
m_volume = vol;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
const Coord3D *AudioEventRTS::getCurrentPosition( void )
|
||||
{
|
||||
if (m_ownerType == OT_Positional)
|
||||
{
|
||||
return &m_positionOfAudio;
|
||||
}
|
||||
else if (m_ownerType == OT_Object)
|
||||
{
|
||||
Object *obj = TheGameLogic->findObjectByID(m_objectID);
|
||||
if (obj)
|
||||
{
|
||||
m_positionOfAudio.set( obj->getPosition() );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ownerType = OT_Dead;
|
||||
}
|
||||
return &m_positionOfAudio;
|
||||
}
|
||||
else if (m_ownerType == OT_Drawable)
|
||||
{
|
||||
Drawable *draw = TheGameClient->findDrawableByID(m_drawableID);
|
||||
if( draw )
|
||||
{
|
||||
m_positionOfAudio.set( draw->getPosition() );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ownerType = OT_Dead;
|
||||
}
|
||||
return &m_positionOfAudio;
|
||||
}
|
||||
else if( m_ownerType == OT_Dead )
|
||||
{
|
||||
return &m_positionOfAudio;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AsciiString AudioEventRTS::generateFilenamePrefix( AudioType audioTypeToPlay, Bool localized )
|
||||
{
|
||||
AsciiString retStr;
|
||||
retStr = TheAudio->getAudioSettings()->m_audioRoot;
|
||||
retStr.concat("\\");
|
||||
if (audioTypeToPlay == AT_Music) {
|
||||
retStr.concat(TheAudio->getAudioSettings()->m_musicFolder);
|
||||
} else if (audioTypeToPlay == AT_Streaming) {
|
||||
retStr.concat(TheAudio->getAudioSettings()->m_streamingFolder);
|
||||
} else {
|
||||
retStr.concat(TheAudio->getAudioSettings()->m_soundsFolder);
|
||||
}
|
||||
retStr.concat("\\");
|
||||
|
||||
if (localized) {
|
||||
retStr.concat(GetRegistryLanguage());
|
||||
retStr.concat("\\");
|
||||
}
|
||||
|
||||
return retStr;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AsciiString AudioEventRTS::generateFilenameExtension( AudioType audioTypeToPlay )
|
||||
{
|
||||
AsciiString retStr = AsciiString::TheEmptyString;
|
||||
if (audioTypeToPlay != AT_Music) {
|
||||
retStr = ".";
|
||||
retStr.concat(TheAudio->getAudioSettings()->m_soundsExtension);
|
||||
}
|
||||
|
||||
return retStr;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::adjustForLocalization(AsciiString &strToAdjust)
|
||||
{
|
||||
if (TheFileSystem->doesFileExist(strToAdjust.str()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const char *str = strToAdjust.reverseFind('\\');
|
||||
if (!str) {
|
||||
return;
|
||||
}
|
||||
|
||||
AsciiString filename = str;
|
||||
|
||||
strToAdjust = generateFilenamePrefix(m_eventInfo->m_soundType, TRUE);
|
||||
strToAdjust.concat(filename);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Int AudioEventRTS::getPlayerIndex( void ) const
|
||||
{
|
||||
if (m_ownerType == OT_Object) {
|
||||
Object *obj = TheGameLogic->findObjectByID(m_objectID);
|
||||
if (obj) {
|
||||
return obj->getControllingPlayer()->getPlayerIndex();
|
||||
}
|
||||
} else if (m_ownerType == OT_Drawable) {
|
||||
Drawable *draw = TheGameClient->findDrawableByID(m_drawableID);
|
||||
if (draw) {
|
||||
Object *obj = draw->getObject();
|
||||
if (obj) {
|
||||
return obj->getControllingPlayer()->getPlayerIndex();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m_playerIndex;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setPlayerIndex( Int playerNdx )
|
||||
{
|
||||
m_playerIndex = playerNdx;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/AudioRequest.h"
|
||||
|
||||
|
||||
AudioRequest::~AudioRequest()
|
||||
{
|
||||
|
||||
}
|
1118
Generals/Code/GameEngine/Source/Common/Audio/GameAudio.cpp
Normal file
1118
Generals/Code/GameEngine/Source/Common/Audio/GameAudio.cpp
Normal file
File diff suppressed because it is too large
Load diff
125
Generals/Code/GameEngine/Source/Common/Audio/GameMusic.cpp
Normal file
125
Generals/Code/GameEngine/Source/Common/Audio/GameMusic.cpp
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Westwood Studios Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright(C) 2001 - All Rights Reserved
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Project: RTS3
|
||||
//
|
||||
// File name: GameMusic.cpp
|
||||
//
|
||||
// Created: 5/01/01
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Includes
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/GameMusic.h"
|
||||
|
||||
#include "Common/AudioEventRTS.h"
|
||||
#include "Common/AudioRequest.h"
|
||||
#include "Common/GameAudio.h"
|
||||
#include "Common/INI.h"
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Externals
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Defines
|
||||
//----------------------------------------------------------------------------
|
||||
#define MUSIC_PATH "Data\\Audio\\Tracks" // directory path to the music files
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Types
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** The INI data fields for music tracks */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
const FieldParse MusicTrack::m_musicTrackFieldParseTable[] =
|
||||
{
|
||||
|
||||
{ "Filename", INI::parseAsciiString, NULL, offsetof( MusicTrack, filename ) },
|
||||
{ "Volume", INI::parsePercentToReal, NULL, offsetof( MusicTrack, volume ) },
|
||||
{ "Ambient", INI::parseBool, NULL, offsetof( MusicTrack, ambient ) },
|
||||
{ NULL, NULL, NULL, 0 },
|
||||
};
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
MusicManager::MusicManager()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
MusicManager::~MusicManager()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void MusicManager::playTrack( AudioEventRTS *eventToUse )
|
||||
{
|
||||
AudioRequest *audioRequest = TheAudio->allocateAudioRequest( true );
|
||||
audioRequest->m_pendingEvent = eventToUse;
|
||||
audioRequest->m_request = AR_Play;
|
||||
TheAudio->appendAudioRequest( audioRequest );
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void MusicManager::stopTrack( AudioHandle eventToRemove )
|
||||
{
|
||||
AudioRequest *audioRequest = TheAudio->allocateAudioRequest( false );
|
||||
audioRequest->m_handleToInteractOn = eventToRemove;
|
||||
audioRequest->m_request = AR_Stop;
|
||||
TheAudio->appendAudioRequest( audioRequest );
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void MusicManager::addAudioEvent( AudioEventRTS *eventToAdd )
|
||||
{
|
||||
playTrack( eventToAdd );
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void MusicManager::removeAudioEvent( AudioHandle eventToRemove )
|
||||
{
|
||||
stopTrack( eventToRemove );
|
||||
}
|
||||
|
333
Generals/Code/GameEngine/Source/Common/Audio/GameSounds.cpp
Normal file
333
Generals/Code/GameEngine/Source/Common/Audio/GameSounds.cpp
Normal file
|
@ -0,0 +1,333 @@
|
|||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Westwood Studios Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright(C) 2001 - All Rights Reserved
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Project: RTS3
|
||||
//
|
||||
// File name: GameSounds.cpp
|
||||
//
|
||||
// Created: 5/02/01
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Includes
|
||||
//----------------------------------------------------------------------------
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Lib/Basetype.h"
|
||||
#include "Common/GameSounds.h"
|
||||
|
||||
#include "Common/AudioEventInfo.h"
|
||||
#include "Common/AudioEventRTS.h"
|
||||
#include "Common/AudioRequest.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/PlayerList.h"
|
||||
|
||||
#include "GameLogic/PartitionManager.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef _INTERNAL
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SoundManager::SoundManager()
|
||||
{
|
||||
// nada to do
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SoundManager::~SoundManager()
|
||||
{
|
||||
// nada to do
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::init( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::postProcessLoad()
|
||||
{
|
||||
// The AudioManager should actually be live now, so go ahead and get the info we need from it
|
||||
// here
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::update( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::reset( void )
|
||||
{
|
||||
m_numPlaying2DSamples = 0;
|
||||
m_numPlaying3DSamples = 0;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::loseFocus( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::regainFocus( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::setListenerPosition( const Coord3D *position )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::setViewRadius( Real viewRadius )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::setCameraAudibleDistance( Real audibleDistance )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Real SoundManager::getCameraAudibleDistance( void )
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::addAudioEvent(AudioEventRTS *eventToAdd)
|
||||
{
|
||||
if (m_num2DSamples == 0 && m_num3DSamples == 0) {
|
||||
m_num2DSamples = TheAudio->getNum2DSamples();
|
||||
m_num3DSamples = TheAudio->getNum3DSamples();
|
||||
}
|
||||
|
||||
if (canPlayNow(eventToAdd)) {
|
||||
#ifdef INTENSIVE_AUDIO_DEBUG
|
||||
DEBUG_LOG((" - appended to request list with handle '%d'.\n", (UnsignedInt) eventToAdd->getPlayingHandle()));
|
||||
#endif
|
||||
AudioRequest *audioRequest = TheAudio->allocateAudioRequest( true );
|
||||
audioRequest->m_pendingEvent = eventToAdd;
|
||||
audioRequest->m_request = AR_Play;
|
||||
TheAudio->appendAudioRequest(audioRequest);
|
||||
} else {
|
||||
TheAudio->releaseAudioEventRTS(eventToAdd);
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::notifyOf2DSampleStart( void )
|
||||
{
|
||||
++m_numPlaying2DSamples;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::notifyOf3DSampleStart( void )
|
||||
{
|
||||
++m_numPlaying3DSamples;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::notifyOf2DSampleCompletion( void )
|
||||
{
|
||||
if (m_numPlaying2DSamples > 0) {
|
||||
--m_numPlaying2DSamples;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::notifyOf3DSampleCompletion( void )
|
||||
{
|
||||
if (m_numPlaying3DSamples > 0) {
|
||||
--m_numPlaying3DSamples;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Int SoundManager::getAvailableSamples( void )
|
||||
{
|
||||
return (m_num2DSamples - m_numPlaying2DSamples);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Int SoundManager::getAvailable3DSamples( void )
|
||||
{
|
||||
return (m_num3DSamples - m_numPlaying3DSamples);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AsciiString SoundManager::getFilenameForPlayFromAudioEvent( const AudioEventRTS *eventToGetFrom )
|
||||
{
|
||||
return AsciiString::TheEmptyString;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool SoundManager::canPlayNow( AudioEventRTS *event )
|
||||
{
|
||||
Bool retVal = false;
|
||||
// 1) Are we muted because we're beyond our maximum distance?
|
||||
// 2) Are we shrouded and this is a shroud sound?
|
||||
// 3) Are we violating our voice count or are we playing above the limit? (If so, stop now)
|
||||
// 4) is there an avaiable channel open?
|
||||
// 5) if not, then determine if there is anything of lower priority that we can kill
|
||||
// 6) if not, are we an interrupt-sound type?
|
||||
// if so, are there any sounds of our type playing right now that we can interrupt?
|
||||
// potentially here: Are there any sounds that are playing that are now beyond their distance?
|
||||
// if so, kill them and start our sound
|
||||
// if not, we're done. Can't play dude.
|
||||
|
||||
if( event->isPositionalAudio() && !BitTest( event->getAudioEventInfo()->m_type, ST_GLOBAL) && event->getAudioEventInfo()->m_priority != AP_CRITICAL )
|
||||
{
|
||||
Coord3D distance = *TheAudio->getListenerPosition();
|
||||
const Coord3D *pos = event->getCurrentPosition();
|
||||
if (pos)
|
||||
{
|
||||
distance.sub(pos);
|
||||
if (distance.length() >= event->getAudioEventInfo()->m_maxDistance)
|
||||
{
|
||||
#ifdef INTENSIVE_AUDIO_DEBUG
|
||||
DEBUG_LOG(("- culled due to distance (%.2f).\n", distance.length()));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
Int localPlayerNdx = ThePlayerList->getLocalPlayer()->getPlayerIndex();
|
||||
if( (event->getAudioEventInfo()->m_type & ST_SHROUDED) &&
|
||||
ThePartitionManager->getShroudStatusForPlayer(localPlayerNdx, pos) != CELLSHROUD_CLEAR )
|
||||
{
|
||||
#ifdef INTENSIVE_AUDIO_DEBUG
|
||||
DEBUG_LOG(("- culled due to shroud.\n"));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (violatesVoice(event))
|
||||
{
|
||||
retVal = isInterrupting(event);
|
||||
if (retVal)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef INTENSIVE_AUDIO_DEBUG
|
||||
DEBUG_LOG(("- culled due to voice.\n"));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if( TheAudio->doesViolateLimit( event ) )
|
||||
{
|
||||
#ifdef INTENSIVE_AUDIO_DEBUG
|
||||
DEBUG_LOG(("- culled due to limit.\n" ));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
else if( isInterrupting( event ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event->isPositionalAudio())
|
||||
{
|
||||
if (m_numPlaying3DSamples < m_num3DSamples)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#ifdef INTENSIVE_AUDIO_DEBUG
|
||||
DEBUG_LOG(("- %d samples playing, %d samples available", m_numPlaying3DSamples, m_num3DSamples));
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// its a UI sound (and thus, 2-D)
|
||||
if (m_numPlaying2DSamples < m_num2DSamples)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (TheAudio->isPlayingLowerPriority(event))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isInterrupting(event))
|
||||
{
|
||||
retVal = TheAudio->isPlayingAlready(event);
|
||||
if (retVal)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef INTENSIVE_AUDIO_DEBUG
|
||||
DEBUG_LOG(("- culled due to no channels available and non-interrupting.\n" ));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#ifdef INTENSIVE_AUDIO_DEBUG
|
||||
DEBUG_LOG(("culled due to unavailable channels"));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool SoundManager::violatesVoice( AudioEventRTS *event )
|
||||
{
|
||||
if (event->getAudioEventInfo()->m_type & ST_VOICE) {
|
||||
return (event->getObjectID() && TheAudio->isObjectPlayingVoice(event->getObjectID()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool SoundManager::isInterrupting( AudioEventRTS *event )
|
||||
{
|
||||
return event->getAudioEventInfo()->m_control & AC_INTERRUPT;
|
||||
}
|
1683
Generals/Code/GameEngine/Source/Common/Audio/GameSpeech.cpp
Normal file
1683
Generals/Code/GameEngine/Source/Common/Audio/GameSpeech.cpp
Normal file
File diff suppressed because it is too large
Load diff
708
Generals/Code/GameEngine/Source/Common/Audio/simpleplayer.cpp
Normal file
708
Generals/Code/GameEngine/Source/Common/Audio/simpleplayer.cpp
Normal file
|
@ -0,0 +1,708 @@
|
|||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "Common/SimplePlayer.h"
|
||||
#include "Common/URLLaunch.h"
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
CSimplePlayer::CSimplePlayer( HRESULT* phr )
|
||||
{
|
||||
m_cRef = 1;
|
||||
m_cBuffersOutstanding = 0;
|
||||
|
||||
m_pReader = NULL;
|
||||
|
||||
m_pHeader = NULL;
|
||||
m_hwo = NULL;
|
||||
|
||||
m_fEof = FALSE;
|
||||
m_pszUrl = NULL;
|
||||
|
||||
*phr = S_OK;
|
||||
|
||||
m_hOpenEvent = CreateEvent( NULL, FALSE, FALSE, SIMPLE_PLAYER_OPEN_EVENT );
|
||||
if ( NULL == m_hOpenEvent )
|
||||
{
|
||||
*phr = E_OUTOFMEMORY;
|
||||
}
|
||||
m_hCloseEvent = CreateEvent( NULL, FALSE, FALSE, SIMPLE_PLAYER_CLOSE_EVENT );
|
||||
if ( NULL == m_hCloseEvent )
|
||||
{
|
||||
*phr = E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
m_hrOpen = S_OK;
|
||||
|
||||
m_hCompletionEvent = NULL;
|
||||
|
||||
InitializeCriticalSection( &m_CriSec );
|
||||
m_whdrHead = NULL;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
CSimplePlayer::~CSimplePlayer()
|
||||
{
|
||||
DEBUG_ASSERTCRASH( 0 == m_cBuffersOutstanding,("CSimplePlayer destructor m_cBuffersOutstanding != 0") );
|
||||
|
||||
Close();
|
||||
|
||||
//
|
||||
// final remove of everything in the wave header list
|
||||
//
|
||||
RemoveWaveHeaders();
|
||||
DeleteCriticalSection( &m_CriSec );
|
||||
|
||||
if( m_pHeader != NULL )
|
||||
{
|
||||
m_pHeader->Release();
|
||||
m_pHeader = NULL;
|
||||
}
|
||||
|
||||
if( m_pReader != NULL )
|
||||
{
|
||||
m_pReader->Release();
|
||||
m_pReader = NULL;
|
||||
}
|
||||
|
||||
if( m_hwo != NULL )
|
||||
{
|
||||
waveOutClose( m_hwo );
|
||||
}
|
||||
|
||||
delete [] m_pszUrl;
|
||||
|
||||
if ( m_hOpenEvent )
|
||||
{
|
||||
CloseHandle( m_hOpenEvent );
|
||||
}
|
||||
if ( m_hCloseEvent )
|
||||
{
|
||||
CloseHandle( m_hCloseEvent );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
HRESULT STDMETHODCALLTYPE CSimplePlayer::QueryInterface(
|
||||
REFIID riid,
|
||||
void **ppvObject )
|
||||
{
|
||||
return( E_NOINTERFACE );
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
ULONG STDMETHODCALLTYPE CSimplePlayer::AddRef()
|
||||
{
|
||||
return( InterlockedIncrement( &m_cRef ) );
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
ULONG STDMETHODCALLTYPE CSimplePlayer::Release()
|
||||
{
|
||||
ULONG uRet = InterlockedDecrement( &m_cRef );
|
||||
|
||||
if( 0 == uRet )
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
return( uRet );
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
HRESULT STDMETHODCALLTYPE CSimplePlayer::OnSample(
|
||||
/* [in] */ DWORD dwOutputNum,
|
||||
/* [in] */ QWORD cnsSampleTime,
|
||||
/* [in] */ QWORD cnsSampleDuration,
|
||||
/* [in] */ DWORD dwFlags,
|
||||
/* [in] */ INSSBuffer __RPC_FAR *pSample,
|
||||
/* [in] */ VOID *pvContext )
|
||||
{
|
||||
if( 0 != dwOutputNum )
|
||||
{
|
||||
return( S_OK );
|
||||
}
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
BYTE *pData;
|
||||
DWORD cbData;
|
||||
|
||||
//
|
||||
// first UnprepareHeader and remove everthing in the ready list
|
||||
//
|
||||
RemoveWaveHeaders( );
|
||||
|
||||
hr = pSample->GetBufferAndLength( &pData, &cbData );
|
||||
if ( FAILED( hr ) )
|
||||
{
|
||||
return( E_UNEXPECTED );
|
||||
}
|
||||
|
||||
DEBUG_LOG(( " New Sample of length %d and PS time %d ms\n",
|
||||
cbData, ( DWORD ) ( cnsSampleTime / 10000 ) ));
|
||||
|
||||
LPWAVEHDR pwh = (LPWAVEHDR) new BYTE[ sizeof( WAVEHDR ) + cbData ];
|
||||
|
||||
if( NULL == pwh )
|
||||
{
|
||||
DEBUG_LOG(( "OnSample OUT OF MEMORY! \n"));
|
||||
|
||||
*m_phrCompletion = E_OUTOFMEMORY;
|
||||
SetEvent( m_hCompletionEvent );
|
||||
return( E_UNEXPECTED );
|
||||
}
|
||||
|
||||
pwh->lpData = (LPSTR)&pwh[1];
|
||||
pwh->dwBufferLength = cbData;
|
||||
pwh->dwBytesRecorded = cbData;
|
||||
pwh->dwUser = 0;
|
||||
pwh->dwLoops = 0;
|
||||
pwh->dwFlags = 0;
|
||||
|
||||
CopyMemory( pwh->lpData, pData, cbData );
|
||||
|
||||
MMRESULT mmr;
|
||||
|
||||
mmr = waveOutPrepareHeader( m_hwo, pwh, sizeof(WAVEHDR) );
|
||||
mmr = MMSYSERR_NOERROR;
|
||||
|
||||
if( mmr != MMSYSERR_NOERROR )
|
||||
{
|
||||
DEBUG_LOG(( "failed to prepare wave buffer, error=%lu\n" , mmr ));
|
||||
*m_phrCompletion = E_UNEXPECTED;
|
||||
SetEvent( m_hCompletionEvent );
|
||||
return( E_UNEXPECTED );
|
||||
}
|
||||
|
||||
mmr = waveOutWrite( m_hwo, pwh, sizeof(WAVEHDR) );
|
||||
mmr = MMSYSERR_NOERROR;
|
||||
|
||||
if( mmr != MMSYSERR_NOERROR )
|
||||
{
|
||||
delete pwh;
|
||||
|
||||
DEBUG_LOG(( "failed to write wave sample, error=%lu\n" , mmr ));
|
||||
*m_phrCompletion = E_UNEXPECTED;
|
||||
SetEvent( m_hCompletionEvent );
|
||||
return( E_UNEXPECTED );
|
||||
}
|
||||
|
||||
InterlockedIncrement( &m_cBuffersOutstanding );
|
||||
|
||||
return( S_OK );
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
HRESULT CSimplePlayer::Play( LPCWSTR pszUrl, DWORD dwSecDuration, HANDLE hCompletionEvent, HRESULT *phrCompletion )
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
//
|
||||
// If the URL is not a UNC path, a full path, or an Internet-style URL then assume it is
|
||||
// a relative local file name that needs to be expanded to a full path.
|
||||
//
|
||||
WCHAR wszFullUrl[ MAX_PATH ];
|
||||
|
||||
if( ( 0 == wcsstr( pszUrl, L"\\\\" ) )
|
||||
&& ( 0 == wcsstr( pszUrl, L":\\" ) )
|
||||
&& ( 0 == wcsstr( pszUrl, L"://" ) ) )
|
||||
{
|
||||
//
|
||||
// Expand to a full path name
|
||||
//
|
||||
LPWSTR pszCheck = _wfullpath( wszFullUrl, pszUrl, MAX_PATH );
|
||||
|
||||
if( NULL == pszCheck )
|
||||
{
|
||||
DEBUG_LOG(( "internal error %lu\n" , GetLastError() ));
|
||||
return E_UNEXPECTED ;
|
||||
}
|
||||
|
||||
pszUrl = wszFullUrl;
|
||||
}
|
||||
|
||||
//
|
||||
// Save a copy of the URL
|
||||
//
|
||||
delete[] m_pszUrl;
|
||||
|
||||
m_pszUrl = new WCHAR[ wcslen( pszUrl ) + 1 ];
|
||||
|
||||
if( NULL == m_pszUrl )
|
||||
{
|
||||
DEBUG_LOG(( "insufficient Memory\n" )) ;
|
||||
return( E_OUTOFMEMORY );
|
||||
}
|
||||
|
||||
wcscpy( m_pszUrl, pszUrl );
|
||||
|
||||
//
|
||||
// Attempt to open the URL
|
||||
//
|
||||
m_hCompletionEvent = hCompletionEvent;
|
||||
|
||||
m_phrCompletion = phrCompletion;
|
||||
|
||||
#ifdef SUPPORT_DRM
|
||||
|
||||
hr = WMCreateReader( NULL, WMT_RIGHT_PLAYBACK, &m_pReader );
|
||||
|
||||
#else
|
||||
|
||||
hr = WMCreateReader( NULL, 0, &m_pReader );
|
||||
|
||||
#endif
|
||||
|
||||
if( FAILED( hr ) )
|
||||
{
|
||||
DEBUG_LOG(( "failed to create audio reader (hr=0x%08x)\n" , hr ));
|
||||
return( hr );
|
||||
}
|
||||
|
||||
//
|
||||
// Open the file
|
||||
//
|
||||
hr = m_pReader->Open( m_pszUrl, this, NULL );
|
||||
if ( SUCCEEDED( hr ) )
|
||||
{
|
||||
WaitForSingleObject( m_hOpenEvent, INFINITE );
|
||||
hr = m_hrOpen;
|
||||
}
|
||||
if ( NS_E_NO_STREAM == hr )
|
||||
{
|
||||
DEBUG_LOG(( "Waiting for transmission to begin...\n" ));
|
||||
WaitForSingleObject( m_hOpenEvent, INFINITE );
|
||||
hr = m_hrOpen;
|
||||
}
|
||||
if ( FAILED( hr ) )
|
||||
{
|
||||
DEBUG_LOG(( "failed to open (hr=0x%08x)\n", hr ));
|
||||
return( hr );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// It worked! Display various attributes
|
||||
//
|
||||
hr = m_pReader->QueryInterface( IID_IWMHeaderInfo, ( VOID ** )&m_pHeader );
|
||||
if ( FAILED( hr ) )
|
||||
{
|
||||
DEBUG_LOG(( "failed to qi for header interface (hr=0x%08x)\n" , hr ));
|
||||
return( hr );
|
||||
}
|
||||
|
||||
WORD i, wAttrCnt;
|
||||
|
||||
hr = m_pHeader->GetAttributeCount( 0, &wAttrCnt );
|
||||
if ( FAILED( hr ) )
|
||||
{
|
||||
DEBUG_LOG(( "GetAttributeCount Failed (hr=0x%08x)\n" , hr ));
|
||||
return( hr );
|
||||
}
|
||||
|
||||
WCHAR *pwszName = NULL;
|
||||
BYTE *pValue = NULL;
|
||||
|
||||
for ( i = 0; i < wAttrCnt ; i++ )
|
||||
{
|
||||
WORD wStream = 0;
|
||||
WORD cchNamelen = 0;
|
||||
WMT_ATTR_DATATYPE type;
|
||||
WORD cbLength = 0;
|
||||
|
||||
hr = m_pHeader->GetAttributeByIndex( i, &wStream, NULL, &cchNamelen, &type, NULL, &cbLength );
|
||||
if ( FAILED( hr ) )
|
||||
{
|
||||
DEBUG_LOG(( "GetAttributeByIndex Failed (hr=0x%08x)\n" , hr ));
|
||||
break;
|
||||
}
|
||||
|
||||
pwszName = new WCHAR[ cchNamelen ];
|
||||
pValue = new BYTE[ cbLength ];
|
||||
|
||||
if( NULL == pwszName || NULL == pValue )
|
||||
{
|
||||
hr = E_OUTOFMEMORY;
|
||||
break;
|
||||
}
|
||||
|
||||
hr = m_pHeader->GetAttributeByIndex( i, &wStream, pwszName, &cchNamelen, &type, pValue, &cbLength );
|
||||
if ( FAILED( hr ) )
|
||||
{
|
||||
DEBUG_LOG(( "GetAttributeByIndex Failed (hr=0x%08x)\n" , hr ));
|
||||
break;
|
||||
}
|
||||
|
||||
switch ( type )
|
||||
{
|
||||
case WMT_TYPE_DWORD:
|
||||
DEBUG_LOG(("%ws: %u\n" , pwszName, *((DWORD *) pValue) ));
|
||||
break;
|
||||
case WMT_TYPE_STRING:
|
||||
DEBUG_LOG(("%ws: %ws\n" , pwszName, (WCHAR *) pValue ));
|
||||
break;
|
||||
case WMT_TYPE_BINARY:
|
||||
DEBUG_LOG(("%ws: Type = Binary of Length %u\n" , pwszName, cbLength ));
|
||||
break;
|
||||
case WMT_TYPE_BOOL:
|
||||
DEBUG_LOG(("%ws: %s\n" , pwszName, ( * ( ( BOOL * ) pValue) ? _T( "true" ) : _T( "false" ) ) ));
|
||||
break;
|
||||
case WMT_TYPE_WORD:
|
||||
DEBUG_LOG(("%ws: %hu\n" , pwszName, *((WORD *) pValue) ));
|
||||
break;
|
||||
case WMT_TYPE_QWORD:
|
||||
DEBUG_LOG(("%ws: %I64u\n" , pwszName, *((QWORD *) pValue) ));
|
||||
break;
|
||||
case WMT_TYPE_GUID:
|
||||
DEBUG_LOG(("%ws: %I64x%I64x\n" , pwszName, *((QWORD *) pValue), *((QWORD *) pValue + 1) ));
|
||||
break;
|
||||
default:
|
||||
DEBUG_LOG(("%ws: Type = %d, Length %u\n" , pwszName, type, cbLength ));
|
||||
break;
|
||||
}
|
||||
|
||||
delete pwszName;
|
||||
pwszName = NULL;
|
||||
|
||||
delete pValue;
|
||||
pValue = NULL;
|
||||
}
|
||||
|
||||
delete pwszName;
|
||||
pwszName = NULL;
|
||||
|
||||
delete pValue;
|
||||
pValue = NULL;
|
||||
|
||||
if ( FAILED( hr ) )
|
||||
{
|
||||
return( hr );
|
||||
}
|
||||
|
||||
//
|
||||
// Make sure we're audio only
|
||||
//
|
||||
DWORD cOutputs;
|
||||
|
||||
hr = m_pReader->GetOutputCount( &cOutputs );
|
||||
if ( FAILED( hr ) )
|
||||
{
|
||||
DEBUG_LOG(( "failed GetOutputCount(), (hr=0x%08x)\n" , hr ));
|
||||
return( hr );
|
||||
}
|
||||
|
||||
if ( cOutputs != 1 )
|
||||
{
|
||||
DEBUG_LOG(( "Not audio only (cOutputs = %d).\n" , cOutputs ));
|
||||
// return( E_UNEXPECTED );
|
||||
}
|
||||
|
||||
IWMOutputMediaProps *pProps;
|
||||
hr = m_pReader->GetOutputProps( 0, &pProps );
|
||||
if ( FAILED( hr ) )
|
||||
{
|
||||
DEBUG_LOG(( "failed GetOutputProps(), (hr=0x%08x)\n" , hr ));
|
||||
return( hr );
|
||||
}
|
||||
|
||||
DWORD cbBuffer = 0;
|
||||
|
||||
hr = pProps->GetMediaType( NULL, &cbBuffer );
|
||||
if ( FAILED( hr ) )
|
||||
{
|
||||
pProps->Release( );
|
||||
DEBUG_LOG(( "GetMediaType failed (hr=0x%08x)\n" , hr ));
|
||||
return( hr );
|
||||
}
|
||||
|
||||
WM_MEDIA_TYPE *pMediaType = ( WM_MEDIA_TYPE * ) new BYTE[cbBuffer] ;
|
||||
|
||||
hr = pProps->GetMediaType( pMediaType, &cbBuffer );
|
||||
if ( FAILED( hr ) )
|
||||
{
|
||||
pProps->Release( );
|
||||
DEBUG_LOG(( "GetMediaType failed (hr=0x%08x)\n" , hr ));
|
||||
return( hr );
|
||||
}
|
||||
|
||||
pProps->Release( );
|
||||
|
||||
if ( pMediaType->majortype != WMMEDIATYPE_Audio )
|
||||
{
|
||||
delete[] (BYTE *) pMediaType ;
|
||||
DEBUG_LOG(( "Not audio only (major type mismatch).\n" ));
|
||||
return( E_UNEXPECTED );
|
||||
}
|
||||
|
||||
//
|
||||
// Set up for audio playback
|
||||
//
|
||||
WAVEFORMATEX *pwfx = ( WAVEFORMATEX * )pMediaType->pbFormat;
|
||||
memcpy( &m_wfx, pwfx, sizeof( WAVEFORMATEX ) + pwfx->cbSize );
|
||||
|
||||
delete[] (BYTE *)pMediaType ;
|
||||
pMediaType = NULL ;
|
||||
|
||||
MMRESULT mmr;
|
||||
|
||||
mmr = waveOutOpen( &m_hwo,
|
||||
WAVE_MAPPER,
|
||||
&m_wfx,
|
||||
(DWORD)WaveProc,
|
||||
(DWORD)this,
|
||||
CALLBACK_FUNCTION );
|
||||
mmr = MMSYSERR_NOERROR;
|
||||
|
||||
if( mmr != MMSYSERR_NOERROR )
|
||||
{
|
||||
|
||||
DEBUG_LOG(( "failed to open wav output device, error=%lu\n" , mmr ));
|
||||
return( E_UNEXPECTED );
|
||||
}
|
||||
|
||||
//
|
||||
// Start reading the data (and rendering the audio)
|
||||
//
|
||||
QWORD cnsDuration = ( QWORD ) dwSecDuration * 10000000;
|
||||
hr = m_pReader->Start( 0, cnsDuration, 1.0, NULL );
|
||||
|
||||
if( FAILED( hr ) )
|
||||
{
|
||||
DEBUG_LOG(( "failed Start(), (hr=0x%08x)\n" , hr ));
|
||||
return( hr );
|
||||
}
|
||||
|
||||
return( hr );
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
HRESULT STDMETHODCALLTYPE CSimplePlayer::OnStatus(
|
||||
/* [in] */ WMT_STATUS Status,
|
||||
/* [in] */ HRESULT hr,
|
||||
/* [in] */ WMT_ATTR_DATATYPE dwType,
|
||||
/* [in] */ BYTE __RPC_FAR *pValue,
|
||||
/* [in] */ void __RPC_FAR *pvContext)
|
||||
{
|
||||
switch( Status )
|
||||
{
|
||||
case WMT_OPENED:
|
||||
DEBUG_LOG(( "OnStatus( WMT_OPENED )\n" ));
|
||||
m_hrOpen = hr;
|
||||
SetEvent( m_hOpenEvent );
|
||||
break;
|
||||
|
||||
case WMT_SOURCE_SWITCH:
|
||||
DEBUG_LOG(( "OnStatus( WMT_SOURCE_SWITCH )\n" ));
|
||||
m_hrOpen = hr;
|
||||
SetEvent( m_hOpenEvent );
|
||||
break;
|
||||
|
||||
case WMT_ERROR:
|
||||
DEBUG_LOG(( "OnStatus( WMT_ERROR )\n" ));
|
||||
break;
|
||||
|
||||
case WMT_STARTED:
|
||||
DEBUG_LOG(( "OnStatus( WMT_STARTED )\n" ));
|
||||
break;
|
||||
|
||||
case WMT_STOPPED:
|
||||
DEBUG_LOG(( "OnStatus( WMT_STOPPED )\n" ));
|
||||
break;
|
||||
|
||||
case WMT_BUFFERING_START:
|
||||
DEBUG_LOG(( "OnStatus( WMT_BUFFERING START)\n" ));
|
||||
break;
|
||||
|
||||
case WMT_BUFFERING_STOP:
|
||||
DEBUG_LOG(( "OnStatus( WMT_BUFFERING STOP)\n" ));
|
||||
break;
|
||||
|
||||
case WMT_EOF:
|
||||
DEBUG_LOG(( "OnStatus( WMT_EOF )\n" ));
|
||||
|
||||
//
|
||||
// cleanup and exit
|
||||
//
|
||||
|
||||
m_fEof = TRUE;
|
||||
|
||||
if( 0 == m_cBuffersOutstanding )
|
||||
{
|
||||
SetEvent( m_hCompletionEvent );
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case WMT_END_OF_SEGMENT:
|
||||
DEBUG_LOG(( "OnStatus( WMT_END_OF_SEGMENT )\n" ));
|
||||
|
||||
//
|
||||
// cleanup and exit
|
||||
//
|
||||
|
||||
m_fEof = TRUE;
|
||||
|
||||
if( 0 == m_cBuffersOutstanding )
|
||||
{
|
||||
SetEvent( m_hCompletionEvent );
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case WMT_LOCATING:
|
||||
DEBUG_LOG(( "OnStatus( WMT_LOCATING )\n" ));
|
||||
break;
|
||||
|
||||
case WMT_CONNECTING:
|
||||
DEBUG_LOG(( "OnStatus( WMT_CONNECTING )\n" ));
|
||||
break;
|
||||
|
||||
case WMT_NO_RIGHTS:
|
||||
{
|
||||
LPWSTR pwszEscapedURL = NULL;
|
||||
|
||||
hr = MakeEscapedURL( m_pszUrl, &pwszEscapedURL );
|
||||
|
||||
if( SUCCEEDED( hr ) )
|
||||
{
|
||||
WCHAR wszURL[ 0x1000 ];
|
||||
|
||||
swprintf( wszURL, L"%s&filename=%s&embedded=false", pValue, pwszEscapedURL );
|
||||
|
||||
hr = LaunchURL( wszURL );
|
||||
|
||||
if( FAILED( hr ) )
|
||||
{
|
||||
DEBUG_LOG(( "Unable to launch web browser to retrieve playback license (hr=0x%08x)\n" , hr ));
|
||||
}
|
||||
|
||||
delete [] pwszEscapedURL;
|
||||
pwszEscapedURL = NULL ;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WMT_MISSING_CODEC:
|
||||
{
|
||||
DEBUG_LOG(( "Missing codec: (hr=0x%08x)\n" , hr ));
|
||||
break;
|
||||
}
|
||||
|
||||
case WMT_CLOSED:
|
||||
SetEvent( m_hCloseEvent );
|
||||
break;
|
||||
};
|
||||
|
||||
return( S_OK );
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
HRESULT CSimplePlayer::Close()
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
if( NULL != m_pReader )
|
||||
{
|
||||
hr = m_pReader->Close();
|
||||
|
||||
if( SUCCEEDED( hr ) )
|
||||
{
|
||||
WaitForSingleObject( m_hCloseEvent, INFINITE );
|
||||
}
|
||||
}
|
||||
|
||||
return( hr );
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void CSimplePlayer::OnWaveOutMsg( UINT uMsg, DWORD dwParam1, DWORD dwParam2 )
|
||||
{
|
||||
if( WOM_DONE == uMsg )
|
||||
{
|
||||
//
|
||||
// add the wave header to ready-to-free list for the caller
|
||||
// to pick up and free in the next OnSample call
|
||||
//
|
||||
AddWaveHeader( ( LPWAVEHDR )dwParam1 );
|
||||
|
||||
InterlockedDecrement( &m_cBuffersOutstanding );
|
||||
|
||||
if( m_fEof && ( 0 == m_cBuffersOutstanding ) )
|
||||
{
|
||||
SetEvent( m_hCompletionEvent );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void CALLBACK CSimplePlayer::WaveProc(
|
||||
HWAVEOUT hwo,
|
||||
UINT uMsg,
|
||||
DWORD dwInstance,
|
||||
DWORD dwParam1,
|
||||
DWORD dwParam2 )
|
||||
{
|
||||
CSimplePlayer *pThis = (CSimplePlayer*)dwInstance;
|
||||
|
||||
pThis->OnWaveOutMsg( uMsg, dwParam1, dwParam2 );
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
HRESULT CSimplePlayer::AddWaveHeader( LPWAVEHDR pwh )
|
||||
{
|
||||
WAVEHDR_LIST *tmp = new WAVEHDR_LIST;
|
||||
if( NULL == tmp )
|
||||
{
|
||||
return( E_OUTOFMEMORY );
|
||||
}
|
||||
tmp->pwh = pwh;
|
||||
|
||||
EnterCriticalSection( &m_CriSec );
|
||||
tmp->next = m_whdrHead;
|
||||
m_whdrHead = tmp;
|
||||
LeaveCriticalSection( &m_CriSec );
|
||||
return( S_OK );
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
void CSimplePlayer::RemoveWaveHeaders( )
|
||||
{
|
||||
WAVEHDR_LIST *tmp;
|
||||
|
||||
EnterCriticalSection( &m_CriSec );
|
||||
while( NULL != m_whdrHead )
|
||||
{
|
||||
tmp = m_whdrHead->next;
|
||||
DEBUG_ASSERTCRASH( m_whdrHead->pwh->dwFlags & WHDR_DONE, ("RemoveWaveHeaders!") );
|
||||
waveOutUnprepareHeader( m_hwo, m_whdrHead->pwh, sizeof( WAVEHDR ) );
|
||||
delete m_whdrHead->pwh;
|
||||
delete m_whdrHead;
|
||||
m_whdrHead = tmp;
|
||||
}
|
||||
LeaveCriticalSection( &m_CriSec );
|
||||
}
|
338
Generals/Code/GameEngine/Source/Common/Audio/urllaunch.cpp
Normal file
338
Generals/Code/GameEngine/Source/Common/Audio/urllaunch.cpp
Normal file
|
@ -0,0 +1,338 @@
|
|||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "Common/URLLaunch.h"
|
||||
|
||||
#define FILE_PREFIX L"file://"
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
HRESULT MakeEscapedURL( LPWSTR pszInURL, LPWSTR *ppszOutURL )
|
||||
{
|
||||
if( ( NULL == pszInURL ) || ( NULL == ppszOutURL ) )
|
||||
{
|
||||
return( E_INVALIDARG );
|
||||
}
|
||||
|
||||
//
|
||||
// Do we need to pre-pend file://?
|
||||
//
|
||||
BOOL fNeedFilePrefix = ( 0 == wcsstr( pszInURL, L"://" ) );
|
||||
|
||||
//
|
||||
// Count how many characters need to be escaped
|
||||
//
|
||||
LPWSTR pszTemp = pszInURL;
|
||||
DWORD cEscapees = 0;
|
||||
|
||||
while( TRUE )
|
||||
{
|
||||
LPWSTR pchToEscape = wcspbrk( pszTemp, L" #$%&\\+,;=@[]^{}" );
|
||||
|
||||
if( NULL == pchToEscape )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
cEscapees++;
|
||||
|
||||
pszTemp = pchToEscape + 1;
|
||||
}
|
||||
|
||||
//
|
||||
// Allocate sufficient outgoing buffer space
|
||||
//
|
||||
int cchNeeded = wcslen( pszInURL ) + ( 2 * cEscapees ) + 1;
|
||||
|
||||
if( fNeedFilePrefix )
|
||||
{
|
||||
cchNeeded += wcslen( FILE_PREFIX );
|
||||
}
|
||||
|
||||
*ppszOutURL = new WCHAR[ cchNeeded ];
|
||||
|
||||
if( NULL == *ppszOutURL )
|
||||
{
|
||||
return( E_OUTOFMEMORY );
|
||||
}
|
||||
|
||||
//
|
||||
// Fill in the outgoing escaped buffer
|
||||
//
|
||||
pszTemp = pszInURL;
|
||||
|
||||
LPWSTR pchNext = *ppszOutURL;
|
||||
|
||||
if( fNeedFilePrefix )
|
||||
{
|
||||
wcscpy( *ppszOutURL, FILE_PREFIX );
|
||||
pchNext += wcslen( FILE_PREFIX );
|
||||
}
|
||||
|
||||
while( TRUE )
|
||||
{
|
||||
LPWSTR pchToEscape = wcspbrk( pszTemp, L" #$%&\\+,;=@[]^{}" );
|
||||
|
||||
if( NULL == pchToEscape )
|
||||
{
|
||||
//
|
||||
// Copy the rest of the input string and get out
|
||||
//
|
||||
wcscpy( pchNext, pszTemp );
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
// Copy all characters since the previous escapee
|
||||
//
|
||||
int cchToCopy = pchToEscape - pszTemp;
|
||||
|
||||
if( cchToCopy > 0 )
|
||||
{
|
||||
wcsncpy( pchNext, pszTemp, cchToCopy );
|
||||
|
||||
pchNext += cchToCopy;
|
||||
}
|
||||
|
||||
//
|
||||
// Expand this character into an escape code and move on
|
||||
//
|
||||
pchNext += swprintf( pchNext, L"%%%02x", *pchToEscape );
|
||||
|
||||
pszTemp = pchToEscape + 1;
|
||||
}
|
||||
|
||||
return( S_OK );
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
HRESULT GetShellOpenCommand( LPTSTR ptszShellOpenCommand, DWORD cbShellOpenCommand )
|
||||
{
|
||||
LONG lResult;
|
||||
|
||||
HKEY hKey = NULL;
|
||||
HKEY hFileKey = NULL;
|
||||
|
||||
BOOL fFoundExtensionCommand = FALSE;
|
||||
|
||||
do
|
||||
{
|
||||
//
|
||||
// Look for the file type associated with .html files
|
||||
//
|
||||
TCHAR szFileType[ MAX_PATH ];
|
||||
|
||||
lResult = RegOpenKeyEx( HKEY_CLASSES_ROOT, _T( ".html" ), 0, KEY_READ, &hKey );
|
||||
|
||||
if( ERROR_SUCCESS != lResult )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
DWORD dwLength = sizeof( szFileType );
|
||||
|
||||
lResult = RegQueryValueEx( hKey, NULL, 0, NULL, (BYTE *)szFileType, &dwLength );
|
||||
|
||||
if( ERROR_SUCCESS != lResult )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
// Find the command for the shell's open verb associated with this file type
|
||||
//
|
||||
TCHAR szKeyName[ MAX_PATH + 20 ];
|
||||
|
||||
wsprintf( szKeyName, _T( "%s\\shell\\open\\command" ), szFileType );
|
||||
|
||||
lResult = RegOpenKeyEx( HKEY_CLASSES_ROOT, szKeyName, 0, KEY_READ, &hFileKey );
|
||||
|
||||
if( ERROR_SUCCESS != lResult )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
dwLength = cbShellOpenCommand;
|
||||
|
||||
lResult = RegQueryValueEx( hFileKey, NULL, 0, NULL, (BYTE *)ptszShellOpenCommand, &dwLength );
|
||||
|
||||
if( 0 == lResult )
|
||||
{
|
||||
fFoundExtensionCommand = TRUE;
|
||||
}
|
||||
}
|
||||
while( FALSE );
|
||||
|
||||
//
|
||||
// If there was no application associated with .html files by extension, look for
|
||||
// an application associated with the http protocol
|
||||
//
|
||||
if( !fFoundExtensionCommand )
|
||||
{
|
||||
if( NULL != hKey )
|
||||
{
|
||||
RegCloseKey( hKey );
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
//
|
||||
// Find the command for the shell's open verb associated with the http protocol
|
||||
//
|
||||
lResult = RegOpenKeyEx( HKEY_CLASSES_ROOT, _T( "http\\shell\\open\\command" ), 0, KEY_READ, &hKey );
|
||||
|
||||
if( ERROR_SUCCESS != lResult )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
DWORD dwLength = cbShellOpenCommand;
|
||||
|
||||
lResult = RegQueryValueEx( hKey, NULL, 0, NULL, (BYTE *)ptszShellOpenCommand, &dwLength );
|
||||
}
|
||||
while( FALSE );
|
||||
}
|
||||
|
||||
if( NULL != hKey )
|
||||
{
|
||||
RegCloseKey( hKey );
|
||||
}
|
||||
|
||||
if( NULL != hFileKey )
|
||||
{
|
||||
RegCloseKey( hFileKey );
|
||||
}
|
||||
|
||||
return( HRESULT_FROM_WIN32( lResult ) );
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
HRESULT LaunchURL( LPCWSTR pszURL )
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
//
|
||||
// Find the appropriate command to launch URLs with
|
||||
//
|
||||
TCHAR szShellOpenCommand[ MAX_PATH * 2 ];
|
||||
|
||||
hr = GetShellOpenCommand( szShellOpenCommand, sizeof( szShellOpenCommand ) );
|
||||
|
||||
if( FAILED( hr ) )
|
||||
{
|
||||
return( hr );
|
||||
}
|
||||
|
||||
//
|
||||
// Build the appropriate command line, substituting our URL parameter
|
||||
//
|
||||
TCHAR szLaunchCommand[ 2000 ];
|
||||
|
||||
LPTSTR pszParam = _tcsstr( szShellOpenCommand, _T( "\"%1\"" ) );
|
||||
|
||||
if( NULL == pszParam )
|
||||
{
|
||||
pszParam = _tcsstr( szShellOpenCommand, _T( "\"%*\"" ) );
|
||||
}
|
||||
|
||||
if( NULL != pszParam )
|
||||
{
|
||||
*pszParam = _T( '\0' ) ;
|
||||
|
||||
wsprintf( szLaunchCommand, _T( "%s%ws%s" ), szShellOpenCommand, pszURL, pszParam + 4 );
|
||||
}
|
||||
else
|
||||
{
|
||||
wsprintf( szLaunchCommand, _T( "%s %ws" ), szShellOpenCommand, pszURL );
|
||||
}
|
||||
|
||||
//
|
||||
// Find the application name, stripping quotes if necessary
|
||||
//
|
||||
TCHAR szExe[ MAX_PATH * 2 ];
|
||||
LPTSTR pchFirst = szShellOpenCommand;
|
||||
LPTSTR pchNext = NULL;
|
||||
|
||||
while( _T( ' ' ) == *pchFirst )
|
||||
{
|
||||
pchFirst++;
|
||||
}
|
||||
|
||||
if( _T( '"' ) == *pchFirst )
|
||||
{
|
||||
pchFirst++;
|
||||
|
||||
pchNext = _tcschr( pchFirst, _T( '"' ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
pchNext = _tcschr( pchFirst + 1, _T( ' ' ) );
|
||||
}
|
||||
|
||||
if( NULL == pchNext )
|
||||
{
|
||||
pchNext = szShellOpenCommand + _tcslen( szShellOpenCommand );
|
||||
}
|
||||
|
||||
_tcsncpy( szExe, pchFirst, pchNext - pchFirst );
|
||||
szExe[ pchNext - pchFirst ] = _T( '\0' ) ;
|
||||
|
||||
//
|
||||
// Because of the extremely long length of the URLs, neither
|
||||
// WinExec, nor ShellExecute, were working correctly. For this reason
|
||||
// we use CreateProcess. The CreateProcess documentation in MSDN says
|
||||
// that the most robust way to call CreateProcess is to pass the full
|
||||
// command line, where the first element is the application name, in the
|
||||
// lpCommandLine parameter. In our case this is necesssary to get Netscape
|
||||
// to function properly.
|
||||
//
|
||||
PROCESS_INFORMATION ProcInfo;
|
||||
ZeroMemory( (LPVOID)&ProcInfo, sizeof( PROCESS_INFORMATION ) );
|
||||
|
||||
STARTUPINFO StartUp;
|
||||
ZeroMemory( (LPVOID)&StartUp, sizeof( STARTUPINFO ) );
|
||||
|
||||
StartUp.cb = sizeof(STARTUPINFO);
|
||||
|
||||
if( !CreateProcess( szExe, szLaunchCommand, NULL, NULL,
|
||||
FALSE, 0, NULL, NULL, &StartUp, &ProcInfo) )
|
||||
{
|
||||
hr = HRESULT_FROM_WIN32( GetLastError() );
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// CreateProcess succeeded and we do not need the handles to the thread
|
||||
// or the process, so close them now.
|
||||
//
|
||||
if( NULL != ProcInfo.hThread )
|
||||
{
|
||||
CloseHandle( ProcInfo.hThread );
|
||||
}
|
||||
|
||||
if( NULL != ProcInfo.hProcess )
|
||||
{
|
||||
CloseHandle( ProcInfo.hProcess );
|
||||
}
|
||||
}
|
||||
|
||||
return( hr );
|
||||
}
|
Reference in a new issue