1560 lines
50 KiB
C++
1560 lines
50 KiB
C++
//
|
|
// audio/radiotrack.cpp
|
|
//
|
|
// Copyright (C) 1999-2007 Rockstar Games. All Rights Reserved.
|
|
//
|
|
|
|
#include "radiotrack.h"
|
|
|
|
#if NA_RADIO_ENABLED
|
|
|
|
#include "northaudioengine.h"
|
|
#include "radioaudioentity.h"
|
|
#include "radiostation.h"
|
|
|
|
#include "audio/scriptaudioentity.h"
|
|
#include "audioengine/engine.h"
|
|
#include "audioengine/soundfactory.h"
|
|
#include "audioengine/soundmanager.h"
|
|
#include "audiohardware/device.h"
|
|
#include "audiohardware/driver.h"
|
|
#include "audiohardware/driverdefs.h"
|
|
#include "audiosoundtypes/simplesound.h"
|
|
#include "audiosoundtypes/streamingsound.h"
|
|
#include "audiosoundtypes/sounddefs.h"
|
|
#include "audiosoundtypes/externalstreamsound.h"
|
|
#include "control/replay/ReplaySettings.h"
|
|
#include "debugaudio.h"
|
|
|
|
AUDIO_MUSIC_OPTIMISATIONS()
|
|
|
|
s32 g_TrackReleaseTime = -1;
|
|
//leave this on for now since hopefully the root cause of continuous spew has been fixed
|
|
bool g_WarnOnPrepareProbs = true;
|
|
|
|
//
|
|
// PURPOSE
|
|
// Radio track state enum, used for virtualization management.
|
|
//
|
|
enum
|
|
{
|
|
AUD_TRACK_STATE_DORMANT,
|
|
AUD_TRACK_STATE_VIRTUAL,
|
|
AUD_TRACK_STATE_STARTING_PHYSICAL,
|
|
AUD_TRACK_STATE_PHYSICAL,
|
|
AUD_TRACK_STATE_STOPPING_PHYSICAL
|
|
};
|
|
|
|
const u32 g_RadioDjMarkerCategoryHash = ATSTRINGHASH("DJ", 0x008dba0f8);
|
|
const u32 g_RadioDjMarkerNameIntroStartHash = ATSTRINGHASH("INTRO_START", 0x0449687ba);
|
|
const u32 g_RadioDjMarkerNameIntroEndHash = ATSTRINGHASH("INTRO_END", 0x02cc6e05e);
|
|
const u32 g_RadioDjMarkerNameOutroStartHash = ATSTRINGHASH("OUTRO_START", 0x07dce9fa0);
|
|
const u32 g_RadioDjMarkerNameOutroEndHash = ATSTRINGHASH("OUTRO_END", 0x071d8dcad);
|
|
const u32 g_RadioTrackIdCategoryHash = ATSTRINGHASH("TRACKID", 0x0a6d93246);
|
|
|
|
float audRadioTrack::sm_LargeReverbMax = 0.f;
|
|
float audRadioTrack::sm_MediumReverbMax = 0.f;
|
|
float audRadioTrack::sm_SmallReverbMax = 0.f;
|
|
float audRadioTrack::sm_VolumeMax = -100.f;
|
|
|
|
float audRadioTrack::sm_LargeReverbMaxPrinted = 0.6f;
|
|
float audRadioTrack::sm_MediumReverbMaxPrinted = 0.6f;
|
|
float audRadioTrack::sm_SmallReverbMaxPrinted = 0.6f;
|
|
float audRadioTrack::sm_VolumeMaxPrinted = 19.f;
|
|
|
|
audRadioTrack::audRadioTrack()
|
|
{
|
|
WIN32PC_ONLY(m_MediaReader = NULL);
|
|
|
|
Reset();
|
|
ShutdownUserMusic();
|
|
}
|
|
|
|
void audRadioTrack::Init(const s32 category, const s32 trackIndex, const u32 soundRef, const float startOffset, const bool isMixStationTrack, const bool isReverbStationTrack, const u32 stationNameHash)
|
|
{
|
|
naAssertf(!m_IsInitialised, "Attempting to init audRadioTrack but it's already been initialised");
|
|
for(u32 emitterIndex=0; emitterIndex<g_NumRadioStationShadowedEmitters; emitterIndex++)
|
|
{
|
|
naAssertf(!m_ShadowedEmitterSounds[emitterIndex], "Emmiter sound at index %d is not null during init", emitterIndex);
|
|
}
|
|
Reset();
|
|
|
|
m_IsReverbStationTrack = isReverbStationTrack;
|
|
m_IsMixStationTrack = isMixStationTrack;
|
|
m_Category = category;
|
|
m_TrackIndex = trackIndex;
|
|
m_SoundRef = soundRef;
|
|
m_TimePlayed = 0;
|
|
m_StationNameHash = stationNameHash;
|
|
m_IsFlyloPart1 = false;
|
|
|
|
#if __WIN32PC
|
|
m_IsUserTrack = IsUserTrackSound(soundRef);
|
|
m_StreamerStartOffset = 0;
|
|
#endif
|
|
|
|
if(soundRef == ATSTRINGHASH("RADIO_14_DANCE_02_FLYLO_PART1", 0x146B859F) ||
|
|
soundRef == ATSTRINGHASH("RADIO_14_DANCE_02_FLYLO_PART2", 0x45D4E879))
|
|
{
|
|
m_StereoVolumeOffset = 0.5f;
|
|
m_IsFlyloPart1 = true;
|
|
}
|
|
else if(soundRef == ATSTRINGHASH("RADIO_13_JAZZ_WWFM_P1", 0xF8C7E995) ||
|
|
soundRef == ATSTRINGHASH("RADIO_13_JAZZ_WWFM_P2", 0xF7B1703) ||
|
|
soundRef == ATSTRINGHASH("RADIO_13_JAZZ_WWFM_P3", 0x7DBC7384) ||
|
|
soundRef == ATSTRINGHASH("RADIO_13_JAZZ_WWFM_P3_Start", 0x708B73A2) ||
|
|
soundRef == ATSTRINGHASH("RADIO_13_JAZZ_WWFM_P3_Alt", 0xD56A23A8) ||
|
|
soundRef == ATSTRINGHASH("RADIO_13_JAZZ_WWFM_P4", 0x69FF4C0A))
|
|
{
|
|
m_StereoVolumeOffset = 1.f;
|
|
}
|
|
else
|
|
{
|
|
m_StereoVolumeOffset = 0.f;
|
|
}
|
|
|
|
m_IsInitialised = true;
|
|
m_HasParsedMarkers = false;
|
|
|
|
m_RefForHistory = MakeTrackId(category, (u32)trackIndex, soundRef);
|
|
|
|
if(!m_IsUserTrack)
|
|
{
|
|
const StreamingSound *streamingSound = SOUNDFACTORY.DecompressMetadata<StreamingSound>(soundRef);
|
|
if(naVerifyf(streamingSound, "Unable to get streaming metadata from track metadata during init: %s (%u) / Category: %s index %u", SOUNDFACTORY.GetMetadataManager().GetObjectName(soundRef), soundRef, TrackCats_ToString((TrackCats)category), trackIndex))
|
|
{
|
|
m_Duration = streamingSound->Duration;
|
|
}
|
|
|
|
if(soundRef == ATSTRINGHASH("RADIO_13_JAZZ_WWFM_P3_Alt", 0xD56A23A8))
|
|
{
|
|
enum {WWFMStartOffset = 278982};
|
|
m_PlayTime = (s32)(WWFMStartOffset + (m_Duration - WWFMStartOffset) * startOffset);
|
|
}
|
|
else
|
|
{
|
|
m_PlayTime = (s32)(m_Duration * startOffset);
|
|
}
|
|
|
|
m_PlayTimeCalculationMixerFrame = audDriver::GetMixer()->GetMixerTimeFrames();
|
|
}
|
|
else
|
|
{
|
|
m_PlayTime = 0;
|
|
m_PlayTimeCalculationMixerFrame = audDriver::GetMixer()->GetMixerTimeFrames();
|
|
#if RSG_PC
|
|
m_Duration = audRadioStation::GetUserRadioTrackManager()->GetTrackDuration(trackIndex);
|
|
#else
|
|
m_Duration = 0;
|
|
#endif
|
|
}
|
|
|
|
// Cache start offset for playtime adjustment later
|
|
m_StartOffset = (s32)m_PlayTime;
|
|
|
|
char textIdObjName[16];
|
|
formatf(textIdObjName, "RTT_%08X", audRadioTrack::GetBeatSoundName(soundRef));
|
|
const RadioTrackTextIds *textIds = audNorthAudioEngine::GetObject<RadioTrackTextIds>(textIdObjName);
|
|
if(textIds)
|
|
{
|
|
m_FoundTrackTextIds = true;
|
|
for(u32 i = 0; i < textIds->numTextIds; i++)
|
|
{
|
|
if(!m_TextIds.IsFull())
|
|
{
|
|
audTextIdMarker &m = m_TextIds.Append();
|
|
m.playtimeMs = textIds->TextId[i].OffsetMs;
|
|
m.textId = textIds->TextId[i].TextId;
|
|
}
|
|
else
|
|
{
|
|
audWarningf("%s has too many TextIDs: %d vs %d", GetBankName(), textIds->numTextIds, kMaxTrackTextIds);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
formatf(textIdObjName, "RTB_%08X", audRadioTrack::GetBeatSoundName(soundRef));
|
|
const RadioTrackTextIds *beatMarkers = audNorthAudioEngine::GetObject<RadioTrackTextIds>(textIdObjName);
|
|
if(beatMarkers)
|
|
{
|
|
m_FoundBeatMarkers = true;
|
|
for(u32 i = 0; i < beatMarkers->numTextIds; i++)
|
|
{
|
|
if(!m_BeatMarkers.IsFull())
|
|
{
|
|
BeatMarker &m = m_BeatMarkers.Append();
|
|
m.numBeats = beatMarkers->TextId[i].TextId;
|
|
m.playtimeMs = beatMarkers->TextId[i].OffsetMs;
|
|
}
|
|
else
|
|
{
|
|
audWarningf("%s has too many beat markers (%d vs %d)", GetBankName(), beatMarkers->numTextIds, kMaxBeatMarkers);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
u32 audRadioTrack::GetBeatSoundName(u32 soundHash)
|
|
{
|
|
u32 beatSoundHash = soundHash;
|
|
|
|
// url:bugstar:6812963 - Kult FM BPM's are slow in-game
|
|
// Manually override the track IDs for Kult FM tracks (requireda s we updated the sound .xsl generator to cope with the
|
|
// unique bank layout, but not the track ID generator .dll)
|
|
switch (soundHash)
|
|
{
|
|
case 0x3597022E /*HEI4_RADIO_KULT_AGE_OF_CONSENT*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_AGE_OF_CONSENT", 0x203DD604); break;
|
|
case 0x7CA143C0 /*HEI4_RADIO_KULT_ALIEN_CRIME_LORD*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_ALIEN_CRIME_LORD", 0xD6EFB84F); break;
|
|
case 0x69469669 /*HEI4_RADIO_KULT_BABY_I_LOVE_YOU_SO*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_BABY_I_LOVE_YOU_SO", 0xA7772305); break;
|
|
case 0xAF7E2F3A /*HEI4_RADIO_KULT_CYCLE*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_CYCLE", 0x2247DFEC); break;
|
|
case 0x5212E8BE /*HEI4_RADIO_KULT_DEEP*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_DEEP", 0x3303D8D0); break;
|
|
case 0x45F1D4D4 /*HEI4_RADIO_KULT_DOWN_ON_THE_STREET*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_DOWN_ON_THE_STREET", 0xCDD7AD08); break;
|
|
case 0xE4A2BC39 /*HEI4_RADIO_KULT_DRAB_MEASURE*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_DRAB_MEASURE", 0xDCC1D3B0); break;
|
|
case 0x4CF2CE52 /*HEI4_RADIO_KULT_EISBAR*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_EISBAR", 0xB94A8B0E); break;
|
|
case 0xB0927F93 /*HEI4_RADIO_KULT_FACELESS*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_FACELESS", 0x27727CB); break;
|
|
case 0xD1A00144 /*HEI4_RADIO_KULT_FOUR_SHADOWS*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_FOUR_SHADOWS", 0x25A41D9B); break;
|
|
case 0x49AB9DC6 /*HEI4_RADIO_KULT_GIRLS_AND_BOYS*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_GIRLS_AND_BOYS", 0x84AA491C); break;
|
|
case 0xDFB0C335 /*HEI4_RADIO_KULT_GOING_BACK_TO_CALI*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_GOING_BACK_TO_CALI", 0xC86AC638); break;
|
|
case 0x9C4BFDD0 /*HEI4_RADIO_KULT_HARD_TO_EXPLAIN*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_HARD_TO_EXPLAIN", 0xC66479AB); break;
|
|
case 0xD968602A /*HEI4_RADIO_KULT_HUMAN_FLY*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_HUMAN_FLY", 0xBCAE2A58); break;
|
|
case 0x8925D95E /*HEI4_RADIO_KULT_ITS_YOURS*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_ITS_YOURS", 0xBEEAD8BE); break;
|
|
case 0xAF409C04 /*HEI4_RADIO_KULT_LFT_ME_LONELY*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_LFT_ME_LONELY", 0x535A265F); break;
|
|
case 0xB98381AF /*HEI4_RADIO_KULT_LIEBE_AUF_DEN_ERSTEN_BLICK*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_LIEBE_AUF_DEN_ERSTEN_BLICK", 0xE9583693); break;
|
|
case 0xA23BA519 /*HEI4_RADIO_KULT_MANY_TEARS_AGO*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_MANY_TEARS_AGO", 0xA62E5143); break;
|
|
case 0x36CC3FE5 /*HEI4_RADIO_KULT_NIGHTCLUBBING*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_NIGHTCLUBBING", 0x95067767); break;
|
|
case 0xD70C78A2 /*HEI4_RADIO_KULT_ON_THE_LEVEL*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_ON_THE_LEVEL", 0x68774DA0); break;
|
|
case 0x256500C1 /*HEI4_RADIO_KULT_POOL_SONG*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_POOL_SONG", 0x507B8473); break;
|
|
case 0xBB0D511E /*HEI4_RADIO_KULT_RAGA_MADHUVANTI*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_RAGA_MADHUVANTI", 0xE1AD1A43); break;
|
|
case 0x5E34F34A /*HEI4_RADIO_KULT_ROCK_AND_ROLL*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_ROCK_AND_ROLL", 0xE8D5BEEF); break;
|
|
case 0x669D5285 /*HEI4_RADIO_KULT_SHES_LOST_CONTROL*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_SHES_LOST_CONTROL", 0x148B6243); break;
|
|
case 0xBF6EA304 /*HEI4_RADIO_KULT_SO_IT_GOES*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_SO_IT_GOES", 0x50E931A1); break;
|
|
case 0x425996B3 /*HEI4_RADIO_KULT_TAINTED_LOVE*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_TAINTED_LOVE", 0x91CAD1CF); break;
|
|
case 0xB838EB1B /*HEI4_RADIO_KULT_TAKE_DOWN_THE_HOUSE*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_TAKE_DOWN_THE_HOUSE", 0xDC0DCC52); break;
|
|
case 0x9DA1CA5B /*HEI4_RADIO_KULT_THE_ADULTS_ARE_TALKING*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_THE_ADULTS_ARE_TALKING", 0xEBD4B927); break;
|
|
case 0xA8AB4150 /*HEI4_RADIO_KULT_THE_ETERNAL_TAU*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_THE_ETERNAL_TAU", 0x6702A27D); break;
|
|
case 0xC658DE05 /*HEI4_RADIO_KULT_THIS_IS_THE_DAY*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_THIS_IS_THE_DAY", 0xFFCF0A95); break;
|
|
case 0x82A07F0 /*HEI4_RADIO_KULT_TIME_BOMB*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_TIME_BOMB", 0x1E297FDF); break;
|
|
case 0xEE49D6D7 /*HEI4_RADIO_KULT_TOO_MUCH_MONEY*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_TOO_MUCH_MONEY", 0x6E30006B); break;
|
|
case 0xD010E265 /*HEI4_RADIO_KULT_TV_CASUALTY*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_TV_CASUALTY", 0x6D73F8A4); break;
|
|
case 0xE36F4E9B /*HEI4_RADIO_KULT_TYPICAL_GIRLS*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_TYPICAL_GIRLS", 0x6C1213CA); break;
|
|
case 0xC271F39A /*HEI4_RADIO_KULT_WHERE_NO_EAGLES_FLY*/: beatSoundHash = ATSTRINGHASH("DLC_HEI4_MUSIC_WHERE_NO_EAGLES_FLY", 0xCB712A21); break;
|
|
default: break;
|
|
}
|
|
|
|
return beatSoundHash;
|
|
}
|
|
|
|
u32 audRadioTrack::FindTextIdForSoundHash(const u32 soundHash)
|
|
{
|
|
char textIdObjName[16];
|
|
formatf(textIdObjName, "RTT_%08X", audRadioTrack::GetBeatSoundName(soundHash));
|
|
const RadioTrackTextIds *textIds = audNorthAudioEngine::GetObject<RadioTrackTextIds>(textIdObjName);
|
|
if(textIds)
|
|
{
|
|
return textIds->TextId[0].TextId;
|
|
}
|
|
|
|
// Score tracks have a different setup; we need to find the bank name
|
|
class BankListBuilderFn : public audSoundFactory::audSoundProcessHierarchyFn
|
|
{
|
|
public:
|
|
u32 bankId;
|
|
BankListBuilderFn()
|
|
{
|
|
bankId = AUD_INVALID_BANK_ID;
|
|
}
|
|
|
|
void operator()(u32 classID, const void* soundData)
|
|
{
|
|
if(classID == SimpleSound::TYPE_ID)
|
|
{
|
|
const SimpleSound * sound = reinterpret_cast<const SimpleSound*>(soundData);
|
|
bankId = g_AudioEngine.GetSoundManager().GetFactory().GetBankIndexFromMetadataRef(sound->WaveRef.BankName);
|
|
}
|
|
}
|
|
};
|
|
|
|
BankListBuilderFn bankListBuilder;
|
|
SOUNDFACTORY.ProcessHierarchy(soundHash, bankListBuilder);
|
|
if(bankListBuilder.bankId < AUD_INVALID_BANK_ID)
|
|
{
|
|
const char *bankName = g_AudioEngine.GetSoundManager().GetFactory().GetBankNameFromIndex(bankListBuilder.bankId);
|
|
char modifiedBankName[64];
|
|
safecpy(modifiedBankName, bankName);
|
|
const size_t len = strlen(modifiedBankName);
|
|
for(size_t i = 0; i < len; i++)
|
|
{
|
|
if(modifiedBankName[i] == '/' || modifiedBankName[i] == '\\')
|
|
{
|
|
modifiedBankName[i] = '_';
|
|
}
|
|
}
|
|
formatf(textIdObjName, "RTT_%08X", atStringHash(modifiedBankName));
|
|
const RadioTrackTextIds *textIds = audNorthAudioEngine::GetObject<RadioTrackTextIds>(textIdObjName);
|
|
if(textIds)
|
|
{
|
|
return textIds->TextId[0].TextId;
|
|
}
|
|
}
|
|
|
|
BANK_ONLY(audWarningf("Failed to find TextId for %u (%s)", soundHash, SOUNDFACTORY.GetMetadataManager().GetObjectName(soundHash)));
|
|
return 1;
|
|
}
|
|
|
|
bool audRadioTrack::IsScoreTrack(const u32 soundHash)
|
|
{
|
|
const u32 textTrackId = FindTextIdForSoundHash(soundHash);
|
|
return textTrackId >= kFirstScoreTrackId && textTrackId < kFirstCommercialTrackId;
|
|
}
|
|
|
|
bool audRadioTrack::IsCommercial(const u32 soundHash)
|
|
{
|
|
const u32 textTrackId = FindTextIdForSoundHash(soundHash);
|
|
return textTrackId >= kFirstCommercialTrackId && textTrackId < kFirstSFXTrackId;
|
|
}
|
|
|
|
bool audRadioTrack::IsSFX(const u32 soundHash)
|
|
{
|
|
const u32 textTrackId = FindTextIdForSoundHash(soundHash);
|
|
return textTrackId >= kFirstSFXTrackId;
|
|
}
|
|
|
|
bool audRadioTrack::IsAmbient(const u32 soundHash)
|
|
{
|
|
const u32 textTrackId = FindTextIdForSoundHash(soundHash);
|
|
return textTrackId >= kFirstAmbinetTrackId;
|
|
}
|
|
|
|
#if __WIN32PC
|
|
bool audRadioTrack::IsUserTrackSound(const u32 soundRef)
|
|
{
|
|
return (soundRef == ATSTRINGHASH("DLC_RADIO_19_USER_NULLTRACK", 0xC96DC433));
|
|
}
|
|
#endif // __WIN32PC
|
|
|
|
void audRadioTrack::Shutdown(void)
|
|
{
|
|
#if __WIN32PC
|
|
if(m_IsUserTrack && m_State == AUD_TRACK_STATE_PHYSICAL && m_StreamingSound && m_StreamingSound->GetPlayState() == AUD_SOUND_PLAYING)
|
|
{
|
|
m_StreamingSound->SetReleaseTime(500);
|
|
}
|
|
#endif /// __WIN32PC
|
|
|
|
if(m_StreamingSound)
|
|
{
|
|
m_StreamingSound->StopAndForget();
|
|
}
|
|
|
|
//Ensure we have cleaned up any active shadow sound positioned emitters.
|
|
for(u8 emitterIndex=0; emitterIndex<g_NumRadioStationShadowedEmitters; emitterIndex++)
|
|
{
|
|
if(m_ShadowedEmitterSounds[emitterIndex] != NULL)
|
|
{
|
|
m_ShadowedEmitterSounds[emitterIndex]->StopAndForget();
|
|
}
|
|
}
|
|
|
|
ShutdownUserMusic();
|
|
Reset();
|
|
}
|
|
|
|
void audRadioTrack::Reset(void)
|
|
{
|
|
m_Category = NUM_RADIO_TRACK_CATS;
|
|
m_SoundRef = 0;
|
|
m_SoundBucketId = 0xff;
|
|
m_StreamingSound = NULL;
|
|
m_WaveSlot = NULL;
|
|
m_PlayTime = 0;
|
|
m_PlayTimeCalculationMixerFrame = 0;
|
|
m_Duration = 0;
|
|
m_LastUpdateTime = 0;
|
|
m_State = AUD_TRACK_STATE_DORMANT;
|
|
m_IsInitialised = false;
|
|
m_IsUserTrack = false;
|
|
|
|
memset(m_ShadowedEmitterSounds.GetElements(), 0, g_NumRadioStationShadowedEmitters * sizeof(audSound *));
|
|
m_DjRegions.Reset();
|
|
m_TextIds.Reset();
|
|
m_BeatMarkers.Reset();
|
|
|
|
m_Bpm = 0.f;
|
|
m_BeatNumber = 0;
|
|
m_BeatTimeS = 0.f;
|
|
|
|
m_FoundTrackTextIds = false;
|
|
m_FoundBeatMarkers = false;
|
|
m_StereoVolumeOffset = 0.f;
|
|
|
|
#if __WIN32PC
|
|
m_MakeUpGain = 0.f;
|
|
m_PostRoll = 0;
|
|
#endif
|
|
}
|
|
|
|
void audRadioTrack::ShutdownUserMusic()
|
|
{
|
|
#if __WIN32PC
|
|
if(m_IsUserTrack)
|
|
{
|
|
if(m_MediaReader)
|
|
{
|
|
audMediaReader::DestroyMediaStreamer(m_MediaReader);
|
|
}
|
|
m_MediaReader = NULL;
|
|
}
|
|
#endif
|
|
//m_IsUserTrack = false;
|
|
}
|
|
|
|
void audRadioTrack::Update(u32 timeInMs, bool isFrozen)
|
|
{
|
|
audPrepareState prepareState;
|
|
switch(m_State)
|
|
{
|
|
case AUD_TRACK_STATE_VIRTUAL:
|
|
if(!isFrozen)
|
|
{
|
|
if(m_LastUpdateTime == 0)
|
|
{
|
|
m_LastUpdateTime = timeInMs;
|
|
}
|
|
// compute virtual playtime
|
|
m_PlayTime += timeInMs - m_LastUpdateTime;
|
|
m_PlayTimeCalculationMixerFrame = audDriver::GetMixer()->GetMixerTimeFrames();
|
|
naAssertf(GetDuration() > 0, "Radio track with 0 duration. sound name: %s hash: %u", m_StreamingSound ? m_StreamingSound->GetName() : "(null)", m_SoundRef);
|
|
|
|
if(m_PlayTime >= (s32)GetDuration())
|
|
{
|
|
//We are past the end of the track, so shutdown.
|
|
Shutdown();
|
|
}
|
|
}
|
|
|
|
//Ensure we have cleaned up any active shadow sound positioned emitters.
|
|
for(u8 emitterIndex=0; emitterIndex<g_NumRadioStationShadowedEmitters; emitterIndex++)
|
|
{
|
|
if(m_ShadowedEmitterSounds[emitterIndex] != NULL)
|
|
{
|
|
m_ShadowedEmitterSounds[emitterIndex]->StopAndForget();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AUD_TRACK_STATE_STARTING_PHYSICAL:
|
|
// we lose time when waiting for the track to physically prepare; compensate for some of that
|
|
enum { PrepareTimeEstimate = 500 };
|
|
prepareState = Prepare((u32)m_PlayTime + (isFrozen? 0u : PrepareTimeEstimate));
|
|
|
|
switch(prepareState)
|
|
{
|
|
case AUD_PREPARED:
|
|
//Physically play our track sound.
|
|
if(!isFrozen)
|
|
{
|
|
if(m_IsUserTrack)
|
|
{
|
|
#if __WIN32PC
|
|
Assert(m_StreamingSound->GetSoundTypeID() == ExternalStreamSound::TYPE_ID);
|
|
audExternalStreamSound *str = reinterpret_cast<audExternalStreamSound*>(m_StreamingSound);
|
|
u32 usamplerate = m_MediaReader->GetSampleRate();
|
|
str->InitStreamPlayer(m_MediaReader->m_RingBuffer, 2, usamplerate); // for now for stereo
|
|
m_StreamingSound->PrepareAndPlay();
|
|
#endif
|
|
}
|
|
else
|
|
m_StreamingSound->Play();
|
|
|
|
m_State = AUD_TRACK_STATE_PHYSICAL;
|
|
}
|
|
break;
|
|
|
|
case AUD_PREPARE_FAILED:
|
|
Shutdown();
|
|
break;
|
|
|
|
//Intentional fall-through.
|
|
case AUD_PREPARING:
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case AUD_TRACK_STATE_PHYSICAL:
|
|
if(m_StreamingSound)
|
|
{
|
|
|
|
if(m_IsUserTrack)
|
|
{
|
|
#if __WIN32PC
|
|
m_PlayTime = m_StreamerStartOffset + ((audExternalStreamSound*)m_StreamingSound)->GetCurrentPlayTimeOfWave();
|
|
m_PlayTimeCalculationMixerFrame = audDriver::GetMixer()->GetMixerTimeFrames();
|
|
|
|
if(m_ShadowedEmitterSounds[0] == NULL && m_StreamingSound->GetPlayState() == AUD_SOUND_PLAYING)
|
|
{
|
|
const s32 pcmSourceId = ((audExternalStreamSound*)m_StreamingSound)->GetStreamPlayerId();
|
|
CreateShadowEmitterSounds(pcmSourceId, pcmSourceId, pcmSourceId);
|
|
}
|
|
|
|
// Check if we're beyond duration due to post-roll.
|
|
if(m_PlayTime >= GetDuration())
|
|
{
|
|
Shutdown();
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
m_PlayTime = ((audStreamingSound*)m_StreamingSound)->GetCurrentPlayTimeOfWave();
|
|
m_PlayTimeCalculationMixerFrame = ((audStreamingSound*)m_StreamingSound)->GetPlayTimeCalculationMixerFrame();
|
|
|
|
//Ensure we have active shadow sound positioned emitters.
|
|
// Wait until the streaming sound has been played to prevent the shadow sounds starting before the streaming sound
|
|
if(m_ShadowedEmitterSounds[0] == NULL && m_StreamingSound->GetPlayState() == AUD_SOUND_PLAYING)
|
|
{
|
|
const s32 pcmSourceIdL = ((audStreamingSound*)m_StreamingSound)->GetFirstWavePlayerId();
|
|
const s32 pcmSourceIdR = ((audStreamingSound*)m_StreamingSound)->GetWavePlayerIdCount() > 1 ? ((audStreamingSound*)m_StreamingSound)->GetWavePlayerId(1) : pcmSourceIdL;
|
|
const s32 pcmSourceIdReverb = ((audStreamingSound*)m_StreamingSound)->GetWavePlayerIdCount() > 2 ? ((audStreamingSound*)m_StreamingSound)->GetWavePlayerId(2) : pcmSourceIdL;
|
|
CreateShadowEmitterSounds(pcmSourceIdL, pcmSourceIdR, pcmSourceIdReverb);
|
|
}
|
|
|
|
if(!m_HasParsedMarkers)
|
|
{
|
|
ParseMarkers();
|
|
}
|
|
UpdateBeatInfo();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Our sounds have finished playing, so shutdown.
|
|
Shutdown();
|
|
}
|
|
break;
|
|
|
|
case AUD_TRACK_STATE_STOPPING_PHYSICAL:
|
|
//Stop our track sound and clear up our sound references.
|
|
if(m_StreamingSound)
|
|
{
|
|
// grab the latest play time
|
|
#if __WIN32PC
|
|
if(m_IsUserTrack && m_StreamingSound)
|
|
{
|
|
m_PlayTime = m_StreamerStartOffset + ((audExternalStreamSound*)m_StreamingSound)->GetCurrentPlayTimeOfWave();
|
|
m_PlayTimeCalculationMixerFrame = audDriver::GetMixer()->GetMixerTimeFrames();
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
m_PlayTime = ((audStreamingSound*)m_StreamingSound)->GetCurrentPlayTimeOfWave();
|
|
m_PlayTimeCalculationMixerFrame = ((audStreamingSound*)m_StreamingSound)->GetPlayTimeCalculationMixerFrame();
|
|
}
|
|
m_StreamingSound->StopAndForget();
|
|
}
|
|
ShutdownUserMusic();
|
|
|
|
m_State = AUD_TRACK_STATE_VIRTUAL;
|
|
break;
|
|
|
|
case AUD_TRACK_STATE_DORMANT: //Intentional fall-through.
|
|
default:
|
|
break;
|
|
}
|
|
|
|
m_LastUpdateTime = timeInMs;
|
|
}
|
|
|
|
void audRadioTrack::CreateShadowEmitterSounds(const s32 pcmSourceIdL, const s32 pcmSourceIdR, const s32 pcmSourceIDReverb)
|
|
{
|
|
audSoundInitParams initParams;
|
|
Assert(-1 != pcmSourceIdL && -1 != pcmSourceIdR);
|
|
initParams.TimerId = 2;
|
|
|
|
for(u32 emitterIndex=0; emitterIndex<g_NumRadioStationShadowedEmitters; emitterIndex++)
|
|
{
|
|
if(m_ShadowedEmitterSounds[emitterIndex] == NULL)
|
|
{
|
|
const u32 shadowSoundNameHash = (g_ScriptAudioEntity.IsFlagSet(audScriptAudioFlags::EnableGameplayCriticalMusicEmitters) && m_IsMixStationTrack) ? ATSTRINGHASH("GamePlayCriticalShadowEmitter", 0xF6CA611F) : ATSTRINGHASH("ShadowEmitter", 0x83B2D715);
|
|
|
|
if (pcmSourceIDReverb == pcmSourceIdL)
|
|
{
|
|
initParams.ShadowPcmSourceId = (emitterIndex & 1) == 1 ? pcmSourceIdR : pcmSourceIdL;
|
|
}
|
|
else
|
|
{
|
|
u32 soundIndex = emitterIndex % 3;
|
|
|
|
if (soundIndex == 0)
|
|
{
|
|
initParams.ShadowPcmSourceId = pcmSourceIdL;
|
|
}
|
|
else if (soundIndex == 1)
|
|
{
|
|
initParams.ShadowPcmSourceId = pcmSourceIdR;
|
|
}
|
|
else
|
|
{
|
|
initParams.ShadowPcmSourceId = pcmSourceIDReverb;
|
|
}
|
|
}
|
|
|
|
g_RadioAudioEntity.CreateAndPlaySound_Persistent(g_RadioAudioEntity.GetRadioSounds().Find(shadowSoundNameHash), &(m_ShadowedEmitterSounds[emitterIndex]), &initParams);
|
|
|
|
if (m_ShadowedEmitterSounds[emitterIndex] && m_IsReverbStationTrack)
|
|
{
|
|
m_ShadowedEmitterSounds[emitterIndex]->SetRequestedShouldDisableRearFiltering(AUD_TRISTATE_TRUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
u32 audRadioTrack::GetTextId() const
|
|
{
|
|
#if RSG_PC
|
|
if(m_IsUserTrack)
|
|
{
|
|
return GetUserTrackTextIdFromIndex(m_TrackIndex);
|
|
}
|
|
#endif
|
|
return GetTextId(static_cast<u32>(m_PlayTime));
|
|
}
|
|
|
|
u32 audRadioTrack::GetTextId(const u32 playTimeMs) const
|
|
{
|
|
#if RSG_PC
|
|
if(m_IsUserTrack)
|
|
{
|
|
return GetUserTrackTextIdFromIndex(m_TrackIndex);
|
|
}
|
|
#endif
|
|
u32 textId = 1;
|
|
for(u32 i = 0; i < m_TextIds.GetCount(); i++)
|
|
{
|
|
if(playTimeMs >= m_TextIds[i].playtimeMs)
|
|
{
|
|
textId = m_TextIds[i].textId;
|
|
}
|
|
else if(m_TextIds[i].playtimeMs > playTimeMs)
|
|
{
|
|
// may aswell stop the search; markers will be ordered
|
|
return textId;
|
|
}
|
|
}
|
|
return textId;
|
|
}
|
|
|
|
audPrepareState audRadioTrack::Prepare(u32 startOffset)
|
|
{
|
|
#if __WIN32PC
|
|
if(m_IsUserTrack)
|
|
return PrepareUserTrack(startOffset);
|
|
#endif
|
|
|
|
if(m_StreamingSound == NULL)
|
|
{
|
|
// Trying to play physically with no waveslot? Parent hasn't managed to allocate us a streaming slot, so mark as preparing
|
|
// so that we try again
|
|
if(!m_WaveSlot)
|
|
{
|
|
audWarningf("Track %u trying to start physically with no waveslot", m_SoundRef);
|
|
return AUD_PREPARING;
|
|
}
|
|
|
|
// if the radio timer is paused then we wont be clearing out old sounds, so we also don't want to be creating new ones
|
|
if(!g_AudioEngine.GetSoundManager().IsPaused(2))
|
|
{
|
|
//Create a new streaming sound.
|
|
audSoundInitParams initParams;
|
|
initParams.BucketId = m_SoundBucketId;
|
|
initParams.TimerId = 2;
|
|
initParams.StartOffset = startOffset;
|
|
// For now we can't virtualise streams
|
|
initParams.ShouldPlayPhysically = true;
|
|
|
|
// Start muted
|
|
initParams.Volume = -100.f;
|
|
|
|
const u32 trackSoundHash = m_SoundRef;
|
|
g_RadioAudioEntity.CreateSound_PersistentReference(trackSoundHash, (audSound**)&m_StreamingSound, &initParams);
|
|
if(m_StreamingSound == NULL || m_StreamingSound->GetSoundTypeID() != StreamingSound::TYPE_ID)
|
|
{
|
|
if(!m_StreamingSound)
|
|
{
|
|
naWarningf("Failed to create radio track sound %u", trackSoundHash);
|
|
}
|
|
else
|
|
{
|
|
naWarningf("radio track sound not of type streamingsound: %u", trackSoundHash);
|
|
}
|
|
return AUD_PREPARE_FAILED;
|
|
}
|
|
|
|
if (m_IsReverbStationTrack)
|
|
{
|
|
m_StreamingSound->SetRequestedShouldDisableRearFiltering(AUD_TRISTATE_TRUE);
|
|
}
|
|
|
|
m_Duration = m_StreamingSound->ComputeDurationMsExcludingStartOffsetAndPredelay(NULL);
|
|
}
|
|
else
|
|
{
|
|
// if we've not created our sound and the radio timer is paused then we're still preparing
|
|
return AUD_PREPARING;
|
|
}
|
|
}
|
|
|
|
audPrepareState state = m_StreamingSound->Prepare(m_WaveSlot, true);
|
|
|
|
if(state != AUD_PREPARED && g_WarnOnPrepareProbs)
|
|
{
|
|
if(m_WaveSlot && m_WaveSlot->GetReferenceCount() > 0)
|
|
{
|
|
naDebugf1("Radio is trying to stream into a wave slot with %u references: %s (currently loaded bank: %s). Prepare state %d.", m_WaveSlot->GetReferenceCount(), m_WaveSlot->GetSlotName(), m_WaveSlot->GetLoadedBankName(), state);
|
|
}
|
|
else if(!m_WaveSlot)
|
|
{
|
|
naWarningf("Radio preparing into NULL slot");
|
|
return AUD_PREPARE_FAILED;
|
|
}
|
|
}
|
|
|
|
return state;
|
|
}
|
|
|
|
#if __WIN32PC
|
|
audPrepareState audRadioTrack::PrepareUserTrack(u32 startOffset)
|
|
{
|
|
if(m_StreamingSound == NULL)
|
|
{
|
|
m_IsUserTrack = true;
|
|
// Trying to play physically with no waveslot? Parent hasn't managed to allocate us a streaming slot, so mark as preparin
|
|
// so that we try again
|
|
//if(!m_WaveSlot)
|
|
//{
|
|
// return AUD_PREPARING;
|
|
//}
|
|
|
|
// if the radio timer is paused then we wont be clearing out old sounds, so we also don't want to be creating new ones
|
|
if(!g_AudioEngine.GetSoundManager().IsPaused(2))
|
|
{
|
|
//Create a new streaming sound.
|
|
audSoundInitParams initParams;
|
|
//initParams.BucketId = m_SoundBucketId;
|
|
initParams.TimerId = 2;
|
|
initParams.StartOffset = 0;// startOffset;
|
|
// For now we can't virtualise streams
|
|
initParams.ShouldPlayPhysically = true;
|
|
|
|
// Start muted
|
|
initParams.Volume = -100.f;
|
|
|
|
const audMetadataRef trackSoundHash = g_RadioAudioEntity.GetRadioSounds().Find(ATSTRINGHASH("UserMusicSound", 0xE9DE2CD9));
|
|
g_RadioAudioEntity.CreateSound_PersistentReference(trackSoundHash, (audSound**)&m_StreamingSound, &initParams);
|
|
if(m_StreamingSound == NULL || m_StreamingSound->GetSoundTypeID() != ExternalStreamSound::TYPE_ID)
|
|
{
|
|
if(!m_StreamingSound)
|
|
{
|
|
naWarningf("Failed to create radio track sound %u", trackSoundHash);
|
|
}
|
|
else
|
|
{
|
|
naWarningf("radio track sound not of type ExternalStreamSound: %u", trackSoundHash);
|
|
}
|
|
return AUD_PREPARE_FAILED;
|
|
}
|
|
|
|
audPrepareState state = CreateStreamer(startOffset);
|
|
m_StreamerStartOffset = startOffset;
|
|
if(state == AUD_PREPARE_FAILED)
|
|
{
|
|
return AUD_PREPARE_FAILED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if we've not created our sound and the radio timer is paused then we're still preparing
|
|
return AUD_PREPARING;
|
|
}
|
|
}
|
|
|
|
if(m_MediaReader)
|
|
{
|
|
s32 user_state = m_MediaReader->GetState();
|
|
if(user_state == TRACK_STATE_PREPARED)
|
|
{
|
|
m_Duration = m_MediaReader->GetStreamLengthMs();
|
|
return AUD_PREPARED;
|
|
}
|
|
if(user_state == TRACK_STATE_FAILED) // we have a media reader, but the actual file is bad
|
|
{
|
|
///OMG better deal with this, shut down and try next track
|
|
if(m_StreamingSound)
|
|
{
|
|
m_StreamingSound->StopAndForget();
|
|
}
|
|
if(m_MediaReader)
|
|
{
|
|
audMediaReader::DestroyMediaStreamer(m_MediaReader);
|
|
}
|
|
m_MediaReader = NULL;
|
|
return AUD_PREPARE_FAILED;
|
|
}
|
|
}
|
|
return AUD_PREPARING;
|
|
}
|
|
|
|
audPrepareState audRadioTrack::CreateStreamer(u32 startOffset)
|
|
{
|
|
s32 track = m_TrackIndex;
|
|
if(track != -1)
|
|
{
|
|
u32 trim = startOffset;
|
|
Displayf("Preparing User Track : %s at %d", audRadioStation::GetUserRadioTrackManager()->GetFileName(track), startOffset);
|
|
if(startOffset == 0)
|
|
{
|
|
trim = audRadioStation::GetUserRadioTrackManager()->GetStartOffsetMs(track);
|
|
}
|
|
SetGainAndPostRoll(audRadioStation::GetUserRadioTrackManager()->ComputeTrackMakeUpGain(track), audRadioStation::GetUserRadioTrackManager()->GetTrackPostRoll(track));
|
|
m_MediaReader = audMediaReader::CreateMediaStreamer(audRadioStation::GetUserRadioTrackManager()->GetFileName(track), &m_StreamingSound, trim);
|
|
if(!m_MediaReader)
|
|
{
|
|
naWarningf("Failed to create radio track user music audMediaReader: %s",
|
|
audRadioStation::GetUserRadioTrackManager()->GetFileName(track));
|
|
return AUD_PREPARE_FAILED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
naWarningf("Failed to create radio track user music audMediaReader, no valid track");
|
|
return AUD_PREPARE_FAILED;
|
|
}
|
|
|
|
return AUD_PREPARING;
|
|
}
|
|
#endif
|
|
|
|
void audRadioTrack::PlayWhenReady()
|
|
{
|
|
if(naVerifyf(m_State == AUD_TRACK_STATE_DORMANT, "Attempted to play a radio track with state %d when it should be in state %d (AUD_TRACK_STATE_DORMANT)", m_State, AUD_TRACK_STATE_DORMANT))
|
|
{
|
|
#if __WIN32PC
|
|
if(m_IsUserTrack)
|
|
{
|
|
m_State = AUD_TRACK_STATE_STARTING_PHYSICAL;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if(m_WaveSlot)
|
|
{
|
|
m_State = AUD_TRACK_STATE_STARTING_PHYSICAL;
|
|
}
|
|
else
|
|
{
|
|
m_State = AUD_TRACK_STATE_VIRTUAL;
|
|
}
|
|
}
|
|
}
|
|
|
|
void audRadioTrack::Play(void)
|
|
{
|
|
if(naVerifyf(m_State == AUD_TRACK_STATE_DORMANT, "Attempted to play a radio track with state %d when it should be in state %d (AUD_TRACK_STATE_DORMANT)", m_State, AUD_TRACK_STATE_DORMANT))
|
|
{
|
|
if(m_StreamingSound)
|
|
{
|
|
//Physically play our track sound.
|
|
if(m_IsUserTrack)
|
|
{
|
|
#if __WIN32PC
|
|
Assert(m_StreamingSound->GetSoundTypeID() == ExternalStreamSound::TYPE_ID);
|
|
audExternalStreamSound *str = reinterpret_cast<audExternalStreamSound*>(m_StreamingSound);
|
|
u32 usamplerate = m_MediaReader->GetSampleRate();
|
|
str->InitStreamPlayer(m_MediaReader->m_RingBuffer, 2, usamplerate); // for now for stereo
|
|
m_StreamingSound->PrepareAndPlay();
|
|
#endif
|
|
}
|
|
else
|
|
m_StreamingSound->Play();
|
|
|
|
m_State = AUD_TRACK_STATE_PHYSICAL;
|
|
}
|
|
else
|
|
{
|
|
m_State = AUD_TRACK_STATE_VIRTUAL;
|
|
}
|
|
|
|
m_TimePlayed = audDriver::GetMixer()->GetMixerTimeMs();
|
|
}
|
|
}
|
|
|
|
void audRadioTrack::SetPhysicalStreamingState(bool shouldStreamPhysically, audWaveSlot *waveSlot, u8 soundBucketId)
|
|
{
|
|
if(shouldStreamPhysically)
|
|
{
|
|
m_WaveSlot = waveSlot;
|
|
m_SoundBucketId = soundBucketId;
|
|
|
|
//Assert(m_State != AUD_TRACK_STATE_STOPPING_PHYSICAL);
|
|
if(m_State == AUD_TRACK_STATE_VIRTUAL)
|
|
{
|
|
m_State = AUD_TRACK_STATE_STARTING_PHYSICAL;
|
|
}
|
|
//else if(m_State == AUD_TRACK_STATE_DORMANT) - If this track is dormant, preparation should be handled explicitly by the radio station.
|
|
}
|
|
else
|
|
{
|
|
if(m_State == AUD_TRACK_STATE_PHYSICAL || m_State == AUD_TRACK_STATE_STARTING_PHYSICAL)
|
|
{
|
|
m_State = AUD_TRACK_STATE_STOPPING_PHYSICAL;
|
|
}
|
|
else if(m_State == AUD_TRACK_STATE_DORMANT)
|
|
{
|
|
//Ensure we clean up our sounds if we have created but not played them yet.
|
|
if(m_StreamingSound)
|
|
{
|
|
m_StreamingSound->StopAndForget();
|
|
}
|
|
ShutdownUserMusic();
|
|
}
|
|
}
|
|
}
|
|
|
|
void audRadioTrack::UpdateStereoEmitter(f32 volumeDb, f32 cutoff)
|
|
{
|
|
#if GTA_REPLAY
|
|
if(CReplayMgr::IsEditModeActive())
|
|
{
|
|
volumeDb = g_SilenceVolume;
|
|
#if __WIN32PC
|
|
m_MakeUpGain = 0.f;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
if(m_StreamingSound)
|
|
{
|
|
#if __WIN32PC
|
|
m_StreamingSound->SetRequestedVolume(volumeDb + m_MakeUpGain);
|
|
#else
|
|
m_StreamingSound->SetRequestedVolume(volumeDb);
|
|
#endif
|
|
m_StreamingSound->SetRequestedLPFCutoff(static_cast<u32>(cutoff));
|
|
m_StreamingSound->SetRequestedPostSubmixVolumeAttenuation(m_StereoVolumeOffset);
|
|
}
|
|
}
|
|
|
|
void audRadioTrack::UpdatePositionedEmitter(const u32 emitterIndex, f32 emittedVolumeDb, f32 volumeOffsetDb, const u32 lpfCutoff, const u32 hpfCutoff, const Vector3 &position, const audEnvironmentGameMetric *occlusionMetric, const u32 emitterType, const f32 environmentalLoudness)
|
|
{
|
|
#if !__ASSERT
|
|
(void)emitterType;
|
|
#endif
|
|
|
|
f32 volumeDb = emittedVolumeDb + volumeOffsetDb;
|
|
#if GTA_REPLAY
|
|
if(CReplayMgr::IsEditModeActive())
|
|
{
|
|
volumeDb = g_SilenceVolume;
|
|
#if __WIN32PC
|
|
m_MakeUpGain = 0.f;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
naCErrorf(emitterIndex < g_NumRadioStationShadowedEmitters, "Emitter index passed in to UpdatePositionedEmitter (%d) is out of bounds", emitterIndex);
|
|
if((emitterIndex < g_NumRadioStationShadowedEmitters) && m_ShadowedEmitterSounds[emitterIndex])
|
|
{
|
|
audRequestedSettings *requestedSettings = m_ShadowedEmitterSounds[emitterIndex]->GetRequestedSettings();
|
|
|
|
audEnvironmentGameMetric &reqSetMetric = requestedSettings->GetEnvironmentGameMetric();
|
|
reqSetMetric = *occlusionMetric;
|
|
|
|
reqSetMetric.SetReverbLarge(Clamp(reqSetMetric.GetReverbLarge(), 0.f, 1.f));
|
|
reqSetMetric.SetReverbMedium(Clamp(reqSetMetric.GetReverbMedium(), 0.f, 1.f));
|
|
reqSetMetric.SetReverbSmall(Clamp(reqSetMetric.GetReverbSmall(), 0.f, 1.f));
|
|
|
|
sm_LargeReverbMax = Max(sm_LargeReverbMax, reqSetMetric.GetReverbLarge());
|
|
sm_MediumReverbMax = Max(sm_MediumReverbMax, reqSetMetric.GetReverbMedium());
|
|
sm_SmallReverbMax = Max(sm_SmallReverbMax, reqSetMetric.GetReverbSmall());
|
|
sm_VolumeMax = Max(sm_VolumeMax, volumeDb);
|
|
|
|
#if RSG_BANK
|
|
if(sm_LargeReverbMax >= sm_LargeReverbMaxPrinted + 1.f)
|
|
{
|
|
audWarningf("Large reverb max send level increased: %f (%s emitter %d type %d pos [%f,%f,%f]) "
|
|
, sm_LargeReverbMax
|
|
, m_StreamingSound ? m_StreamingSound->GetName() : "(NULL sound)"
|
|
, emitterIndex
|
|
, emitterType
|
|
, position.x
|
|
, position.y
|
|
, position.z
|
|
);
|
|
|
|
sm_LargeReverbMaxPrinted = sm_LargeReverbMax;
|
|
}
|
|
if(sm_MediumReverbMax >= sm_MediumReverbMaxPrinted + 1.f)
|
|
{
|
|
audWarningf("Medium reverb max send level increased: %f (%s emitter %d type %d pos [%f,%f,%f]) "
|
|
, sm_MediumReverbMax
|
|
, m_StreamingSound ? m_StreamingSound->GetName() : "(NULL sound)"
|
|
, emitterIndex
|
|
, emitterType
|
|
, position.x
|
|
, position.y
|
|
, position.z
|
|
);
|
|
|
|
sm_MediumReverbMaxPrinted = sm_MediumReverbMax;
|
|
}
|
|
|
|
if(sm_SmallReverbMax >= sm_SmallReverbMaxPrinted + 1.f)
|
|
{
|
|
audWarningf("Small reverb max send level increased: %f (%s emitter %d type %d pos [%f,%f,%f]) "
|
|
, sm_SmallReverbMax
|
|
, m_StreamingSound ? m_StreamingSound->GetName() : "(NULL sound)"
|
|
, emitterIndex
|
|
, emitterType
|
|
, position.x
|
|
, position.y
|
|
, position.z
|
|
);
|
|
|
|
sm_SmallReverbMaxPrinted = sm_SmallReverbMax;
|
|
}
|
|
|
|
if(sm_VolumeMax >= sm_VolumeMaxPrinted + 1.f)
|
|
{
|
|
audWarningf("Emitter volume max level increased: %f (%s emitter %d type %d pos [%f,%f,%f]) "
|
|
, sm_VolumeMax
|
|
, m_StreamingSound ? m_StreamingSound->GetName() : "(NULL sound)"
|
|
, emitterIndex
|
|
, emitterType
|
|
, position.x
|
|
, position.y
|
|
, position.z
|
|
);
|
|
|
|
sm_VolumeMaxPrinted = sm_VolumeMax;
|
|
}
|
|
#endif
|
|
|
|
if (volumeDb >= 36.f)
|
|
{
|
|
audWarningf("Radio emitter track volume: %f (Emitter Vol: %f Offset Vol: %f, %s emitter %d type %d pos [%f,%f,%f]) - will clamp"
|
|
, volumeDb
|
|
, emittedVolumeDb
|
|
, volumeOffsetDb
|
|
, m_StreamingSound ? m_StreamingSound->GetName() : "(NULL sound)"
|
|
, emitterIndex
|
|
, emitterType
|
|
, position.x
|
|
, position.y
|
|
, position.z
|
|
);
|
|
}
|
|
|
|
// Don't allow volume to go beyond category headroom (which is approx 32dB)
|
|
volumeDb = Clamp(volumeDb, g_SilenceVolume, 26.f);
|
|
|
|
//Use RequestedPostSubmixVolumeAttenuation rather than RequestedVolume here, so we have the flexibility to specify a volume above 0dB
|
|
//in order to cancel-out a proportion of the attenuation specified via categories.
|
|
requestedSettings->SetVolume(0.0f);
|
|
#if __WIN32PC
|
|
requestedSettings->SetPostSubmixVolumeAttenuation(volumeDb + m_MakeUpGain);
|
|
#else
|
|
requestedSettings->SetPostSubmixVolumeAttenuation(volumeDb);
|
|
#endif
|
|
requestedSettings->SetPosition(position);
|
|
requestedSettings->SetLowPassFilterCutoff(lpfCutoff);
|
|
requestedSettings->SetHighPassFilterCutoff(hpfCutoff);
|
|
requestedSettings->SetEnvironmentalLoudnessFloat(environmentalLoudness);
|
|
}
|
|
}
|
|
|
|
void audRadioTrack::MuteEmitters(void)
|
|
{
|
|
//Mute the stereo emitter.
|
|
UpdateStereoEmitter(g_SilenceVolume, kVoiceFilterLPFMaxCutoff);
|
|
|
|
//Mute the shadowed emitters.
|
|
for(u8 emitterIndex=0; emitterIndex<g_NumRadioStationShadowedEmitters; emitterIndex++)
|
|
{
|
|
if(m_ShadowedEmitterSounds[emitterIndex])
|
|
{
|
|
//Use RequestedPostSubmixVolumeAttenuation rather than RequestedVolume here, so we have the flexibility to specify a volume above 0dB
|
|
//in order to cancel-out a proportion of the attenuation specified via categories.
|
|
m_ShadowedEmitterSounds[emitterIndex]->SetRequestedPostSubmixVolumeAttenuation(g_SilenceVolume);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void audRadioTrack::SkipForward(u32 timeToSkip)
|
|
{
|
|
if(naVerifyf(m_State == AUD_TRACK_STATE_VIRTUAL || m_State == AUD_TRACK_STATE_DORMANT, "Attempting to skip track forward but state (%d) is neither virtual (%d) or dormant (%d)", m_State, AUD_TRACK_STATE_VIRTUAL, AUD_TRACK_STATE_DORMANT))
|
|
{
|
|
//Skip forward by advancing the virtual play time.
|
|
m_PlayTime += timeToSkip;
|
|
}
|
|
}
|
|
|
|
bool audRadioTrack::IsStreamingPhysically() const
|
|
{
|
|
//Report that we are physically streaming if we have already created sounds, but not played them yet.
|
|
return ((m_State == AUD_TRACK_STATE_PHYSICAL) || ((m_State == AUD_TRACK_STATE_DORMANT) && m_StreamingSound));
|
|
}
|
|
|
|
bool audRadioTrack::IsStreamingVirtually() const
|
|
{
|
|
//Report that we are virtually streaming if we have not created any sounds and are yet to play virtually.
|
|
return ((m_State == AUD_TRACK_STATE_VIRTUAL) || ((m_State == AUD_TRACK_STATE_DORMANT) && (m_StreamingSound == NULL)));
|
|
}
|
|
|
|
bool audRadioTrack::IsPlayingPhysicallyOrVirtually() const
|
|
{
|
|
return (m_State == AUD_TRACK_STATE_VIRTUAL || m_State == AUD_TRACK_STATE_PHYSICAL);
|
|
}
|
|
|
|
const char *audRadioTrack::GetBankName() const
|
|
{
|
|
#if __WIN32PC
|
|
if(m_IsUserTrack && m_MediaReader)
|
|
{
|
|
static char trackInfo[255];
|
|
formatf(trackInfo, "%s - %s",
|
|
audRadioStation::GetUserRadioTrackManager()->GetTrackArtist(m_TrackIndex),
|
|
audRadioStation::GetUserRadioTrackManager()->GetTrackTitle(m_TrackIndex));
|
|
return trackInfo;
|
|
}
|
|
#endif
|
|
if(m_StreamingSound)
|
|
{
|
|
u32 bankIndex = ((audStreamingSound*)m_StreamingSound)->GetStreamingWaveBankIndex();
|
|
return SOUNDFACTORY.GetBankNameFromIndex(bankIndex);
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
u32 audRadioTrack::MakeTrackId(const s32 category, const u32 trackIndex, const u32 soundRef)
|
|
{
|
|
if(audRadioStation::IsNetworkModeHistoryActive())
|
|
{
|
|
// We need to encode category and trackIndex as the TrackId for network sync
|
|
return (u32(category)&0xffff) | (trackIndex << 16);
|
|
}
|
|
else
|
|
{
|
|
// in SP, using the sound ref as the history identifier allows us to check against custom track lists
|
|
return soundRef;
|
|
}
|
|
}
|
|
|
|
void audRadioTrack::UpdateBeatInfo()
|
|
{
|
|
const u32 currentPlaytimeInMs = m_PlayTime;
|
|
const f32 currentPlaytimeInSeconds = currentPlaytimeInMs*0.001f;
|
|
|
|
u32 nearestEarlierMarkerTimeMs = 0;
|
|
u32 nearestLaterMarkerTimeMs = m_IsMixStationTrack ? GetDuration() : ~0u; // B*4886712 - Always ensure that we have a valid beat marker to cover the last section of a mix track
|
|
u32 numBeats = 0;
|
|
// search for the two bar markers around the current play head
|
|
for(u32 i=0; i<m_BeatMarkers.GetCount(); i++)
|
|
{
|
|
if(m_BeatMarkers[i].playtimeMs >= nearestEarlierMarkerTimeMs && m_BeatMarkers[i].playtimeMs <= currentPlaytimeInMs)
|
|
{
|
|
nearestEarlierMarkerTimeMs = m_BeatMarkers[i].playtimeMs;
|
|
numBeats = m_BeatMarkers[i].numBeats;
|
|
}
|
|
else if(m_BeatMarkers[i].playtimeMs > currentPlaytimeInMs && m_BeatMarkers[i].playtimeMs < nearestLaterMarkerTimeMs)
|
|
{
|
|
nearestLaterMarkerTimeMs = m_BeatMarkers[i].playtimeMs;
|
|
}
|
|
}
|
|
|
|
if(nearestEarlierMarkerTimeMs != nearestLaterMarkerTimeMs && nearestLaterMarkerTimeMs <= GetDuration())
|
|
{
|
|
const f32 nearestBarTimeInSeconds = nearestEarlierMarkerTimeMs*0.001f;
|
|
const f32 timeSinceBar = currentPlaytimeInSeconds - nearestBarTimeInSeconds;
|
|
|
|
if(numBeats == 0)
|
|
{
|
|
// default to 2 bar markup
|
|
numBeats = 8;
|
|
}
|
|
|
|
// markers occur n beats
|
|
const f32 beatDuration = (nearestLaterMarkerTimeMs - nearestEarlierMarkerTimeMs) / (numBeats * 1000.f);
|
|
m_Bpm = 60.f / beatDuration;
|
|
const f32 beatsPastLastBar = timeSinceBar / beatDuration;
|
|
const f32 currentBeat = Floorf(beatsPastLastBar);
|
|
m_BeatNumber = 1 + ((u32)currentBeat) % 4;
|
|
const f32 timeSinceLastBeat = (beatsPastLastBar - currentBeat) * beatDuration;
|
|
m_BeatTimeS = beatDuration - timeSinceLastBeat;
|
|
}
|
|
}
|
|
|
|
bool audRadioTrack::GetNextBeat(float &timeS, float &bpm, s32 &beatNumber) const
|
|
{
|
|
if(HasBeatInfo())
|
|
{
|
|
if (m_IsMixStationTrack)
|
|
{
|
|
audRadioStation* radioStation = audRadioStation::FindStation(m_StationNameHash);
|
|
|
|
if (radioStation)
|
|
{
|
|
return radioStation->GetNextMixStationBeat(timeS, bpm, beatNumber);
|
|
}
|
|
}
|
|
|
|
if(m_Bpm > 220.f)
|
|
{
|
|
// Filter out very high BPMs (they typically occur due to incorrect markup on start/end of tracks)
|
|
return false;
|
|
}
|
|
|
|
timeS = m_BeatTimeS;
|
|
bpm = m_Bpm;
|
|
beatNumber = static_cast<s32>(m_BeatNumber);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool audRadioTrack::GetDjMarkers(s32 &introStartMs, s32 &introEndMs, s32 &outroStartMs, s32 &outroEndMs) const
|
|
{
|
|
if(m_DjRegions.GetCount() > 0)
|
|
{
|
|
const audDjSpeechRegion *region = FindNextRegion(audDjSpeechRegion::INTRO);
|
|
if(region)
|
|
{
|
|
introStartMs = region->startMs;
|
|
introEndMs = region->endMs;
|
|
}
|
|
else
|
|
{
|
|
introStartMs = introEndMs = -1;
|
|
}
|
|
|
|
region = FindNextRegion(audDjSpeechRegion::OUTRO);
|
|
if(region)
|
|
{
|
|
outroStartMs = region->startMs;
|
|
outroEndMs = region->endMs;
|
|
|
|
// For motomami we want the hosts to talk right up until the end of the track. Since WFH is slowing down marker edits significantly this is an expedient way to achieve that.
|
|
if(m_StationNameHash == ATSTRINGHASH("RADIO_37_MOTOMAMI", 0x97074FCC))
|
|
{
|
|
outroEndMs = GetDuration();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
outroEndMs = outroStartMs = -1;
|
|
}
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
introStartMs = -1;
|
|
introEndMs = -1;
|
|
outroStartMs = -1;
|
|
outroEndMs = -1;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const audRadioTrack::audDjSpeechRegion *audRadioTrack::FindNextRegion(const audRadioTrack::audDjSpeechRegion::Type type) const
|
|
{
|
|
const audDjSpeechRegion *region = NULL;
|
|
for(s32 i = 0; i < m_DjRegions.GetCount(); i++)
|
|
{
|
|
if(m_DjRegions[i].type == type && (s32)m_DjRegions[i].endMs > m_PlayTime)
|
|
{
|
|
region = &m_DjRegions[i];
|
|
// Stop on the first one we find that we've not yet passed
|
|
break;
|
|
}
|
|
}
|
|
|
|
return region;
|
|
}
|
|
|
|
void audRadioTrack::ParseMarkers()
|
|
{
|
|
if(audVerifyf(m_WaveSlot, "Radio track playing physically with no wave slot"))
|
|
{
|
|
// find the wavename hash
|
|
const u32 metadataSoundHash = m_SoundRef;
|
|
u32 wavehash[2] = {~0U,~0U};
|
|
|
|
const StreamingSound *streamingSound = SOUNDFACTORY.DecompressMetadata<StreamingSound>(metadataSoundHash);
|
|
if(streamingSound)
|
|
{
|
|
for(u32 waveIndex = 0; waveIndex < Min<u32>(2, streamingSound->numSoundRefs); waveIndex++)
|
|
{
|
|
const SimpleSound *simpleSound = SOUNDFACTORY.DecompressMetadata<SimpleSound>(streamingSound->SoundRef[waveIndex].SoundId);
|
|
if(simpleSound)
|
|
{
|
|
wavehash[waveIndex] = simpleSound->WaveRef.WaveName;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(wavehash[0] != ~0U || wavehash[1] != ~0U)
|
|
{
|
|
audWaveRef waveRef;
|
|
audWaveMarkerList markers;
|
|
|
|
u32 sampleRate = 0;
|
|
if(waveRef.Init(m_WaveSlot, wavehash[0]))
|
|
{
|
|
waveRef.FindMarkerData(markers);
|
|
const audWaveFormat *format = waveRef.FindFormat();
|
|
if(format)
|
|
{
|
|
sampleRate = format->SampleRate;
|
|
}
|
|
}
|
|
if(markers.GetCount() == 0)
|
|
{
|
|
if(waveRef.Init(m_WaveSlot, wavehash[1]))
|
|
{
|
|
waveRef.FindMarkerData(markers);
|
|
const audWaveFormat *format = waveRef.FindFormat();
|
|
if(format)
|
|
{
|
|
sampleRate = format->SampleRate;
|
|
}
|
|
}
|
|
}
|
|
|
|
audDjSpeechRegion currentRegion;
|
|
currentRegion.type = audDjSpeechRegion::OUTRO;
|
|
|
|
audDjSpeechRegion currentRockOutRegion;
|
|
currentRockOutRegion.type = audDjSpeechRegion::ROCKOUT;
|
|
|
|
for(u32 i=0; i<markers.GetCount(); i++)
|
|
{
|
|
if(markers[i].categoryNameHash == g_RadioTrackIdCategoryHash)
|
|
{
|
|
if(!m_FoundTrackTextIds)
|
|
{
|
|
if(!m_TextIds.IsFull())
|
|
{
|
|
audTextIdMarker &textIdMarker = m_TextIds.Append();
|
|
textIdMarker.textId = static_cast<u32>(markers[i].value);
|
|
textIdMarker.playtimeMs = audDriverUtil::ConvertSamplesToMs(markers[i].positionSamples, sampleRate);
|
|
}
|
|
else
|
|
{
|
|
naWarningf("Wave with too many track text markers: %s", m_WaveSlot->GetLoadedBankName());
|
|
}
|
|
}
|
|
}
|
|
else if(markers[i].categoryNameHash == g_RadioDjMarkerCategoryHash)
|
|
{
|
|
if(markers[i].nameHash == g_RadioDjMarkerNameIntroStartHash)
|
|
{
|
|
currentRegion.type = audDjSpeechRegion::INTRO;
|
|
currentRegion.startMs = audDriverUtil::ConvertSamplesToMs(markers[i].positionSamples, sampleRate);
|
|
}
|
|
else if(markers[i].nameHash == g_RadioDjMarkerNameIntroEndHash)
|
|
{
|
|
audAssertf(currentRegion.type == audDjSpeechRegion::INTRO, "%s - invalid intro end marker at %u ms", GetBankName(), audDriverUtil::ConvertSamplesToMs(markers[i].positionSamples, sampleRate));
|
|
|
|
currentRegion.endMs = audDriverUtil::ConvertSamplesToMs(markers[i].positionSamples, sampleRate);
|
|
m_DjRegions.Append() = currentRegion;
|
|
}
|
|
else if(markers[i].nameHash == g_RadioDjMarkerNameOutroStartHash)
|
|
{
|
|
currentRegion.type = audDjSpeechRegion::OUTRO;
|
|
currentRegion.startMs = audDriverUtil::ConvertSamplesToMs(markers[i].positionSamples, sampleRate);
|
|
}
|
|
else if(markers[i].nameHash == g_RadioDjMarkerNameOutroEndHash)
|
|
{
|
|
audAssertf(currentRegion.type == audDjSpeechRegion::OUTRO, "%s - invalid intro end marker at %u ms", GetBankName(), audDriverUtil::ConvertSamplesToMs(markers[i].positionSamples, sampleRate));
|
|
currentRegion.endMs = audDriverUtil::ConvertSamplesToMs(markers[i].positionSamples, sampleRate);
|
|
m_DjRegions.Append() = currentRegion;
|
|
}
|
|
else
|
|
{
|
|
naWarningf("Unknown wave marker name in %s", m_WaveSlot->GetLoadedBankName());
|
|
}
|
|
}
|
|
else if(markers[i].categoryNameHash == ATSTRINGHASH("ROCKOUT", 0xF31B4F6A))
|
|
{
|
|
if(markers[i].nameHash == ATSTRINGHASH("START", 0x84DC271F))
|
|
{
|
|
currentRockOutRegion.startMs = audDriverUtil::ConvertSamplesToMs(markers[i].positionSamples, sampleRate);
|
|
}
|
|
else if(markers[i].nameHash == ATSTRINGHASH("END", 0xB0C485D7))
|
|
{
|
|
currentRockOutRegion.endMs = audDriverUtil::ConvertSamplesToMs(markers[i].positionSamples, sampleRate);
|
|
m_DjRegions.Append() = currentRockOutRegion;
|
|
}
|
|
}
|
|
else if(markers[i].categoryNameHash == ATSTRINGHASH("dance", 0x44AAC91A))
|
|
{
|
|
if(!m_FoundBeatMarkers)
|
|
{
|
|
// E2 format beat markers
|
|
if(!m_BeatMarkers.IsFull())
|
|
{
|
|
audAssertf(markers[i].nameHash == ATSTRINGHASH("beat", 0xE89AE78C), "Invalid radio dance marker name hash: %u", markers[i].nameHash);
|
|
BeatMarker &bm = m_BeatMarkers.Append();
|
|
bm.numBeats = markers[i].data;
|
|
bm.playtimeMs = audDriverUtil::ConvertSamplesToMs(markers[i].positionSamples, sampleRate);
|
|
}
|
|
else
|
|
{
|
|
naWarningf("Too many beat markers for track: %s", GetBankName());
|
|
}
|
|
}
|
|
}
|
|
else if(markers[i].categoryNameHash == ATSTRINGHASH("TEMPO", 0x7a495db3))
|
|
{
|
|
// ignore interactive music format tempo markers
|
|
}
|
|
else if(markers[i].categoryNameHash == ATSTRINGHASH("beat", 0xE89AE78C))
|
|
{
|
|
if(!m_FoundBeatMarkers)
|
|
{
|
|
// E2 format beat markers
|
|
if(!m_BeatMarkers.IsFull())
|
|
{
|
|
BeatMarker &bm = m_BeatMarkers.Append();
|
|
bm.numBeats = markers[i].data;
|
|
bm.playtimeMs = audDriverUtil::ConvertSamplesToMs(markers[i].positionSamples, sampleRate);
|
|
}
|
|
else
|
|
{
|
|
naWarningf("Too many beat markers for track: %s", GetBankName());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
naWarningf("Unknown wave marker category %08X in %s", markers[i].categoryNameHash, m_WaveSlot->GetLoadedBankName());
|
|
}
|
|
}
|
|
}
|
|
m_HasParsedMarkers = true;
|
|
}
|
|
}
|
|
|
|
bool audRadioTrack::IsDormant() const
|
|
{
|
|
return m_State == AUD_TRACK_STATE_DORMANT;
|
|
}
|
|
#if __BANK
|
|
|
|
void audRadioTrack::DrawDebug(audDebugDrawManager &drawMgr) const
|
|
{
|
|
s32 introS, introE, outroS, outroE;
|
|
GetDjMarkers(introS, introE, outroS, outroE);
|
|
|
|
char playtimeString[32];
|
|
char durationString[32];
|
|
char timeLeftString[32];
|
|
|
|
audRadioStation::FormatTimeString(GetPlayTime(), playtimeString);
|
|
audRadioStation::FormatTimeString(GetDuration(), durationString);
|
|
audRadioStation::FormatTimeString(GetDuration() - GetPlayTime(), timeLeftString);
|
|
|
|
char introRegionString[32];
|
|
char outroRegionString[32];
|
|
|
|
if(introS != -1 && introE != -1)
|
|
{
|
|
char startString[32];
|
|
char endString[32];
|
|
audRadioStation::FormatTimeString(introS, startString);
|
|
audRadioStation::FormatTimeString(introE, endString);
|
|
formatf(introRegionString, "Intro [%s,%s] ", startString, endString);
|
|
}
|
|
else
|
|
{
|
|
introRegionString[0] = 0;
|
|
}
|
|
if(outroS != -1 && outroE != -1)
|
|
{
|
|
char startString[32];
|
|
char endString[32];
|
|
audRadioStation::FormatTimeString(outroS, startString);
|
|
audRadioStation::FormatTimeString(outroE, endString);
|
|
formatf(outroRegionString, "Outro [%s,%s] ", startString, endString);
|
|
}
|
|
else
|
|
{
|
|
outroRegionString[0] = 0;
|
|
}
|
|
|
|
const char *trackName = GetBankName();
|
|
if(trackName == NULL)
|
|
{
|
|
trackName = g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().GetObjectName(m_SoundRef);
|
|
}
|
|
|
|
drawMgr.DrawLinef("%s %s(%s:%u) playtime: %s/%s timeLeft: %s TextID: %u",
|
|
trackName,
|
|
m_StreamingSound == NULL ? "(NoSound) " : "",
|
|
audRadioStation::GetTrackCategoryName(GetCategory()),
|
|
GetTrackIndex(),
|
|
playtimeString,
|
|
durationString,
|
|
timeLeftString,
|
|
GetTextId());
|
|
|
|
if(m_StreamingSound)
|
|
{
|
|
drawMgr.DrawLinef("Track Volume: %.02f", audDriverUtil::ComputeLinearVolumeFromDb(m_StreamingSound->GetRequestedVolume()));
|
|
}
|
|
|
|
if(strlen(introRegionString))
|
|
drawMgr.DrawLine(introRegionString);
|
|
if(strlen(outroRegionString))
|
|
drawMgr.DrawLine(outroRegionString);
|
|
|
|
const audDjSpeechRegion *rockOutRegion = FindNextRegion(audDjSpeechRegion::ROCKOUT);
|
|
if(rockOutRegion)
|
|
{
|
|
if(GetPlayTime() >= rockOutRegion->startMs)
|
|
{
|
|
drawMgr.DrawLinef("ROCK-OUT! (%.2fS remaining)", 0.001f * (rockOutRegion->endMs - GetPlayTime()));
|
|
}
|
|
else
|
|
{
|
|
drawMgr.DrawLinef("Rock out in %.2fS", 0.001f * (rockOutRegion->startMs - GetPlayTime()));
|
|
}
|
|
}
|
|
|
|
float bpm;
|
|
s32 beatNumber;
|
|
float beatTimeS;
|
|
if(GetNextBeat(beatTimeS, bpm, beatNumber))
|
|
{
|
|
drawMgr.DrawLinef("%u beat markers. Beat: %.2f (%u/4 %.1f BPM)", m_BeatMarkers.GetCount(), beatTimeS, beatNumber, bpm);
|
|
}
|
|
|
|
#if __WIN32PC
|
|
if(m_IsUserTrack && audRadioStation::GetUserRadioTrackManager()->GetRadioMode() == USERRADIO_PLAY_MODE_RADIO)
|
|
{
|
|
char startString[32];
|
|
char endString[32];
|
|
audRadioStation::FormatTimeString(audRadioStation::GetUserRadioTrackManager()->GetStartOffsetMs(m_TrackIndex), startString);
|
|
audRadioStation::FormatTimeString(m_PostRoll, endString);
|
|
drawMgr.DrawLinef("Make-up gain: %f, start trim: %s end trim: %s]", m_MakeUpGain, startString, endString);
|
|
}
|
|
#endif // __WIN32PC
|
|
}
|
|
|
|
#endif // __BANK
|
|
#endif // NA_RADIO_ENABLED
|