Interactive Multi-State Engine
Key Features:
- Done with FMOD API, diagram shows class dependency and interactive class state structure
- State containers and segments can be set by order, loop, and randomness
- Interactive music class uses an open nested loop structure with no buffering
- Uses two interchangeable sub-music channels that share states via crossfade
- Cross-fades occur by means of embedded thread objects
- SFML library is being used for press key options to send commands
Improvement Areas:
- A thread work might be used for “SegmentIndexToPlayGenerator” if the system is very busy
- General refactoring
Download Link:
- MultiStateInteractiveTest.zip (zipped)
- Composed 8 music segments (vertical approach) for 2 different states and 1 music track for background ambience



AudioEngine.h
// AudioEngine.h
#pragma once
#include <iostream>
#include <fmod.hpp>
#include <fmod_errors.h>
#include <string>
#include <Windows.h>
#include <unordered_map>
#include <thread>
#include <chrono>
class AudioEngine
{
public:
AudioEngine();
AudioEngine(const AudioEngine&) = delete;
~AudioEngine();
AudioEngine &operator=(const AudioEngine&) = delete;
void SetMasterChannelVolume(const float _volume);
void SetMusicChannelVolume(const float _volume);
void Load(const int _fileID, const std::string & _path, const std::string & _name);
void LoadFromResource(const int _resourceID, LPCTSTR _resourceType, const std::string & _name);
void ShrinkAudioEngineMaps();
int MusicChannelActivity();
void AssignChannel();
FMOD::Channel* GetActiveChannel();
void CrossFade(FMOD::Channel* _channelIn, FMOD::Channel* _channelOut, const int _millisec);
void PlayMusic(const int _fileID);
void Stop();
void Update();
private:
//Declaring system object
FMOD::System* ptrSystem;
//Declaring master and music group channels
FMOD::ChannelGroup* ptrMasterGroupChannel;
FMOD::ChannelGroup* ptrMusicGroupChannel;
//Declaring music sub channels to be used continuously
FMOD::Channel* ptrMusicChannel1;
FMOD::Channel* ptrMusicChannel2;
//Pointer to channels for assigning them
FMOD::Channel** pptrChannel;
//For checking if interactive sub channel is playing or not
bool m_ch1_Activity;
bool m_ch2_Activity;
//Declaring map for audio files
std::unordered_map<int, FMOD::Sound*> MusicMap;
//Creating ID-name match map for file information
std::unordered_map<int, std::string> NameIDMap;
};
AudioEngine.cpp
// AudioEngine.cpp
#pragma once
#include "AudioEngine.h"
AudioEngine::AudioEngine() :
ptrSystem(nullptr),
ptrMasterGroupChannel(nullptr),
ptrMusicGroupChannel(nullptr),
ptrMusicChannel1(nullptr),
ptrMusicChannel2(nullptr),
pptrChannel(nullptr),
m_ch1_Activity(false),
m_ch2_Activity(false)
{
//Initialising the system
FMOD::System_Create(&ptrSystem);
ptrSystem->init(100, FMOD_INIT_NORMAL, nullptr);
//Creating group channels
ptrSystem->createChannelGroup("Master", &ptrMasterGroupChannel);
ptrSystem->createChannelGroup("Music", &ptrMusicGroupChannel);
//Assigning music group channel to the master group channel
ptrMasterGroupChannel->addGroup(ptrMusicGroupChannel); //Music->Master
}
////////////////////////////////////////////////////////////////////////
AudioEngine::~AudioEngine()
{
//Releasing sounds and clear the containers
for (auto & sound_file : MusicMap)
{
sound_file.second->release();
}
MusicMap.clear();
//Releasing the root of the system
ptrSystem->release();
}
////////////////////////////////////////////////////////////////////////
void AudioEngine::SetMasterChannelVolume
(
const float _volume
)
{
ptrMasterGroupChannel->setVolume(_volume);
}
////////////////////////////////////////////////////////////////////////
void AudioEngine::SetMusicChannelVolume
(
const float _volume
)
{
ptrMusicGroupChannel->setVolume(_volume);
}
////////////////////////////////////////////////////////////////////////
void AudioEngine::Load
(
const int _fileID,
const std::string & _path,
const std::string & _name
)
{
FMOD::Sound* ptrSound;
ptrSystem->createSound(_path.c_str(), FMOD_DEFAULT, nullptr, &ptrSound);
//Or ptrSystem->createStream(path.c_str(), FMOD_DEFAULT, nullptr, &ptrSound);
//It depends on the situation, streaming is better for big size audio files
//Checking if the file does exist or not against any typing error
unsigned int length = 0;
ptrSound->getLength(&length, FMOD_TIMEUNIT_MS);
if (length == 0)
{
std::cout << "Serious problem occured at loading stage..." << "\n";
std::cout << "Please check this sound file, it does not exist:" << "\n";
std::cout << _path << "\n";
std::cout << "" << "\n";
ptrSound->release();
throw;
}
//Adding audio objects and setting their names
MusicMap.emplace(_fileID, ptrSound);
NameIDMap.emplace(_fileID, _name);
}
////////////////////////////////////////////////////////////////////////
void AudioEngine::LoadFromResource
(
const int _resourceID,
LPCTSTR _resourceType,
const std::string & _name
)
{
HRSRC rsrc = FindResource(NULL, MAKEINTRESOURCE(_resourceID), _resourceType);
HGLOBAL handle = LoadResource(NULL, rsrc);
DWORD audio_size = SizeofResource(NULL, rsrc);
LPVOID audio_data = LockResource(handle);
FMOD_CREATESOUNDEXINFO AudioInfo;
memset(&AudioInfo, 0, sizeof(FMOD_CREATESOUNDEXINFO));
AudioInfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
AudioInfo.length = static_cast<unsigned int>(audio_size);
FMOD::Sound* ptrResource;
ptrSystem->createSound(static_cast<const char*>(audio_data),
FMOD_OPENMEMORY, &AudioInfo, &ptrResource);
//Adding loaded resource file and setting their names
MusicMap.emplace(_resourceID, ptrResource);
NameIDMap.emplace(_resourceID, _name);
}
////////////////////////////////////////////////////////////////////////
void AudioEngine::ShrinkAudioEngineMaps
(
)
{
//For potentially less memory consumption
std::unordered_map<int, FMOD::Sound*>(MusicMap).swap(MusicMap);
std::unordered_map<int, std::string>(NameIDMap).swap(NameIDMap);
}
////////////////////////////////////////////////////////////////////////
int AudioEngine::MusicChannelActivity
(
)
{
ptrMusicChannel1->isPlaying(&m_ch1_Activity);
ptrMusicChannel2->isPlaying(&m_ch2_Activity);
//Both channels are active -> return 1
//None of them is active -> return 2
//Only one channel is active -> return 3
if (m_ch1_Activity && m_ch2_Activity)
{
return 1;
}
else if (!m_ch1_Activity && !m_ch2_Activity)
{
return 2;
}
else
{
return 3;
}
}
////////////////////////////////////////////////////////////////////////
void AudioEngine::AssignChannel
(
)
{
ptrMusicChannel1->isPlaying(&m_ch1_Activity);
ptrMusicChannel2->isPlaying(&m_ch2_Activity);
if ((!m_ch1_Activity && !m_ch2_Activity) || (!m_ch1_Activity && m_ch2_Activity))
{
//If channels are empty or the second one is active, take the first one
pptrChannel = &ptrMusicChannel1;
}
else if (m_ch1_Activity && !m_ch2_Activity)
{
//If the first one is active, take the second one
pptrChannel = &ptrMusicChannel2;
}
}
////////////////////////////////////////////////////////////////////////
FMOD::Channel* AudioEngine::GetActiveChannel()
{
return *pptrChannel;
}
////////////////////////////////////////////////////////////////////////
void AudioEngine::CrossFade
(
FMOD::Channel* _channelIn,
FMOD::Channel* _channelOut,
const int _millisec
)
{
//channel_in is the pasive channel which will play soon, it will fade in
//channel_out is the active channel which is currently playing, it will fade away
float channelVolumeIn = 0.0f;
float channelVolumeOut = 1.0f;
_channelIn->setVolume(0.0f);
_channelOut->setVolume(1.0f);
while (channelVolumeIn < 1.0f && channelVolumeOut > 0.0f)
{
//1-(x^2.5) crossfade calculation with 0.15 base value, pow(0.15f, 2.5f)
//Increment-decrement value is approximately 0.0087 per decided millisecs
channelVolumeIn += 0.0087f;
channelVolumeOut -= 0.0087f;
_channelIn->setVolume(channelVolumeIn);
_channelOut->setVolume(channelVolumeOut);
std::this_thread::sleep_for(std::chrono::milliseconds(_millisec));
if (channelVolumeOut < 0.01f && channelVolumeIn > 0.99f)
{
_channelOut->setVolume(0.0f); //For precision
_channelIn->setVolume(1.0f);
_channelOut->stop();
break;
}
_channelIn->getVolume(&channelVolumeIn);
_channelOut->getVolume(&channelVolumeOut);
}
}
////////////////////////////////////////////////////////////////////////
void AudioEngine::PlayMusic
(
const int _fileID
)
{
auto musicFile = MusicMap.find(_fileID);
ptrSystem->playSound(musicFile->second, ptrMusicGroupChannel, false, pptrChannel);
//Progressing the cross-fade if there is a new play call
if (MusicChannelActivity() == 1)
{
if (pptrChannel == &ptrMusicChannel1)
{
std::thread crossState(&AudioEngine::CrossFade, this,
ptrMusicChannel1, ptrMusicChannel2, 15);
crossState.detach();
}
else if (pptrChannel == &ptrMusicChannel2)
{
std::thread crossState(&AudioEngine::CrossFade, this,
ptrMusicChannel2, ptrMusicChannel1, 15);
crossState.detach();
}
}
//Segment information
std::cout << NameIDMap.find(_fileID)->second << "\n";
}
////////////////////////////////////////////////////////////////////////
void AudioEngine::Stop
(
)
{
float channelVolume;
ptrMusicGroupChannel->getVolume(&channelVolume);
if (channelVolume < 0.02f)
{
//Minimum base value against a locked loop
channelVolume = 0.02f;
}
while (channelVolume > 0.0f)
{
//1-(x^2.5) crossfade calculation with 0.15 base value, pow(0.15f, 2.5f)
//Increment-decrement value is approximately 0.0087 per 15 millisecs
channelVolume -= 0.0087f;
ptrMusicGroupChannel->setVolume(channelVolume);
std::this_thread::sleep_for(std::chrono::milliseconds(15));
if (channelVolume < 0.01f)
{
ptrMusicGroupChannel->setVolume(0.0f); //For precision
ptrMusicChannel1->stop();
ptrMusicChannel2->stop();
break;
}
ptrMusicGroupChannel->getVolume(&channelVolume);
}
ptrMusicGroupChannel->setVolume(1.0f);
}
////////////////////////////////////////////////////////////////////////
void AudioEngine::Update
(
)
{
ptrSystem->update();
}
Music.h
// Music.h
#pragma once
#include "AudioEngine.h"
class Music
{
public:
virtual ~Music() = default;
virtual void Play(AudioEngine & _obj, const int _fileID) = 0;
virtual void CallToPlay(AudioEngine & _obj, const int _fileID) = 0;
virtual void UpdateMusic(AudioEngine & _obj) = 0;
};
Interactive.h
// Interactive.h
#pragma once
#include "Music.h"
#include <vector>
#include <list>
#include <random>
#include <thread>
class InteractiveState final : public Music
{
public:
InteractiveState();
~InteractiveState() = default;
void SetMaxStateLoop(const int _maxStateLoopAmount = 4);
void CreateContainer(const bool _randomContainer = false,
const int _containerLoopAmount = 1);
void SetSegmentData(const int _segmentID,
const int _containerIndex,
const int _segmentLoopAmount,
const int _segmentDuration);
void SetContainerSegmentOrder(const int _containerIndex);
void ShrinkInteractiveContainers();
void SegmentIndexToPlayGenerator(const int _containerIndex);
void Play(AudioEngine & _obj, const int _fileID = 0) override;
void CallToPlay(AudioEngine & _obj, const int _fileID = 0) override;
void UpdateMusic(AudioEngine & _obj) override;
void DefaultValues();
private:
//State members
int m_maxStateLoopAmount;
int m_maxContainerIndex;
//Segment (music) members
int m_segmentLoopAmount;
unsigned int m_segmentDuration;
//Play state members in active usage
bool m_playAction;
int m_currentStateLoop;
int m_currentContainerIndex;
int m_currentContainerLoop;
int m_currentSegmentLoop;
unsigned int m_currentSegmentPosition;
//Creating segment music data for each container's segment data store
struct SegmentData
{
int m_segmentID = 0;
int m_segmentLoopAmount = 0;
unsigned int m_segmentDuration = 0;
};
//Creating container store for states which will be triggered in game scenario
struct ContainerData
{
bool b_randomContainer = false;
int m_containerLoopAmount = 0;
std::vector<SegmentData> Segments;
std::list<int> SegmentIndicesToPlay;
};
std::vector<ContainerData> Containers;
};
Interactive.cpp
// Interactive.cpp
#pragma once
#include "Interactive.h"
InteractiveState::InteractiveState() :
m_maxStateLoopAmount(0),
m_maxContainerIndex(0),
m_segmentLoopAmount(0),
m_segmentDuration(0),
m_playAction(false),
m_currentStateLoop(0),
m_currentContainerIndex(0),
m_currentContainerLoop(0),
m_currentSegmentLoop(0),
m_currentSegmentPosition(0)
{
}
////////////////////////////////////////////////////////////////////////
void InteractiveState::SetMaxStateLoop
(
const int _maxStateLoopAmount
)
{
//State max loop amount should be bigger than 0
m_maxStateLoopAmount = _maxStateLoopAmount;
}
////////////////////////////////////////////////////////////////////////
void InteractiveState::CreateContainer
(
const bool _randomContainer,
const int _containerLoopAmount
)
{
//Containers play in order
//In random type container, segments will play in random
auto containerData = ContainerData();
containerData.b_randomContainer = _randomContainer;
containerData.m_containerLoopAmount = _containerLoopAmount;
Containers.push_back(containerData);
//Setting interactive state size at the same time
m_maxContainerIndex = Containers.size();
}
////////////////////////////////////////////////////////////////////////
void InteractiveState::SetSegmentData
(
const int _segmentID,
const int _containerIndex,
const int _segmentLoopAmount,
const int _segmentDuration
)
{
auto segmentData = SegmentData();
segmentData.m_segmentID = _segmentID;
segmentData.m_segmentLoopAmount = _segmentLoopAmount;
segmentData.m_segmentDuration = _segmentDuration;
Containers[_containerIndex].Segments.push_back(segmentData);
}
////////////////////////////////////////////////////////////////////////
void InteractiveState::SetContainerSegmentOrder
(
const int _containerIndex
)
{
SegmentIndexToPlayGenerator(_containerIndex);
}
////////////////////////////////////////////////////////////////////////
void InteractiveState::ShrinkInteractiveContainers
(
)
{
//For potentially less memory consumption
for (auto & containerData : Containers)
{
containerData.Segments.shrink_to_fit();
}
Containers.shrink_to_fit();
}
////////////////////////////////////////////////////////////////////////
void InteractiveState::SegmentIndexToPlayGenerator
(
const int _containerIndex
)
{
const bool isRandom = Containers[_containerIndex].b_randomContainer;
const int totalSegments = Containers[_containerIndex].Segments.size();
auto & segmentIndicesToPlay = Containers[_containerIndex].SegmentIndicesToPlay;
segmentIndicesToPlay.clear();
if (isRandom)
{
//Create a vector of indices from 0 to totalSegments - 1
std::vector<int> indices(totalSegments);
for (int i = 0; i < totalSegments; ++i)
{
indices[i] = i;
}
//Shuffle the vector using a random engine
unsigned int seed = static_cast<unsigned int>(time(0));
std::mt19937 engine(seed);
std::shuffle(indices.begin(), indices.end(), engine);
//Copy this temporary vector into the segment indices list
for (const int i : indices)
{
segmentIndicesToPlay.push_back(i);
}
}
else
{
// If not random, just insert the indices in order
for (int i = 0; i < totalSegments; ++i)
{
segmentIndicesToPlay.push_back(i);
}
}
}
////////////////////////////////////////////////////////////////////////
void InteractiveState::Play
(
AudioEngine & _obj,
const int _fileID
)
{
//Checking current state loop number
if (m_currentStateLoop < m_maxStateLoopAmount)
{
//Checking current container
if (m_currentContainerIndex < m_maxContainerIndex)
{
//Playing container loops
auto & activeContainer = Containers[m_currentContainerIndex];
if (m_currentContainerLoop < activeContainer.m_containerLoopAmount)
{
//Extracting segment info and playing the segment
auto & segmentIndicesToPlay = activeContainer.SegmentIndicesToPlay;
if (m_playAction) //m_playAction works as a valve !!!
{
const int segmentIndex = segmentIndicesToPlay.front();
const auto & segmentData = activeContainer.Segments[segmentIndex];
m_segmentLoopAmount = segmentData.m_segmentLoopAmount;
m_segmentDuration = segmentData.m_segmentDuration;
m_currentSegmentLoop++;
m_playAction = false; //valve is close
_obj.PlayMusic(segmentData.m_segmentID);
}
//Just before segment duration to be completed, it will go inside earlier
//Then, thread "sleep for" will wait for the time difference
//Let's make the threshold 100 ms
_obj.GetActiveChannel()->getPosition(&m_currentSegmentPosition, FMOD_TIMEUNIT_MS);
if (m_currentSegmentPosition >= m_segmentDuration - 100)
{
//If position is higher than m_segmentDuration somehow, there will be delay
//Programme will continue to run for stability by accepting delay if it is case
if (m_currentSegmentPosition < m_segmentDuration)
{
std::this_thread::sleep_for
(
std::chrono::milliseconds(m_segmentDuration - m_currentSegmentPosition)
);
}
m_playAction = true; //valve is open
//If the current segment's job is done, go to the next segment to play
if (m_currentSegmentLoop >= m_segmentLoopAmount)
{
segmentIndicesToPlay.pop_front();
m_currentSegmentLoop = 0;
}
//Going to next container loop when segment plays are completed
if (segmentIndicesToPlay.empty())
{
m_currentContainerLoop++;
}
}
}
else
{
//Setting segment indices to play for the current container after it's turn is done
SegmentIndexToPlayGenerator(m_currentContainerIndex);
//If segment numbers are big enough, we can use thread to calculate indices
/*
{
const int containerIndex = m_currentContainerIndex; // Against index change during the execution
std::thread playOrder(&InteractiveState::SegmentIndexToPlayGenerator, this, containerIndex);
playOrder.detach();
}
*/
//Move to next container in its state after previous container finished its all loops
m_currentContainerIndex++;
m_currentContainerLoop = 0;
}
}
else
{
m_currentStateLoop++;
m_currentContainerIndex = 0; //Back to the first container
}
}
else
{
m_currentStateLoop = 0;
_obj.Stop();
std::cout << "- Current state play loops are completed" << "\n";
}
}
////////////////////////////////////////////////////////////////////////
void InteractiveState::CallToPlay
(
AudioEngine & _obj,
const int _fileID
)
{
//If both channels are active, refuse for the call
if (_obj.MusicChannelActivity() != 1)
{
//First play for new coming state
_obj.AssignChannel();
m_playAction = true;
Play(_obj);
}
}
////////////////////////////////////////////////////////////////////////
void InteractiveState::UpdateMusic
(
AudioEngine & _obj
)
{
Play(_obj, 0);
}
////////////////////////////////////////////////////////////////////////
void InteractiveState::DefaultValues
(
)
{
m_playAction = false;
m_currentStateLoop = 0;
m_currentContainerIndex = 0;
m_currentContainerLoop = 0;
m_currentSegmentLoop = 0;
m_currentSegmentPosition = 0;
}
Background.h
// Background.h
#pragma once
#include "Music.h"
class Background final : public Music
{
public:
Background() = default;
Background(const Background&) = delete;
~Background() = default;
Background &operator=(const Background&) = delete;
void Play(AudioEngine & _obj, const int _fileID) override;
void CallToPlay(AudioEngine & _obj, const int _fileID) override;
void UpdateMusic(AudioEngine & _obj) override;
};
Background.cpp
// Background.cpp
#pragma once
#include "Background.h"
void Background::Play(AudioEngine & _obj, const int _fileID)
{
_obj.AssignChannel();
_obj.PlayMusic(_fileID);
}
void Background::CallToPlay(AudioEngine & _obj, const int _fileID)
{
//If both channels are active, refuse for the call
if (_obj.MusicChannelActivity() != 1)
{
Play(_obj, _fileID);
}
}
void Background::UpdateMusic(AudioEngine & _obj)
{
}
Main.cpp
// Main.cpp
#pragma once
#include "Interactive.h"
#include "Background.h"
#include <SFML/Window.hpp>
int main()
{
//Creating main system object dealing with system, channels, load, etc.
AudioEngine audio;
//Initialising channel group volumes
audio.SetMasterChannelVolume(1.0f);
audio.SetMusicChannelVolume(1.0f);
/*Loading interactive music files example: if their paths were used
audio.Load(201, R"(C:\Data\battle1.mp3)", "Battle State -> Segment 1");
Segment ID, Container index, Segment index, Segment loop, Segment duration:
battle.setSegment(201, 0, 0, 1, 4000);*/
//Loading interactive music files from resource
//Setting Resource ID, Resource type, File name
audio.LoadFromResource(201, "MP3", "- Battle State -> Segment 1");
audio.LoadFromResource(202, "MP3", "- Battle State -> Segment 2");
audio.LoadFromResource(203, "MP3", "- Battle State -> Segment 3");
audio.LoadFromResource(204, "MP3", "- Battle State -> Segment 4");
audio.LoadFromResource(205, "MP3", "- Tension State -> Segment 1");
audio.LoadFromResource(206, "MP3", "- Tension State -> Segment 2");
audio.LoadFromResource(207, "MP3", "- Tension State -> Segment 3");
audio.LoadFromResource(208, "MP3", "- Tension State -> Segment 4");
audio.LoadFromResource(209, "MP3", "- Background Ambience -> Peaceful track");
//Creating background music state
Background background;
//Creating interactive music states
InteractiveState battle;
InteractiveState tension;
//Pointer to music subclass objects
Music *ptrMusic = nullptr;
//SETTING INTERACTIVE STATE OBJECTS
//State max loop amount (default = 4)
battle.SetMaxStateLoop();
tension.SetMaxStateLoop();
//Deciding how many containers would be in the interactive state
//Container type (default = false), Container loop amount (default = 1)
battle.CreateContainer();
battle.CreateContainer(true); //Segments will play in random order
tension.CreateContainer();
tension.CreateContainer(true);
//Assigning the segments into the containers with relevant data
//Segment ID, Container index, Segment loop, Segment duration(ms)
battle.SetSegmentData(201, 0, 1, 4000);
battle.SetSegmentData(202, 1, 1, 4000);
battle.SetSegmentData(203, 1, 2, 4000);
battle.SetSegmentData(204, 1, 2, 4000);
tension.SetSegmentData(205, 0, 1, 4000);
tension.SetSegmentData(206, 0, 2, 4000);
tension.SetSegmentData(207, 1, 1, 4000);
tension.SetSegmentData(208, 1, 1, 4000);
//Setting segment play order in the containers (random or not)
//Container index
battle.SetContainerSegmentOrder(0);
battle.SetContainerSegmentOrder(1);
tension.SetContainerSegmentOrder(0);
tension.SetContainerSegmentOrder(1);
//Shrink engine maps and state containers (Optional)
audio.ShrinkAudioEngineMaps();
battle.ShrinkInteractiveContainers();
tension.ShrinkInteractiveContainers();
//======================================================================//
std::cout << "Multistate Interactive Music Demonstration" << "\n";
std::cout << "Music and programming: Hakan Yurdakul, copyright(c) 2025" << "\n";
std::cout << "" << "\n";
std::cout << "Press 1 to play Background Ambience" << "\n";
std::cout << "Press 2 to play Tension State (interactive)" << "\n";
std::cout << "Press 3 to play Battle State (interactive)" << "\n";
std::cout << "" << "\n";
std::cout << "Press SPACE to stop playing music" << "\n";
std::cout << "Press ESCAPE to exit" << "\n";
std::cout << "" << "\n";
//======================================================================//
//For keyword commands; it works as a valve
std::string last_command = "";
//START OF THE LOOP
while (true)
{
//Keyboard Commands
//======================================================================//
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Num1))
{
if (last_command != "Background" && audio.MusicChannelActivity() != 1)
{
last_command = "Background";
ptrMusic = &background;
background.Play(audio, 209);
}
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Num2))
{
if (last_command != "Tension" && audio.MusicChannelActivity() != 1)
{
last_command = "Tension";
ptrMusic = &tension;
tension.CallToPlay(audio);
}
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Num3))
{
if (last_command != "Battle" && audio.MusicChannelActivity() != 1)
{
last_command = "Battle";
ptrMusic = &battle;
battle.CallToPlay(audio);
}
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Space))
{
if (last_command != "Stop" && audio.MusicChannelActivity() != 2)
{
last_command = "Stop";
ptrMusic = nullptr;
audio.Stop();
battle.DefaultValues();
tension.DefaultValues();
std::cout << "- Stopped..." << "\n";
}
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))
{
std::cout << "" << "\n";
std::cout << "Thanks for listening..." << "\n";
std::cout << "Preparing to exit..." << "\n";
ptrMusic = nullptr;
audio.Stop();
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
break;
}
//======================================================================//
//Checking and updating continously
if (ptrMusic != nullptr)
{
ptrMusic->UpdateMusic(audio);
}
audio.Update();
//If nothing plays, release the pointer and command key (if assigned)
if (audio.MusicChannelActivity() == 2)
{
ptrMusic = nullptr;
last_command = "";
}
}
//END OF THE LOOP
return 0;
}