Skip to main content

04 サウンドのコントロール機能(簡単なのだけ)

第4回 音量管理とBGMフェード

目標

この回では、オーディオエンジンに音量管理を追加します。

次の3種類の音量を扱います。

MasterVolume : 全体の音量
SEVolume     : 効果音の音量
BGMVolume    : BGMの音量

さらに、BGMのフェードアウトも追加します。 シーン切り替え時に、音を急に止めず、自然に小さくできます。


今回変更するファイル

Engine/Audio.h
Engine/Audio.cpp
TestScene.cpp

今回のフェード処理について

この資料では、分かりやすさを優先して BGMVolume そのものを少しずつ変化させます。

本格的なエンジンでは、次のように分けて管理する方法もあります。

BGMの基本音量
フェード用の音量倍率

今回は、まず動くものを作ることを優先します。


1. Engine/Audio.h に音量とフェードの関数を追加する

Engine/Audio.h を次のように変更します。

#pragma once
#include <string>
#include <filesystem>

using file_path = std::filesystem::path;

namespace Audio
{
    bool Initialize();
    void Update();
    void Release();

    bool LoadSE(const std::string& name, file_path& filepath);
    void PlaySE(const std::string& name);

    bool LoadBGM(const std::string& name, file_path& filepath);
    void PlayBGM(const std::string& name, bool loop = true);
    void StopBGM();

    void SetMasterVolume(float volume);
    void SetSEVolume(float volume);
    void SetBGMVolume(float volume);

    void FadeOutBGM(float fadeTime);
}

2. Engine/Audio.cpp に音量用の変数を追加する

Engine/Audio.cppnamespace 内に、音量用の変数を追加します。

float masterVolume_ = 1.0f;
float seVolume_ = 1.0f;
float bgmVolume_ = 1.0f;

さらに、フェード用の変数も追加します。

bool isBGMFadeOut_ = false;
float bgmFadeTimer_ = 0.0f;
float bgmFadeTime_ = 0.0f;
float bgmFadeStartVolume_ = 1.0f;

namespace 内は、最終的に次のような形になります。

namespace
{
    std::unique_ptr<DirectX::AudioEngine> audioEngine_;

    std::unordered_map<std::string, std::unique_ptr<DirectX::SoundEffect>> seSounds_;
    std::unordered_map<std::string, std::unique_ptr<DirectX::SoundEffect>> bgmSounds_;

    std::unique_ptr<DirectX::SoundEffectInstance> currentBGM_;
    std::string currentBGMName_;

    float masterVolume_ = 1.0f;
    float seVolume_ = 1.0f;
    float bgmVolume_ = 1.0f;

    bool isBGMFadeOut_ = false;
    float bgmFadeTimer_ = 0.0f;
    float bgmFadeTime_ = 0.0f;
    float bgmFadeStartVolume_ = 1.0f;
}

3. 音量を0.0f〜1.0fに収める関数を作る

Audio.cppnamespace 内に、次の補助関数を追加します。

float ClampVolume(float volume)
{
    if (volume < 0.0f)
    {
        return 0.0f;
    }
    if (volume > 1.0f)
    {
        return 1.0f;
    }
    return volume;
}

float GetBGMFinalVolume()
{
    return masterVolume_ * bgmVolume_;
}

float GetSEFinalVolume()
{
    return masterVolume_ * seVolume_;
}

音量は、基本的に 0.0f から 1.0f の範囲で扱います。

0.0f : 無音
0.5f : 半分くらい
1.0f : 標準音量

4. PlaySE にSE音量を反映する

PlaySE を次のように変更します。

void Audio::PlaySE(const std::string& name)
{
    auto it = seSounds_.find(name);
    if (it == seSounds_.end())
    {
        return;
    }

    it->second->Play(GetSEFinalVolume());
}

これで、効果音は MasterVolume × SEVolume の音量で鳴ります。


5. PlayBGM にBGM音量を反映する

PlayBGM の最後に、音量設定を追加します。

void Audio::PlayBGM(const std::string& name, bool loop)
{
    auto it = bgmSounds_.find(name);
    if (it == bgmSounds_.end())
    {
        return;
    }

    if (currentBGM_ != nullptr)
    {
        currentBGM_->Stop(true);
        currentBGM_.reset();
    }

    currentBGM_ = it->second->CreateInstance();
    currentBGM_->SetVolume(GetBGMFinalVolume());
    currentBGM_->Play(loop);
    currentBGMName_ = name;

    isBGMFadeOut_ = false;
}

6. 音量設定関数を追加する

Audio.cpp に次の関数を追加します。

void Audio::SetMasterVolume(float volume)
{
    masterVolume_ = ClampVolume(volume);

    if (currentBGM_ != nullptr)
    {
        currentBGM_->SetVolume(GetBGMFinalVolume());
    }
}

void Audio::SetSEVolume(float volume)
{
    seVolume_ = ClampVolume(volume);
}

void Audio::SetBGMVolume(float volume)
{
    bgmVolume_ = ClampVolume(volume);

    if (currentBGM_ != nullptr)
    {
        currentBGM_->SetVolume(GetBGMFinalVolume());
    }
}

再生中のBGMは、音量を変更した瞬間に反映します。 効果音は、次に鳴らす音から新しい音量になります。


7. フェードアウト開始関数を追加する

Audio.cpp に次の関数を追加します。

void Audio::FadeOutBGM(float fadeTime)
{
    if (currentBGM_ == nullptr)
    {
        return;
    }

    if (fadeTime <= 0.0f)
    {
        StopBGM();
        return;
    }

    isBGMFadeOut_ = true;
    bgmFadeTimer_ = 0.0f;
    bgmFadeTime_ = fadeTime;
    bgmFadeStartVolume_ = bgmVolume_;
}

fadeTime は、何秒かけて音を小さくするかです。

Audio::FadeOutBGM(1.0f); // 1秒で小さくする
Audio::FadeOutBGM(2.0f); // 2秒で小さくする

8. Updateでフェード処理を進める

Audio::Update() を次のように変更します。

void Audio::Update()
{
    if (audioEngine_ != nullptr)
    {
        audioEngine_->Update();
    }

    if (isBGMFadeOut_ && currentBGM_ != nullptr)
    {
        const float DELTA_TIME = 1.0f / 60.0f;

        bgmFadeTimer_ += DELTA_TIME;
        float rate = bgmFadeTimer_ / bgmFadeTime_;

        if (rate >= 1.0f)
        {
            SetBGMVolume(0.0f);
            StopBGM();
            bgmVolume_ = bgmFadeStartVolume_;
            isBGMFadeOut_ = false;
            return;
        }

        float volume = bgmFadeStartVolume_ * (1.0f - rate);
        SetBGMVolume(volume);
    }
}

このエンジンはメインループを約60FPSで回しているため、ここでは 1.0f / 60.0f を使います。 将来的に正確な deltaTime を作った場合は、その値を使う形に変更できます。


9. TestScene.cpp で確認する

TestScene.cppUpdate() を次のように変更します。

void TestScene::Update()
{
    if (Input::IsKeyDown(DIK_SPACE))
    {
        Audio::PlaySE("shot");
    }

    if (Input::IsKeyDown(DIK_D))
    {
        Audio::PlaySE("damage");
    }

    if (Input::IsKeyDown(DIK_1))
    {
        Audio::SetMasterVolume(1.0f);
    }

    if (Input::IsKeyDown(DIK_2))
    {
        Audio::SetMasterVolume(0.5f);
    }

    if (Input::IsKeyDown(DIK_3))
    {
        Audio::SetMasterVolume(0.0f);
    }

    if (Input::IsKeyDown(DIK_F))
    {
        Audio::FadeOutBGM(2.0f);
    }
}

確認すること:

1キーで音量が通常になる
2キーで音量が小さくなる
3キーで無音になる
FキーでBGMが2秒かけて小さくなる

今回の完成状態

この回で、Audio機能は次の形になりました。

Audio::SetMasterVolume(0.8f);
Audio::SetSEVolume(0.7f);
Audio::SetBGMVolume(0.5f);
Audio::FadeOutBGM(2.0f);

次回は、ゲームオブジェクトの位置と音を結びつけるために、AudioSourceと3D Audioを追加します。