01-23-2022, 02:47 PM
(This post was last modified: 01-24-2022, 02:26 AM by DerVVulfman.
Edit Reason: Update : Fix inaccurate fade volumes
)
Audio Module (C++ / SFML)
Version 0.2
Description
This is a C++ rewrite of the RPG Maker Audio module implemented with Simple and Fast Multimedia Library by Laurent Gomilla and team.
The end user is responsible for compiling and deploying this in their project. I may provide a plug-n-play DLL later but for now its all on you to figure it out.
The system is written to RGSS3 specifications (and should be backwards compatible with RGSS and RGSS2 since all it does is add features and doesn't change fundamentals.) I have not implemented this in Ruby or any of the RPG Maker systems. It might be awhile but I will get back to that at a later date.
Features
(NOTE: Lack of mp3 support is a legal issue, apparently. This is outside the scope of my script and more related to SFML. End user is responsible for implementing their own mp3 support, if desired. (I find ogg to be a better format, personally. Ogg BGM and BGS seem to loop better than mp3 so I actually encourage people to switch formats.))
Requirements
Instructions
This is NOT a plug-n-play RPG Maker script, you'd need to compile it yourself with C++ and configure it to your needs.
While this is designed to be RGSS compliant, it has not been implemented in Ruby/RPG Maker.
I'm just sharing it for anybody who can do something with it.
If I get the C++ and Ruby to communicate, I will return with a full plug-in-play implementation anyone can use.
Differences from Original RGSS
There are minor differences between this and the vanilla RGSS, RGSS2 and RGSS3 implementations (personal taste)...
Source Files
RGL/Audio.hpp
RGL/Audio.cpp
Author's Notes
Notes : Classes and Memory
Notes : What Could Be Improved?
Notes : About (lack of) rbSFML Bindings
I am well aware that there is a SFML bindings for Ruby (rbSFML). It doesn't look like it has been updated in 6 years and I don't know if anybody still uses or supports it, nor do I know how stable it is. The SFML forum doesn't have a sub-forum for it in their bindings so I can only assume rbSFML may not be a stable library? I made the personal choice to do this without rbSFML. I would like to learn for myself how to extend Ruby with C++ so it was a personal choice to do it in C++.
Thank you for checking it out, feel free to lend me a hand if you know anything about this stuff.
Terms and Conditions
End user is responsible for figuring out how to compile and use this themselves. I will only be providing limited support and future updates.
Free to use in commercial and non-commercial projects, you don't even have to credit me (but I do like credits -_^).
SFML has its own terms under the ZLIB/PNG License. SFML is an open source multimedia library written by Laurent Gomilla and a team of programming ninjas.
Version 0.2
Description
This is a C++ rewrite of the RPG Maker Audio module implemented with Simple and Fast Multimedia Library by Laurent Gomilla and team.
The end user is responsible for compiling and deploying this in their project. I may provide a plug-n-play DLL later but for now its all on you to figure it out.
The system is written to RGSS3 specifications (and should be backwards compatible with RGSS and RGSS2 since all it does is add features and doesn't change fundamentals.) I have not implemented this in Ruby or any of the RPG Maker systems. It might be awhile but I will get back to that at a later date.
Features
- Supports multiple filetypes; wav, ogg, flac, probably others.
- Play 1 Background Music
- Play 1 Background Sound
- Play 1 Music Effect
- Play multiple Sound Effects
- Adjust fade in/fade out timing of Music Effects
- Available Functions : Play, Pause, Stop, Fade In/Out, Replay, Volume=, Pitch=
- Please see SFML/Audio for more information.
(NOTE: Lack of mp3 support is a legal issue, apparently. This is outside the scope of my script and more related to SFML. End user is responsible for implementing their own mp3 support, if desired. (I find ogg to be a better format, personally. Ogg BGM and BGS seem to loop better than mp3 so I actually encourage people to switch formats.))
Requirements
- SFML (Simple and Fast Multimedia Library)
- Should only require SFML/Audio (for audio) and SFML/System (for sf::Time functionality). See header file for more details.
- Working knowledge of C++ and the build tools required to compile and run it.
Instructions
This is NOT a plug-n-play RPG Maker script, you'd need to compile it yourself with C++ and configure it to your needs.
While this is designed to be RGSS compliant, it has not been implemented in Ruby/RPG Maker.
I'm just sharing it for anybody who can do something with it.
If I get the C++ and Ruby to communicate, I will return with a full plug-in-play implementation anyone can use.
Content Hidden
Assuming you have a C++ build environment...
(NOT INCLUDED : RGL module. This is more tailored to my personal project than anything. You can write your own module and change the "RGL" to whatever you find appropriate. I use namespace "rgl" (ie rgl::Audio) to separate it from SFML's implementation (sf::Audio). My RGL module has nothing to do with the functionality of this system other than as a directory/namespace.)
You could probably compile yourself a nice little libaudio.dll and include it with your game project, but then you'd have to write C++/Ruby extensions. I haven't got that far nor do I know (yet) how to do all that. I'd assume you can probably do it via Win32API (or Win32::API for Ruby >= 1.9.2) if you're running an RPG Maker game on Windows. How it would work on other platforms? I don't have the EXP yet to know how that all works. Until then, I will only be providing very limited support.
- Place RGL/Audio.hpp in */include/RGL/
- Place the RGL/Audio.cpp in */src/RGL/
(NOT INCLUDED : RGL module. This is more tailored to my personal project than anything. You can write your own module and change the "RGL" to whatever you find appropriate. I use namespace "rgl" (ie rgl::Audio) to separate it from SFML's implementation (sf::Audio). My RGL module has nothing to do with the functionality of this system other than as a directory/namespace.)
You could probably compile yourself a nice little libaudio.dll and include it with your game project, but then you'd have to write C++/Ruby extensions. I haven't got that far nor do I know (yet) how to do all that. I'd assume you can probably do it via Win32API (or Win32::API for Ruby >= 1.9.2) if you're running an RPG Maker game on Windows. How it would work on other platforms? I don't have the EXP yet to know how that all works. Until then, I will only be providing very limited support.
Differences from Original RGSS
There are minor differences between this and the vanilla RGSS, RGSS2 and RGSS3 implementations (personal taste)...
- When playing Music Effects, all other audio (BGM, BGS, SE) fade out in 2 seconds instead of instant.
- When playing Music Effects, the volume of all other audio is reduced to 10 instead of 0.
- When playing Music Effects, the volume fades in approximately 4 seconds. Adjustable.
- These timing features should be easily adjustable by changing the member variable values in the header file.
- NOTE: The Fade Out for music effects should be a minimum of 1.0f or it won't fade out at all. Of course, you can edit the script yourself if you're trying to do, say, a half second.
Source Files
RGL/Audio.hpp
Content Hidden
Code:
/*=============================================================================*/
// ** rgl::Audio
/*-----------------------------------------------------------------------------*/
// Public Members
// ==============
// sf::Music m_BGM
// sf::Music m_BGS
// sf::Sound m_ME
// sf::Vector m_VectorSE
//
// Public Functions
// ================
// Audio.playBGM()
// Audio.playBGM(name)
// Audio.playBGM(name, volume, pitch)
// Audio.replayBGM()
// Audio.pauseBGM()
// Audio.stopBGM()
// Audio.playBGS()
// Audio.playBGS(name)
// Audio.playBGS(name, volume, pitch)
// Audio.replayBGS()
// Audio.pauseBGS()
// Audio.stopBGS()
// Audio.playME()
// Audio.playME(name)
// Audio.playME(name, volume, pitch)
// Audio.replayME()
// Audio.pauseME()
// Audio.stopME()
// Audio.playSE()
// Audio.playSE(name)
// Audio.playSE(name, volume, pitch)
// Audio.replaySE()
// Audio.pauseSE()
// Audio.stopSE()
// Audio.update()
//
// Private Members
// ===============
// bool m_waitME
/*=============================================================================*/
#ifndef RGL_AUDIO_H
#define RGL_AUDIO_H
#include <RGL.hpp>
#include <RGL/Audio.hpp>
#include <RGL/AudioFile.hpp>
#include <SFML/Audio.hpp>
#include <SFML/Audio/Sound.hpp>
#include <SFML/Audio/SoundBuffer.hpp>
#include <SFML/System/Time.hpp>
#include <deque>
#include <stdlib.h>
#include <iostream>
#include <istream>
/*============================================================================*/
// ** rgl
/*============================================================================*/
namespace rgl {
static const int NUM_OF_SE = 16;
//template<typename S> sf::Sound;
//template<typename B> sf::SoundBuffer;
class Audio {
public:
// Construct and Destruct
Audio();
virtual ~Audio();
// Public Members
bool m_OutOfFocus = false;
// Public Functions : BGM
bool playBGM(const sf::String& l_name, float volume, float pitch);
bool playBGM(const sf::String& l_name);
bool playBGM();
bool replayBGM();
void pauseBGM();
void stopBGM();
void fadeBGM(float volume, float duration);
// Public Functions : BGS
bool playBGS(const sf::String& l_name, float volume, float pitch);
bool playBGS(const sf::String& l_name);
bool playBGS();
bool replayBGS();
void pauseBGS();
void stopBGS();
void fadeBGS(float volume, float duration);
// Public Functions : ME
bool playME(const sf::String& l_name, float volume, float pitch);
bool playME(const sf::String& l_name);
bool playME();
bool replayME();
void pauseME();
void stopME();
void fadeME(float volume, float duration);
// Public Functions : SE
bool playSE(const sf::String& l_name, float volume, float pitch);
bool playSE(const sf::String& l_name);
bool playSE();
bool replaySE();
void pauseSE();
void stopSE();
void fadeSE(float volume, float duration);
// Public Functions : Update
void update();
void reset();
void fadeAll(float volume, float duration);
protected:
// ...
private:
// Private Member Constants
const float m_MEFadeOutT = 2.0f;
const float m_MEFadeInT = 4.0f;
// Private Member Variables
sf::Music m_BGM; // Background Music
sf::Music m_BGS; // Background Sound
sf::Music m_ME; // Music Effect
sf::Sound m_SE; // Sound Effect
std::deque<sf::Sound> m_SoundV;
std::deque<sf::SoundBuffer> m_BufferV;
int m_phaseME = 0;// Music Effect phase (0 None, 1 Fade in, 2 Play, 3 Fade Out)
sf::Clock m_timeME; // Music effect clock
sf::Clock m_timeSE; // Sound effect clock
sf::Time m_timeSEMax = sf::seconds(3.0);
float m_BGMFadeV = 0.0; // BGM Fade Volume
float m_BGSFadeV = 0.0; // BGS Fade Volume
float m_MEFadeV = 0.0; // ME Fade Volume
float m_SEFadeV = 0.0; // SE Fade Volume
float m_BGMFadeD = 0.0f; // BGM Fade Duration
float m_BGSFadeD = 0.0f; // BGS Fade Duration
float m_MEFadeD = 0.0f; // ME Fade Duration
float m_SEFadeD = 0.0f; // SE Fade Duration
float m_BGMLastV = 0.0;// BGM Last Volume
float m_BGSLastV = 0.0;// BGS Last Volume
//float m_MELastV = 0.0;// ME Last Volume (Not used/needed)
float m_SELastV = 0.0;// ME Last Volume
// Private Functions
void updateBGM();
void updateBGS();
void updateME();
void updateSE();
void updateSE(int i);
void updateBuffers();
void updateMusicEffect();
// ...Need to add limited vector for sound queue
};
}
#endif //RGL_AUDIO_H
RGL/Audio.cpp
Content Hidden
Code:
/*=============================================================================*/
// ** rgl::Audio
/*-----------------------------------------------------------------------------*/
// Public Members
// ==============
// sf::Music m_BGM
// sf::Music m_BGS
// sf::Sound m_ME
// sf::Vector m_VectorSE
//
// Public Functions
// ================
// Audio.playBGM()
// Audio.playBGM(name)
// Audio.playBGM(name, volume, pitch)
// Audio.pauseBGM()
// Audio.stopBGM()
// Audio.fadeBGM(volume, duration)
// Audio.playBGS()
// Audio.playBGS(name)
// Audio.playBGS(name, volume, pitch)
// Audio.pauseBGS()
// Audio.stopBGS()
// Audio.fadeBGS(volume, duration)
// Audio.playME()
// Audio.playME(name)
// Audio.playME(name, volume, pitch)
// Audio.pauseME()
// Audio.stopME()
// Audio.fadeME(volume, duration)
// Audio.playSE()
// Audio.playSE(name)
// Audio.playSE(name, volume, pitch)
// Audio.fadeSE(volume, duration)
// Audio.pauseSE()
// Audio.stopSE()
// Audio.fadeSE(volume, duration)
// Audio.reset()
// Audio.fadeAll(duration)
// Audio.update()
//
// Private Members
// ===============
// bool m_waitME
// float m_bgmVolB4ME
// float m_bgsVolB4ME
// float m_bgmFade
// float m_bgsFade
/*=============================================================================*/
#include <SFML/Audio.hpp>
#include <SFML/System/Time.hpp>
#include <RGL.hpp>
#include <RGL/Audio.hpp>
#include <istream>
//#include <RGL/AudioFile.hpp>
/*============================================================================*/
// ** rgl
/*============================================================================*/
namespace rgl {
/*----------------------------------------------------------------------------*/
// ** Audio() [Constructor]
/*----------------------------------------------------------------------------*/
Audio::Audio() {}
/*----------------------------------------------------------------------------*/
// ** ~Audio() [Destructor]
/*----------------------------------------------------------------------------*/
Audio::~Audio() {}
/*----------------------------------------------------------------------------*/
// ** Audio.playBGM(name, volume, pitch)
/*----------------------------------------------------------------------------*/
bool Audio::playBGM(const sf::String& l_name, float volume, float pitch) {
// If name is empty
if (l_name.isEmpty()) {
// Stop the BGM
m_BGM.stop();
// Nothing wrong with stopping the BGM
return true;
}
// If unable to open from file
if (!m_BGM.openFromFile(l_name)) {
// Error opening file
return false;
}
// Set volume, pitch and loop
m_BGM.setVolume(volume);
m_BGM.setPitch(pitch);
m_BGM.setLoop(true);
// Play the BGM
m_BGM.play();
// Opened file successfully
return true;
}
/*----------------------------------------------------------------------------*/
// ** Audio.playBGM(name)
/*----------------------------------------------------------------------------*/
bool Audio::playBGM(const sf::String& l_name) {
// Play BGM by name
return playBGM(l_name, 80.0, 1.0);
}
/*----------------------------------------------------------------------------*/
// ** Audio.playBGM()
/*----------------------------------------------------------------------------*/
bool Audio::playBGM() {
// Ensure BGM loops
m_BGM.setLoop(true);
// Play BGM without any arguments
m_BGM.play();
// Return true
return true;
}
/*----------------------------------------------------------------------------*/
// ** Audio.replayBGM()
/*----------------------------------------------------------------------------*/
bool Audio::replayBGM() {
m_BGM.stop();
m_BGM.play();
// Return true
return true;
}
/*----------------------------------------------------------------------------*/
// ** Audio.pauseBGM()
/*----------------------------------------------------------------------------*/
void Audio::pauseBGM() {
// Pause BGM
m_BGM.pause();
}
/*----------------------------------------------------------------------------*/
// ** Audio.fadeBGM(volume, duration, memorize = false)
/*----------------------------------------------------------------------------*/
void Audio::fadeBGM(float volume, float duration) {
// Set BGM fade volume and duration
m_BGMFadeV = volume;
m_BGMFadeD = sf::seconds(duration).asSeconds();
}
/*----------------------------------------------------------------------------*/
// ** Audio.stopBGM()
/*----------------------------------------------------------------------------*/
void Audio::stopBGM() {
// Stop BGM
m_BGM.stop();
}
/*----------------------------------------------------------------------------*/
// ** Audio.playBGS(name, volume, pitch)
/*----------------------------------------------------------------------------*/
bool Audio::playBGS(const sf::String& l_name, float volume, float pitch) {
// If name is empty
if (l_name.isEmpty()) {
// Stop the BGS
m_BGS.stop();
// Nothing wrong with stopping the BGS
return true;
}
// If unable to open from file
if (!m_BGS.openFromFile(l_name)) {
// Error opening file
return false;
}
// Set volume, pitch and loop
m_BGS.setVolume(volume);
m_BGS.setPitch(pitch);
m_BGS.setLoop(true);
// Play the BGS
m_BGS.play();
// Opened file successfully
return true;
}
/*----------------------------------------------------------------------------*/
// ** Audio.playBGS(name)
/*----------------------------------------------------------------------------*/
bool Audio::playBGS(const sf::String& l_name) {
// Play BGS by name
return playBGS(l_name, 80.0, 1.0);
}
/*----------------------------------------------------------------------------*/
// ** Audio.playBGS()
/*----------------------------------------------------------------------------*/
bool Audio::playBGS() {
// Ensure BGS loops
m_BGS.setLoop(true);
// Play BGS without any arguments
m_BGS.play();
// Return true
return true;
}
/*----------------------------------------------------------------------------*/
// ** Audio.replayBGS()
/*----------------------------------------------------------------------------*/
bool Audio::replayBGS() {
// Restart BGS
m_BGS.stop();
m_BGS.play();
// Return true
return true;
}
/*----------------------------------------------------------------------------*/
// ** Audio.pauseBGS()
/*----------------------------------------------------------------------------*/
void Audio::pauseBGS() {
// Pause BGS
m_BGS.pause();
}
/*----------------------------------------------------------------------------*/
// ** Audio.stopBGS()
/*----------------------------------------------------------------------------*/
void Audio::stopBGS() {
// Stop BGS
m_BGS.stop();
}
/*----------------------------------------------------------------------------*/
// ** Audio.fadeBGS(volume, duration, memorize = false)
/*----------------------------------------------------------------------------*/
void Audio::fadeBGS(float volume, float duration) {
// Set BGS fade volume and duration
m_BGSFadeV = volume;
m_BGSFadeD = duration;
}
/*----------------------------------------------------------------------------*/
// ** Audio.playME(name, volume, pitch)
/*----------------------------------------------------------------------------*/
bool Audio::playME(const sf::String& l_name, float volume, float pitch) {
// If name is empty
if (l_name.isEmpty()) {
// Ensure phase is 0
m_phaseME = 0;
// Stop the ME
m_ME.stop();
// Nothing wrong with stopping the ME
return true;
}
// If unable to open from file
if (!m_ME.openFromFile(l_name)) {
// Print to console
cout << "Error loading file" << endl;;
// Error opening file
return false;
}
// Set phase to 1
m_phaseME = 1;
// Set volume, pitch and loop
m_ME.setVolume(volume);
m_ME.setPitch(pitch);
m_ME.setLoop(false);
// Play the ME
m_ME.play();
// Opened file successfully
return true;
}
/*----------------------------------------------------------------------------*/
// ** Audio.playME(name)
/*----------------------------------------------------------------------------*/
bool Audio::playME(const sf::String& l_name) {
// Play ME by name
return playME(l_name, 80.0, 1.0);
}
/*----------------------------------------------------------------------------*/
// ** Audio.playME()
/*----------------------------------------------------------------------------*/
bool Audio::playME() {
// Ensure ME does not loop
m_ME.setLoop(false);
// Play ME without any arguments
m_ME.play();
// Return true
return true;
}
/*----------------------------------------------------------------------------*/
// ** Audio.replayME()
/*----------------------------------------------------------------------------*/
bool Audio::replayME() {
// Restart ME
m_ME.stop();
m_ME.play();
// Return true
return true;
}
/*----------------------------------------------------------------------------*/
// ** Audio.pauseME()
/*----------------------------------------------------------------------------*/
void Audio::pauseME() {
// Pause ME
m_ME.pause();
}
/*----------------------------------------------------------------------------*/
// ** Audio.stopME()
/*----------------------------------------------------------------------------*/
void Audio::stopME() {
// Stop ME
m_ME.stop();
}
/*----------------------------------------------------------------------------*/
// ** Audio.fadeME(volume, duration, memorize = false)
/*----------------------------------------------------------------------------*/
void Audio::fadeME(float volume, float duration) {
// Set ME fade volume and duration
m_MEFadeV = volume;
m_MEFadeD = duration;
}
/*----------------------------------------------------------------------------*/
// ** Audio.playSE(name, volume, pitch)
/*----------------------------------------------------------------------------*/
bool Audio::playSE(const sf::String& l_name, float volume, float pitch) {
// End function doing nothing if no name given
if (l_name.isEmpty()) { return true; }
// Create sound and sound buffer
sf::Sound sound;
sf::SoundBuffer buffer;
// If unable to open from file
if (!buffer.loadFromFile(l_name)) {
// Print to console
cout << "Unable to load Sound Effect" << endl;
// Error opening file
return false;
}
// Set volume, pitch and loop
sound.setVolume(volume);
sound.setPitch(pitch);
sound.setLoop(false);
// If sound duration is more than current max time
if (buffer.getDuration() > m_timeSEMax) {
// Restart timer and set time max duration
m_timeSE.restart();
m_timeSEMax = (buffer.getDuration());
m_timeSEMax += (buffer.getDuration());
}
// Add to vectors
m_SoundV.push_back(sound);
m_BufferV.push_back(buffer);
// Set the last buffer on the stack
m_SoundV[m_SoundV.size() - 1].setBuffer(m_BufferV[m_BufferV.size() - 1]);
// Play the sound
m_SoundV[m_SoundV.size() - 1].play();
cout << "Playing SE : " << m_SoundV.size() << endl;
// Opened file successfully
return true;
}
/*----------------------------------------------------------------------------*/
// ** Audio.playSE(name)
/*----------------------------------------------------------------------------*/
bool Audio::playSE(const sf::String& l_name) {
// Play SE by name
return playSE(l_name, 80.0, 1.0);
}
/*----------------------------------------------------------------------------*/
// ** Audio.playSE()
/*----------------------------------------------------------------------------*/
bool Audio::playSE() {
// Ensure SE does not loop
m_SE.setLoop(false);
// Play SE without any arguments
m_SE.play();
// Return true
return true;
}
/*----------------------------------------------------------------------------*/
// ** Audio.replaySE()
/*----------------------------------------------------------------------------*/
bool Audio::replaySE() {
// Restart SE
m_SE.stop();
m_SE.play();
// Return true
return true;
}
/*----------------------------------------------------------------------------*/
// ** Audio.pauseSE()
/*----------------------------------------------------------------------------*/
void Audio::pauseSE() {
// Pause SE
m_SE.pause();
}
/*----------------------------------------------------------------------------*/
// ** Audio.stopSE()
/*----------------------------------------------------------------------------*/
void Audio::stopSE() {
// Stop SE
m_SE.stop();
}
/*----------------------------------------------------------------------------*/
// ** Audio.fadeSE(volume, duration, memorize = false)
/*----------------------------------------------------------------------------*/
void Audio::fadeSE(float volume, float duration) {
// Set SE fade volume and duration
m_SEFadeV = volume;
m_SEFadeD = duration;
}
/*----------------------------------------------------------------------------*/
// ** Audio.reset()
/*----------------------------------------------------------------------------*/
void Audio::reset() {
m_BGM.stop();
m_BGS.stop();
m_ME.stop();
m_SE.stop();
}
/*----------------------------------------------------------------------------*/
// ** Audio.fadeAll(duration)
/*----------------------------------------------------------------------------*/
void Audio::fadeAll(float volume, float duration) {
fadeBGM(volume, duration);
fadeBGS(volume, duration);
fadeME(volume, duration);
fadeSE(volume, duration);
}
/*----------------------------------------------------------------------------*/
// ** Audio.update()
/*----------------------------------------------------------------------------*/
void Audio::update() {
// End method unless a second has passed
if (m_timeME.getElapsedTime() < sf::seconds(0.1f)) { return; }
// Update audio
updateBGM();
updateBGS();
updateME();
updateSE();
// Update buffers
updateBuffers();
// Restart tick
m_timeME.restart();
}
/*----------------------------------------------------------------------------*/
// ** Audio.updateBGM()
/*----------------------------------------------------------------------------*/
void Audio::updateBGM() {
// End method if no music is playing
if (m_BGM.getStatus() == sf::Music::Stopped) { return;}
// End method if not fading
if (m_BGMFadeD <= 0.0) { return; }
// Decrease wait duration
m_BGMFadeD -= 0.1f;
// If duration is 0
if (m_BGMFadeD <= 0.0) {
// Reset fade to 0
m_BGMFadeV = m_BGMFadeD = 0.0;
// End function
return;
}
// If fade volume is more than ME volume
if (m_BGMFadeV > m_BGM.getVolume()) {
// Get volume increment
float vol = (abs(m_BGMFadeV - m_BGM.getVolume()) / m_BGMFadeD);
// Increase volume
m_BGM.setVolume(m_BGM.getVolume() + vol);
// If fade volume is less than ME volume
} else if (m_BGMFadeV < m_BGM.getVolume()) {
// Get volume increment
float vol = (abs(m_BGM.getVolume() - m_BGMFadeV) / m_BGMFadeD);
// Decrease volume
m_BGM.setVolume(m_BGM.getVolume() - vol);
} else if (m_BGMFadeD <= 0.0) {
// Reset fade to 0
m_BGMFadeV = m_BGMFadeD = 0.0;
// End function
return;
}
}
/*----------------------------------------------------------------------------*/
// ** Audio.updateBGS()
/*----------------------------------------------------------------------------*/
void Audio::updateBGS() {
// End method if no music is playing
if (m_BGS.getStatus() == sf::Music::Stopped) { return;}
// End method if not fading
if (m_BGSFadeD <= 0.0) { return; }
// Decrease wait duration
m_BGSFadeD -= 0.1f;
// If duration is 0
if (m_BGSFadeD <= 0.0) {
// Reset fade to 0
m_BGSFadeV = m_BGSFadeD = 0.0;
// End function
return;
}
// If fade volume is more than ME volume
if (m_BGSFadeV > m_BGS.getVolume()) {
// Get volume increment
float vol = (abs(m_BGSFadeV - m_BGS.getVolume()) / m_BGSFadeD);
// Increase volume
m_BGS.setVolume(m_BGS.getVolume() + vol);
// If fade volume is less than ME volume
} else if (m_BGSFadeV < m_BGS.getVolume()) {
// Get volume increment
float vol = (abs(m_BGS.getVolume() - m_BGSFadeV) / m_BGSFadeD);
// Decrease volume
m_BGS.setVolume(m_BGS.getVolume() - vol);
} else if (m_BGSFadeD <= 0.0) {
// Reset fade to 0
m_BGSFadeV = m_BGSFadeD = 0.0;
// End function
return;
}
}
/*----------------------------------------------------------------------------*/
// ** Audio.updateME()
/*----------------------------------------------------------------------------*/
void Audio::updateME() {
// End method if no music is playing
if (m_ME.getStatus() == sf::Music::Stopped) { return;}
// Do internal music effect update
updateMusicEffect();
//if (m_MEFadeD <= 0.0) { return; }
// Decrease wait duration
m_MEFadeD -= 0.1f;
// If duration is 0
if (m_MEFadeD <= 0.0) {
// Reset fade to 0
m_MEFadeV = m_MEFadeD = 0.0;
// End function
return;
}
// If fade volume is more than ME volume
if (m_MEFadeV > m_ME.getVolume()) {
// Get volume increment
float vol = (abs(m_MEFadeV - m_ME.getVolume()) / m_MEFadeD);
// Increase volume
m_ME.setVolume(m_ME.getVolume() + vol);
// If fade volume is less than ME volume
} else if (m_MEFadeV < m_ME.getVolume()) {
// Get volume increment
float vol = (abs(m_ME.getVolume() - m_MEFadeV) / m_MEFadeD);
// Decrease volume
m_ME.setVolume(m_ME.getVolume() - vol);
} else if (m_MEFadeD <= 0.0) {
// Reset fade to 0
m_MEFadeV = m_MEFadeD = 0.0;
// End function
return;
}
}
/*----------------------------------------------------------------------------*/
// ** Audio.updateMusicEffect()
/*----------------------------------------------------------------------------*/
void Audio::updateMusicEffect() {
// Get current and total duration
float sec = m_ME.getPlayingOffset().asSeconds();
float all = m_ME.getDuration().asSeconds();
// If phase 1 : Fade Out
if (m_phaseME == 1 && sec < 1.0) {
// Memorize volumes
m_BGMLastV = m_BGM.getVolume();
m_BGSLastV = m_BGS.getVolume();
m_SELastV = ((m_SoundV.size() > 0) ? m_SoundV[0].getVolume() : 80.0 );
// Trigger a rolling fade out of audio assets
fadeBGM(10.0, m_MEFadeOutT);
fadeBGS(10.0, m_MEFadeOutT);
fadeSE(10.0, m_MEFadeOutT);
// Set next phase : play the body of the music effect
m_phaseME = 2;
// End function
return;
// If phase 2 : Play Body
} else if (m_phaseME == 2 &&
(sec > 1.0 && sec < (all * 0.9))) {
// Set next phase : fade back in
m_phaseME = 3;
// End function
return;
// If phase 3 : Fade In
} else if (m_phaseME == 3 && sec > (all * 0.9)) {
// Trigger a rolling fade in of audio assets
fadeBGM(m_BGMLastV, m_MEFadeInT);
fadeBGS(m_BGSLastV, m_MEFadeInT);
fadeSE(m_SELastV, m_MEFadeInT);
// Set next phase : DONE
m_phaseME = 0;
// End function
return;
}
}
/*----------------------------------------------------------------------------*/
// ** Audio.updateSE()
/*----------------------------------------------------------------------------*/
void Audio::updateSE() {
// Update each sound
for(unsigned int i = 0; i < m_SoundV.size() ; i++){ updateSE(i);}
}
/*----------------------------------------------------------------------------*/
// ** Audio.updateSE()
/*----------------------------------------------------------------------------*/
void Audio::updateSE(int i) {
// End method if no music is playing
if (m_SoundV[i].getStatus() == sf::Sound::Stopped) {
// Erase sound at iterator position
m_SoundV.erase(m_SoundV.begin()+i);
// Print to console
return;
}
// End method if not fading
if (m_SEFadeD <= 0.0) { return; }
// Decrease wait duration
m_SEFadeD -= 0.1f;
// If duration is 0
if (m_SEFadeD <= 0.0) {
// Reset fade to 0
m_SEFadeV = m_SEFadeD = 0.0;
// End function
return;
}
// If fade volume is more than SE volume
if (m_SEFadeV > m_SoundV[i].getVolume()) {
// Get volume increment
float vol = (abs(m_SEFadeV - m_SoundV[i].getVolume()) / m_SEFadeD);
// Increase volume
m_SoundV[i].setVolume(m_SoundV[i].getVolume() + vol);
// If fade volume is less than SE volume
} else if (m_SEFadeV < m_SoundV[i].getVolume()) {
// Get volume increment
float vol = (abs(m_SoundV[i].getVolume() - m_SEFadeV) / m_SEFadeD);
// Decrease volume
m_SoundV[i].setVolume(m_SoundV[i].getVolume() - vol);
// If fade duration is less than or equal to 0 seconds
} else if (m_SEFadeD <= 0.0) {
// Reset fade and volume to 0
m_SEFadeV = m_SEFadeD = 0.0;
// End function
return;
}
}
/*----------------------------------------------------------------------------*/
// ** Audio.updateBuffers();
/*----------------------------------------------------------------------------*/
void Audio::updateBuffers() {
// End function if no buffers
if (m_BufferV.empty()) { return; }
// Clear buffers if enough time has passed by
if (m_timeSE.getElapsedTime() >= m_timeSEMax) {
// Clear buffers and restart timer
m_BufferV.clear();
m_timeSE.restart();
}
}
} // End of rgl::Audio
Author's Notes
Notes : Classes and Memory
Content Hidden
Since it was built to RGSS standards, the system uses 2x sf::Music (1 BGM, 1 BGS) and 1x sf::Sound (1 ME). Plus 2x sf::Clocks for internal timing. These are always loaded in memory so long as the rgl::Audio class exists in your project.
Sound Effects (SE) use sf::Sound and sf::SoundBuffers which are dynamically created and destroyed within an std::deque class. The sound buffers are automatically cleared after the maximum (sound.getDuration() * 2). The length of the latest, and longest, sound effect is doubled and then the buffers are cleared after that if Audio.play_se hasn't been called again.
A better memory protocol could probably be implemented in the future but it is important that, by hook or by crook, sounds and buffers are removed when not in use because they take up memory.
If you shorten the duration, some sound effects may not play all the way though. I double it to give the sounds room to run (it takes the time of the longest sound and doubles it.)
Lengthening the duration of time between a buffer creation and destruction probably wouldn't hurt, as buffers are meant to be reused. I keep my buffers on a short leash.
Sound Effects (SE) use sf::Sound and sf::SoundBuffers which are dynamically created and destroyed within an std::deque class. The sound buffers are automatically cleared after the maximum (sound.getDuration() * 2). The length of the latest, and longest, sound effect is doubled and then the buffers are cleared after that if Audio.play_se hasn't been called again.
A better memory protocol could probably be implemented in the future but it is important that, by hook or by crook, sounds and buffers are removed when not in use because they take up memory.
If you shorten the duration, some sound effects may not play all the way though. I double it to give the sounds room to run (it takes the time of the longest sound and doubles it.)
Lengthening the duration of time between a buffer creation and destruction probably wouldn't hurt, as buffers are meant to be reused. I keep my buffers on a short leash.
Notes : What Could Be Improved?
Content Hidden
This is an early implementation so I'm sure it's not perfect. I'm not a master C++ wizard so I'm still learning.
First thing that could be improved is applying DRY (Don't Repeat Yourself) and standardizing the functionality. I'll probably write a new version using templates or typedef calls in the future because I can't stand redundant repetition but, for now, it should work fine I think.
There is not support for multiple BGM/BGS. I tried to keep it as close to original RGSS* specification as possible but I may add that support in the future. You'd have to change the single member variables to std::vector or std::list or some other C++ container style. I would like to add that but it's not exactly high on my priority list at the moment.
For anybody aiming for cross-platform compatibility I would avoid using Win32API / Win32::API and do a direct C++/Ruby FFI (Foreign Function Interface). I don't know how to set these things up yet but that's probably going to be the next step...
(NOTE: You can probably do it with RbInline or RICE or some other system. I'm still studying this stuff so I won't pretend to know the details - YET.)
First thing that could be improved is applying DRY (Don't Repeat Yourself) and standardizing the functionality. I'll probably write a new version using templates or typedef calls in the future because I can't stand redundant repetition but, for now, it should work fine I think.
There is not support for multiple BGM/BGS. I tried to keep it as close to original RGSS* specification as possible but I may add that support in the future. You'd have to change the single member variables to std::vector or std::list or some other C++ container style. I would like to add that but it's not exactly high on my priority list at the moment.
For anybody aiming for cross-platform compatibility I would avoid using Win32API / Win32::API and do a direct C++/Ruby FFI (Foreign Function Interface). I don't know how to set these things up yet but that's probably going to be the next step...
(NOTE: You can probably do it with RbInline or RICE or some other system. I'm still studying this stuff so I won't pretend to know the details - YET.)
Notes : About (lack of) rbSFML Bindings
I am well aware that there is a SFML bindings for Ruby (rbSFML). It doesn't look like it has been updated in 6 years and I don't know if anybody still uses or supports it, nor do I know how stable it is. The SFML forum doesn't have a sub-forum for it in their bindings so I can only assume rbSFML may not be a stable library? I made the personal choice to do this without rbSFML. I would like to learn for myself how to extend Ruby with C++ so it was a personal choice to do it in C++.
Thank you for checking it out, feel free to lend me a hand if you know anything about this stuff.
Terms and Conditions
End user is responsible for figuring out how to compile and use this themselves. I will only be providing limited support and future updates.
Free to use in commercial and non-commercial projects, you don't even have to credit me (but I do like credits -_^).
SFML has its own terms under the ZLIB/PNG License. SFML is an open source multimedia library written by Laurent Gomilla and a team of programming ninjas.