Fix phoneme playback and lower background audio during animations

PhonemePlayer resolved ROIs by name, but multiplayer participants have
unique ROI names (e.g. pepper_mp_8) that don't match the animation actor
name in the SI phoneme track (pepper). Pass actor-name-to-ROI aliases
from SetupROIs through to PhonemePlayer::Init so it can resolve
participant ROIs correctly.

Also lower/raise background audio volume for the duration of scene
animations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Christian Semmler 2026-03-27 15:30:23 -07:00
parent 7566d5c468
commit 343610ece5
4 changed files with 40 additions and 7 deletions

View File

@ -22,7 +22,12 @@ struct PhonemeState {
class PhonemePlayer { class PhonemePlayer {
public: public:
void Init(const std::vector<SceneAnimData::PhonemeTrack>& p_tracks, LegoROI** p_roiMap, MxU32 p_roiMapSize); void Init(
const std::vector<SceneAnimData::PhonemeTrack>& p_tracks,
LegoROI** p_roiMap,
MxU32 p_roiMapSize,
const std::vector<std::pair<std::string, LegoROI*>>& p_actorAliases
);
void Tick(float p_elapsedMs, const std::vector<SceneAnimData::PhonemeTrack>& p_tracks); void Tick(float p_elapsedMs, const std::vector<SceneAnimData::PhonemeTrack>& p_tracks);
void Cleanup(); void Cleanup();

View File

@ -85,6 +85,9 @@ class ScenePlayer {
LegoROI** m_roiMap; LegoROI** m_roiMap;
MxU32 m_roiMapSize; MxU32 m_roiMapSize;
// Actor name → ROI aliases (participant ROIs whose names differ from animation actor names)
std::vector<std::pair<std::string, LegoROI*>> m_actorAliases;
// Props created for the animation (cloned characters and prop models) // Props created for the animation (cloned characters and prop models)
std::vector<LegoROI*> m_propROIs; std::vector<LegoROI*> m_propROIs;

View File

@ -12,13 +12,26 @@
using namespace Multiplayer::Animation; using namespace Multiplayer::Animation;
// Find the ROI matching a phoneme track's roiName in the roiMap. // Find the ROI matching a phoneme track's roiName.
static LegoROI* FindTrackROI(const std::string& p_roiName, LegoROI** p_roiMap, MxU32 p_roiMapSize) // Check actor aliases first (participant ROIs whose names differ from animation actor names),
// then fall back to a direct name search in the roiMap.
static LegoROI* FindTrackROI(
const std::string& p_roiName,
LegoROI** p_roiMap,
MxU32 p_roiMapSize,
const std::vector<std::pair<std::string, LegoROI*>>& p_actorAliases
)
{ {
if (p_roiName.empty() || !p_roiMap) { if (p_roiName.empty() || !p_roiMap) {
return nullptr; return nullptr;
} }
for (const auto& alias : p_actorAliases) {
if (!SDL_strcasecmp(p_roiName.c_str(), alias.first.c_str())) {
return alias.second;
}
}
for (MxU32 i = 1; i < p_roiMapSize; i++) { for (MxU32 i = 1; i < p_roiMapSize; i++) {
if (p_roiMap[i] && p_roiMap[i]->GetName() && !SDL_strcasecmp(p_roiName.c_str(), p_roiMap[i]->GetName())) { if (p_roiMap[i] && p_roiMap[i]->GetName() && !SDL_strcasecmp(p_roiName.c_str(), p_roiMap[i]->GetName())) {
return p_roiMap[i]; return p_roiMap[i];
@ -27,7 +40,12 @@ static LegoROI* FindTrackROI(const std::string& p_roiName, LegoROI** p_roiMap, M
return nullptr; return nullptr;
} }
void PhonemePlayer::Init(const std::vector<SceneAnimData::PhonemeTrack>& p_tracks, LegoROI** p_roiMap, MxU32 p_roiMapSize) void PhonemePlayer::Init(
const std::vector<SceneAnimData::PhonemeTrack>& p_tracks,
LegoROI** p_roiMap,
MxU32 p_roiMapSize,
const std::vector<std::pair<std::string, LegoROI*>>& p_actorAliases
)
{ {
for (auto& track : p_tracks) { for (auto& track : p_tracks) {
PhonemeState state; PhonemeState state;
@ -37,8 +55,8 @@ void PhonemePlayer::Init(const std::vector<SceneAnimData::PhonemeTrack>& p_track
state.bitmap = nullptr; state.bitmap = nullptr;
state.currentFrame = -1; state.currentFrame = -1;
// Resolve the target ROI from the track's roiName via the roiMap // Resolve the target ROI from the track's roiName via aliases or roiMap
LegoROI* targetROI = FindTrackROI(track.roiName, p_roiMap, p_roiMapSize); LegoROI* targetROI = FindTrackROI(track.roiName, p_roiMap, p_roiMapSize, p_actorAliases);
if (!targetROI) { if (!targetROI) {
m_states.push_back(state); m_states.push_back(state);
continue; continue;

View File

@ -11,6 +11,7 @@
#include "legoworld.h" #include "legoworld.h"
#include "misc.h" #include "misc.h"
#include "misc/legotree.h" #include "misc/legotree.h"
#include "mxbackgroundaudiomanager.h"
#include "mxgeometry/mxgeometry3d.h" #include "mxgeometry/mxgeometry3d.h"
#include "realtime/realtime.h" #include "realtime/realtime.h"
#include "roi/legoroi.h" #include "roi/legoroi.h"
@ -85,6 +86,7 @@ void ScenePlayer::SetupROIs(const AnimInfo* p_animInfo)
auto addAlias = [&](const std::string& p_name, LegoROI* p_roi) { auto addAlias = [&](const std::string& p_name, LegoROI* p_roi) {
aliasNames.push_back(p_name); aliasNames.push_back(p_name);
aliases.push_back({aliasNames.back().c_str(), p_roi}); aliases.push_back({aliasNames.back().c_str(), p_roi});
m_actorAliases.push_back({p_name, p_roi});
}; };
auto createProp = [&](const std::string& p_name, const char* p_lodName) -> LegoROI* { auto createProp = [&](const std::string& p_name, const char* p_lodName) -> LegoROI* {
@ -292,7 +294,7 @@ void ScenePlayer::Play(
} }
ResolvePtAtCamROIs(); ResolvePtAtCamROIs();
m_phonemePlayer.Init(data->phonemeTracks, m_roiMap, m_roiMapSize); m_phonemePlayer.Init(data->phonemeTracks, m_roiMap, m_roiMapSize, m_actorAliases);
m_audioPlayer.Init(data->audioTracks); m_audioPlayer.Init(data->audioTracks);
// Observers don't get camera control — they watch the animation from their own viewpoint // Observers don't get camera control — they watch the animation from their own viewpoint
@ -310,6 +312,8 @@ void ScenePlayer::Play(
m_startTime = 0; m_startTime = 0;
m_playing = true; m_playing = true;
BackgroundAudioManager()->LowerVolume();
} }
void ScenePlayer::ComputeRebaseMatrix() void ScenePlayer::ComputeRebaseMatrix()
@ -547,7 +551,10 @@ void ScenePlayer::Stop()
} }
m_participants.clear(); m_participants.clear();
BackgroundAudioManager()->RaiseVolume();
m_ptAtCamROIs.clear(); m_ptAtCamROIs.clear();
m_actorAliases.clear();
m_playing = false; m_playing = false;
m_rebaseComputed = false; m_rebaseComputed = false;
m_currentData = nullptr; m_currentData = nullptr;