The open source repository for the action RPG game in development by Sig Productions titled 'Adventures in Lestoria'!
https://forums.lestoria.net
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
292 lines
9.2 KiB
292 lines
9.2 KiB
#pragma region License
|
|
/*
|
|
License (OLC-3)
|
|
~~~~~~~~~~~~~~~
|
|
|
|
Copyright 2024 Joshua Sigona <sigonasr2@gmail.com>
|
|
|
|
Redistribution and use in source and binary forms, with or without modification,
|
|
are permitted provided that the following conditions are met:
|
|
|
|
1. Redistributions or derivations of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
2. Redistributions or derivative works in binary form must reproduce the above
|
|
copyright notice. This list of conditions and the following disclaimer must be
|
|
reproduced in the documentation and/or other materials provided with the distribution.
|
|
|
|
3. Neither the name of the copyright holder nor the names of its contributors may
|
|
be used to endorse or promote products derived from this software without specific
|
|
prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
|
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
SUCH DAMAGE.
|
|
|
|
Portions of this software are copyright © 2023 The FreeType
|
|
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
|
|
All rights reserved.
|
|
*/
|
|
#pragma endregion
|
|
#include "Audio.h"
|
|
#include "AdventuresInLestoria.h"
|
|
#include "DEFINES.h"
|
|
#include "util.h"
|
|
|
|
INCLUDE_game
|
|
INCLUDE_DATA
|
|
|
|
float Audio::defaultFadeTime;
|
|
|
|
void Audio::Initialize(){
|
|
Engine().SetBackgroundPlay(true);
|
|
Self().events.insert("Default Volume");
|
|
for(auto&[key,data]:DATA["Events"]){
|
|
if(key!="SFX"){ //HACK ALERT! We are specifically excluding the SFX key because it's used to specify the sound effects in-game.
|
|
Self().events.insert(key);
|
|
}
|
|
}
|
|
for(auto&[songFileName,size]:DATA["BGM"]){
|
|
auto&data=DATA["BGM"][songFileName];
|
|
if(songFileName!="Default Fade Time"){
|
|
int channelCounter=0;
|
|
|
|
BGM&bgm=Self().bgm[songFileName];
|
|
|
|
bgm.SetFileName(songFileName);
|
|
bgm.SetName(data["Track Name"].GetString());
|
|
|
|
while(data.HasProperty(std::format("channel[{}]",channelCounter))){
|
|
std::string channelName=data[std::format("channel[{}]",channelCounter)].GetString();
|
|
if(!std::filesystem::exists("bgm_directory"_S+channelName))ERR(std::format("WARNING! Could not load file {} for track {}",channelName,songFileName));
|
|
bgm.AddChannel(channelName);
|
|
channelCounter++;
|
|
}
|
|
|
|
if(!data["Events"].HasProperty("Default Volume"))ERR(std::format("WARNING! Track {} does not have a Default Volume parameter!",bgm.GetName()));
|
|
|
|
if(data.HasProperty("Fade Time")){
|
|
bgm.SetFadeTime(data["Fade Time"].GetReal());
|
|
}else{
|
|
bgm.SetFadeTime(defaultFadeTime);
|
|
}
|
|
|
|
|
|
if(data.HasProperty("Events")){
|
|
for(auto&eventName:Self().events){
|
|
auto&eventData=data["Events"][eventName];
|
|
|
|
if(eventData.GetValueCount()==0){ //We assume we'll inherit from the default volume instead.
|
|
VolumeList volumes;
|
|
for(int i=0;i<eventData.GetValueCount();i++){
|
|
volumes.push_back(data["Events"]["Default Volume"].GetInt(i)/100.f);
|
|
}
|
|
bgm.AddEventVolumes(eventName,volumes);
|
|
}else{
|
|
if(eventData.GetValueCount()!=bgm.GetChannelCount())ERR(std::format("WARNING! {} parameters do not match channel count. {} != {}",eventName,eventData.GetValueCount(),bgm.GetChannelCount()));
|
|
VolumeList volumes;
|
|
for(int i=0;i<eventData.GetValueCount();i++){
|
|
volumes.push_back(eventData.GetInt(i)/100.f);
|
|
}
|
|
bgm.AddEventVolumes(eventName,volumes);
|
|
}
|
|
}
|
|
}
|
|
}else{
|
|
defaultFadeTime=data.GetReal();
|
|
}
|
|
}
|
|
|
|
Self().bgm.SetInitialized();
|
|
}
|
|
|
|
MiniAudio&Audio::Engine(){
|
|
return game->audioEngine.audioEngine;
|
|
}
|
|
void Audio::Play(const std::string_view sound){
|
|
Engine().Play(std::string(sound));
|
|
};
|
|
const size_t Audio::LoadAndPlay(const std::string_view sound,const bool loop){
|
|
size_t soundID=Engine().LoadSound(std::string(sound));
|
|
Engine().Play(soundID,loop);
|
|
return soundID;
|
|
};
|
|
void Audio::PlayBGM(const std::string_view sound,const bool loop){
|
|
BGM&track=Self().bgm[std::string(sound)];
|
|
Self().fullyLoaded=false;
|
|
StopBGM(); //Stop any currently playing track.
|
|
Self().playParams={std::string(sound),loop};
|
|
Self().playBGMWaitTime=0.7f;
|
|
};
|
|
|
|
void Audio::StopBGM(){
|
|
if(Self().BGMIsPlaying()){
|
|
BGM¤tTrack=Self().bgm[Self().currentBGM];
|
|
for(int trackID:currentTrack.GetChannelIDs()){
|
|
Engine().Stop(trackID);
|
|
}
|
|
}
|
|
}
|
|
|
|
const bool Audio::BGMIsPlaying(){
|
|
return Self().currentBGM.length()>0;
|
|
}
|
|
|
|
const Volume&Audio::BGM::GetVolume(const Event&eventName,const ChannelID&id)const{
|
|
return eventVolumes.GetVolumes(eventName).at(id);
|
|
}
|
|
|
|
void Audio::BGM::Load(){
|
|
if(Self().BGMIsPlaying()){
|
|
if(Self().GetTrackName()==songFileName)return; //We are already playing the current track.
|
|
BGM&bgm=Self().bgm[Self().GetTrackName()];
|
|
if(Self().BGMIsPlaying()){
|
|
bgm.Unload();
|
|
}
|
|
}
|
|
Self().currentBGM=songFileName;
|
|
BGM&newBgm=Self().bgm[songFileName];
|
|
if(newBgm.channels.size()>0)ERR(std::format("WARNING! The size of the channels list is greater than zero! Size: {}",newBgm.channels.size()));
|
|
for(const ChannelName&channel:newBgm.GetChannels()){
|
|
ChannelID soundID=Engine().LoadSound("bgm_directory"_S+channel);
|
|
newBgm.channels.push_back(soundID);
|
|
}
|
|
}
|
|
|
|
void Audio::BGM::Unload(){
|
|
BGM&bgm=Self().bgm[Self().currentBGM];
|
|
for(const ChannelID&id:channels){
|
|
Engine().UnloadSound(id);
|
|
}
|
|
channels.clear();
|
|
Self().currentBGM="";
|
|
}
|
|
|
|
const ChannelID&Audio::BGM::GetChannelID(const int index){
|
|
return channels[index];
|
|
}
|
|
|
|
const ChannelIDList&Audio::BGM::GetChannelIDs()const{
|
|
return channels;
|
|
}
|
|
|
|
const std::vector<ChannelName>&Audio::BGM::GetChannels()const{
|
|
return channelNames;
|
|
}
|
|
|
|
void Audio::BGM::SetFadeTime(const float fadeTime){
|
|
this->fadeTime=fadeTime;
|
|
}
|
|
|
|
void Audio::BGM::AddEventVolumes(const Event&eventName,const VolumeList&volumes){
|
|
eventVolumes.AddEventInfo(eventName,volumes);
|
|
}
|
|
|
|
const size_t Audio::BGM::GetChannelCount()const{
|
|
return channelNames.size();
|
|
}
|
|
|
|
const SongName&Audio::BGM::GetName()const{
|
|
return songName;
|
|
}
|
|
|
|
void Audio::BGM::SetName(std::string_view name){
|
|
songName=name;
|
|
}
|
|
|
|
void Audio::BGM::SetFileName(std::string_view name){
|
|
songFileName=name;
|
|
}
|
|
|
|
void Audio::BGM::AddChannel(const ChannelName&name){
|
|
channelNames.push_back(name);
|
|
}
|
|
|
|
const VolumeList&Audio::EventData::GetVolumes(const Event&event)const{
|
|
if(eventInfo.find(event)!=eventInfo.end())return eventInfo.at(event);
|
|
return eventInfo.at("Default Volume");
|
|
}
|
|
|
|
void Audio::EventData::AddEventInfo(const Event&eventName,const VolumeList&volumes){
|
|
eventInfo[eventName]=volumes;
|
|
}
|
|
|
|
Audio&Audio::Self(){
|
|
return game->audioEngine;
|
|
}
|
|
|
|
const Event&Audio::GetAudioEvent(){
|
|
return Self().currentAudioEvent;
|
|
}
|
|
void Audio::SetAudioEvent(const Event&eventName){
|
|
if(Self().events.find(eventName)==Self().events.end())ERR(std::format("WARNING! cannot find event {}",eventName));
|
|
|
|
Self().currentAudioEvent=eventName;
|
|
|
|
if(Audio::BGMIsPlaying()){
|
|
BGM¤tBgm=Self().bgm[Self().currentBGM];
|
|
|
|
Self().targetVolumes.clear();
|
|
|
|
Self().fadeToTargetVolumeTime=currentBgm.GetFadeTime();
|
|
for(int currentTrackIndex=0;int trackID:currentBgm.GetChannelIDs()){
|
|
//Engine().SetVolume(trackID,currentBgm.GetVolume(eventName,currentTrackIndex));
|
|
Self().targetVolumes.push_back(currentBgm.GetVolume(eventName,currentTrackIndex)); //Set the target volumes so that the engine can gradually move the volumes over to the new settings.
|
|
currentTrackIndex++;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string operator""_SFX(const char*key,size_t length){
|
|
return "sfx_directory"_S+std::string(key,length);
|
|
}
|
|
|
|
const SongName&Audio::GetTrackName(){
|
|
return Self().currentBGM;
|
|
}
|
|
|
|
void Audio::Update(){
|
|
if(Self().playBGMWaitTime>0.f){
|
|
Self().playBGMWaitTime=std::max(Self().playBGMWaitTime-game->GetElapsedTime(),0.f);
|
|
if(Self().playBGMWaitTime==0.f){
|
|
BGM&track=Self().bgm[Self().playParams.sound];
|
|
track.Load();
|
|
Self().prevVolumes.clear();
|
|
Self().targetVolumes.clear();
|
|
Self().fadeToTargetVolumeTime=0.f;
|
|
for(int channelListIndex=0;int trackID:track.GetChannelIDs()){
|
|
float channelVol=track.GetVolume(Self().currentAudioEvent,channelListIndex);
|
|
Self().prevVolumes.push_back(channelVol);
|
|
Self().targetVolumes.push_back(channelVol);
|
|
Engine().SetVolume(trackID,channelVol);
|
|
Engine().Play(trackID,Self().playParams.loop);
|
|
channelListIndex++;
|
|
}
|
|
Self().fullyLoaded=true;
|
|
}
|
|
}
|
|
|
|
if(Self().fadeToTargetVolumeTime>0.f){
|
|
Self().fadeToTargetVolumeTime=std::max(0.f,Self().fadeToTargetVolumeTime-game->GetElapsedTime());
|
|
for(int counter=0;float&vol:Self().prevVolumes){
|
|
const BGM¤tBgm=Self().bgm[Self().currentBGM];
|
|
Engine().SetVolume(currentBgm.GetChannelIDs()[counter],util::lerp(vol,Self().targetVolumes[counter],1-(Self().fadeToTargetVolumeTime/currentBgm.GetFadeTime())));
|
|
counter++;
|
|
}
|
|
}
|
|
}
|
|
|
|
const bool Audio::BGMFullyLoaded(){
|
|
return Self().fullyLoaded;
|
|
}
|
|
|
|
const float&Audio::BGM::GetFadeTime()const{
|
|
return fadeTime;
|
|
} |