Implémenter de la musique de manière plus efficace
Lorsque vous implémentez de la musique dans un jeu à l'aide de Wwise, il est nécessaire d'écouter le morceau de musique en entier pour confirmer que la musique joue en boucle de façon naturelle. Par exemple, si vous avez un segment de musique de deux minutes, vous devrez l'écouter pendant les deux minutes complètes pour vous assurer que la lecture en boucle se fait de manière transparente.
Il en va de même pour la vérification d'un changement de segment et d'une transition dans le système de musique interactive. En particulier pour les transitions se produisant vers la fin d'une longue piste musicale, une seule pré-écoute peut suffire s'il n'y a pas de problème. Toutefois, en présence d'une erreur, il sera nécessaire de réécouter le segment dans toute sa longueur après avoir amené des corrections sur les paramètres et les assets.
Ce type de vérification répétée peut prendre du temps, être source de fatigue et compromettre la qualité du contenu si votre temps est compté.
Pour résoudre ce problème, PlatinumGames a développé « Music Render », un outil unique qui aide à vérifier les boucles audio et les transitions plus efficacement en utilisant les API de rendu hors ligne de Wwise. Cet article présente des exemples d'utilisation de l'outil ainsi que sa configuration technique.
Music Render : Exemples d'utilisation
Voici quelques exemples pratiques de la façon dont Music Render peut résoudre efficacement des problèmes courants d'implémentation de la musique avec Wwise, en utilisant l'échantillon de projet « Cube » de Wwise comme exemple.
Exemple 1 : Boucle simple
Le Playlist Container « Story » consiste en une boucle simple utilisant un seul Music Segment, d'une durée approximative d'une minute, ce qui signifie que vous devez consacrer une minute à vérifier la bonne lecture de la boucle.
C'est là que Music Render entre en jeu. Activez l'outil à partir du menu Playlist de Wwise et réglez les paramètres suivants.
- Temps de rendu : la durée nécessaire pour vérifier la boucle (de 1 à 10 minutes).
- Application pour ouvrir le fichier WAV : par exemple, SOUND FORGE de MAGIX.
Cliquez sur [Start Rendering] pour créer une SoundBank et effectuer le rendu automatiquement ; une fois le processus terminé, un fichier WAV s'ouvre dans SOUND FORGE. Vous pouvez rapidement lire un Music Segment à partir du point de boucle pour vérifier la bonne lecture de la boucle.
Exemple 2 : Playlist complexe
Le Playlist Container « Explore » contient plusieurs Music Segments. Sans notre outil, vous devez inévitablement passer beaucoup de temps à lire l'ensemble des Music Segments pour vérifier s'il y a des problèmes dans les transitions.
Pendant le processus de rendu, Music Render ajoute automatiquement des marqueurs aux points de transition des Segments dans le fichier généré. Cela vous permet de vérifier les transitions sans avoir à écouter l'intégralité de la piste musicale ; vous pouvez simplement reprendre la lecture à partir de ces marqueurs.
Exemple 3 : Transition interactive
Le Music Switch Container « Wwise 201 » joue en alternance les pistes musicales « Combat » et « Explore », selon ce qui est déclenché par le Switch « Gameplay_Switch ».
Vérifier les transitions requiert habituellement de changer manuellement la piste musicale, mais Music Render offre plus d'efficacité en aidant à créer et à rendre un Event où cette commutation est automatisée.
Voici un exemple de configuration d'Event :
- Lire un Switch Container
- Changer la valeur du Switch toutes les 15 secondes
Le rendu hors ligne de l'Event avec Music Render crée un fichier audio où la piste musicale change à des intervalles de 15 secondes (15, 30, 45, ...). Si vous trouvez des transitions peu naturelles, vous pouvez modifier les transitions ou les assets, puis effectuer un nouveau rendu pour valider la correction. Cette automatisation garantit que même les problèmes difficiles à reproduire manuellement peuvent être vérifiés et corrigés de manière fiable.
Configuration technique
Music Render se compose d'un frontend et d'un backend.
Frontend (interface utilisateur)
- Langage de programmation : C#
- Cadre de l'interface utilisateur : WPF
- Activation : activé comme un add-on (.exe) à l'outil de création Wwise.
- Caractéristiques :
- Communication avec l'outil de création Wwise via WAAPI
- Création d'Events et de SoundBanks
- Envoi d'une commande de rendu au backend et, une fois le rendu terminé, ouverture du fichier WAV
Backend (processus de rendu)
- Langage de programmation : C++
- Activation : activé à partir du frontend (.dll).
- Caractéristiques :
- Initialisation du moteur sonore avec Wwise SDK
- Chargement des SoundBanks, lecture d'Events et génération audio via l'outil de rendu hors ligne.
- Envoi du résultat au frontend après avoir complété le processus
Détails du backend
Pour les processus de base tels que l'initialisation du moteur sonore de Wwise et le chargement d'une SoundBank, référez-vous aux documents officiels. Cette section résume les points à noter spécifiques au rendu hors ligne.
Variables globales requises :
AK::OfflineRendering::g_fFrameTimeInSeconds = 1.0f / 60.0f;
AK::OfflineRendering::g_bOfflineRenderingEnabled = true;
Ces variables sont définies dans AkProfile.cpp. Bien que vous ayez besoin d'une licence Wwise appropriée pour accéder au code source, cela ne devrait pas être nécessaire pour créer un outil de rendu hors ligne pour vous-même.
API de rendu hors ligne :
AK::SoundEngine::StartOutputCapture(settings.WavFileName);
AK::SoundEngine::StopOutputCapture();
Callback pour ajouter des marqueurs :
static void MusicCallback(AkCallbackType in_eType, AkCallbackInfo* in_pCallbackInfo)
{
AK::SoundEngine::AddOutputCaptureMarker("Entry");
}
Code complet :
Voici la majeure partie du code, incluant ce qui précède. Il comprend une classe MusicRender ainsi que les définitions des structures utilisées dans la classe. Il inclut également tous les points techniques nécessaires au rendu hors ligne, à l'exclusion du traitement des erreurs.
// MusicRender.h
struct MusicRenderSettings{
wchar_t* SoundBankFolderName;
wchar_t* StreamFolderName;
wchar_t* SoundBankFileName;
wchar_t* PluginDllFolderName;
wchar_t* Event;
int RenderingDurationSec;
wchar_t* WavFileName;
};
typedef void(__stdcall* NotifyProgress)(float);
extern "C" __declspec(dllexport) void __stdcall MusicRender(const MusicRenderSettings&, const NotifyProgress);
// MusicRender.cpp
namespace AK {
namespace OfflineRendering
{
extern AkReal32 g_fFrameTimeInSeconds;
extern bool g_bOfflineRenderingEnabled;
}
}
CAkFilePackageLowLevelIODeferred g_lowLevelIO;
class CMusicRender
{
public:
const AkGameObjectID LISTENER_ID = 0;
const AkGameObjectID GAME_OBJECT_MUSIC = 1;
AkPlayingID m_iPlayingID = 0;
void Initialize(const MusicRenderSettings& settings)
{
AK::SoundEngine::RegisterGameObj(LISTENER_ID, "Listener (Default)");
AK::SoundEngine::SetDefaultListeners(&LISTENER_ID, 1);
g_lowLevelIO.SetBasePath(settings.SoundBankFolderName);
AK::StreamMgr::SetCurrentLanguage(AKTEXT("English(US)"));
AkBankID bankID;
AK::SoundEngine::LoadBank("Init.bnk", bankID);
AK::SoundEngine::LoadBank(settings.SoundBankFileName, bankID);
AK::SoundEngine::RegisterGameObj(GAME_OBJECT_MUSIC, "Music");
g_lowLevelIO.SetBasePath(settings.StreamFolderName);
}
void Terminate(const MusicRenderSettings& settings)
{
AK::SoundEngine::StopPlayingID(m_iPlayingID);
AK::SoundEngine::UnregisterGameObj(GAME_OBJECT_MUSIC);
AK::SoundEngine::UnloadBank(settings.SoundBankFileName, NULL);
AK::SoundEngine::UnloadBank("Init.bnk", NULL);
}
void Main(const MusicRenderSettings& settings, const NotifyProgress callback)
{
AK::OfflineRendering::g_fFrameTimeInSeconds = 1.0f / 60.0f;
AK::OfflineRendering::g_bOfflineRenderingEnabled = true;
AK::SoundEngine::StartOutputCapture(settings.WavFileName);
{
m_iPlayingID = AK::SoundEngine::PostEvent(settings.Event, GAME_OBJECT_MUSIC, AK_MusicSyncEntry, MusicCallback);
const int callbackIntervalMS = 10;
DWORD lastNotificationTimeMS = GetTickCount64();
float renderTimeSec = settings.RenderingDurationSec;
float audioBufferSec = AK::OfflineRendering::g_fFrameTimeInSeconds;
for (float timeSec = 0; timeSec < renderTimeSec; timeSec += audioBufferSec) {
AK::SoundEngine::RenderAudio();
if (GetTickCount64() - lastNotificationTimeMS >= callbackIntervalMS) {
callback(timeSec / renderTimeSec);
lastNotificationTimeMS = GetTickCount64();
}
}
}
AK::OfflineRendering::g_bOfflineRenderingEnabled = false;
AK::SoundEngine::StopOutputCapture();
}
static void MusicCallback(AkCallbackType in_eType, AkCallbackInfo* in_pCallbackInfo)
{
AK::SoundEngine::AddOutputCaptureMarker("Entry");
}
};
void __stdcall MusicRender(const MusicRenderSettings& settings, const NotifyProgress callback)
{
if (InitSoundEngine(settings) == false) {
return;
}
CMusicRender cMusicRender;
cMusicRender.Initialize(settings);
cMusicRender.Main(settings, callback);
cMusicRender.Terminate(settings);
TermSoundEngine();
}
Conclusion
Nous avons utilisé cet outil sur plusieurs titres de PlatinumGames depuis environ 2019. Il nous aide à vérifier efficacement les boucles musicales et les transitions interactives, et sert de base pour créer une production sonore plus sophistiquée tout en économisant du temps de travail. Avec cet outil, vous pouvez valider votre travail avec facilité et passer plus de temps à vous concentrer sur le perfectionnement et l'expérimentation de la manière dont la musique peut être exprimée dans votre jeu.
En outre, si l'on vous demande de fournir des éléments sonores pendant la phase de développement, vous pouvez facilement combiner des fichiers WAV divisés en segments et les convertir en un seul fichier WAV qui peut être utilisé comme matériel de boucle. Comme les éléments sont rendus au volume du jeu, lorsqu'ils sont combinés à une vidéo de gameplay qui ne contient que des effets sonores, leurs volumes peuvent être naturellement équilibrés. Pour ce cas d'utilisation, l'outil fournit également une fonction permettant de rendre simultanément plusieurs Playlist Containers et Events.
J'espère que cet article vous aidera à implémenter votre musique de manière plus efficace !
Commentaires