audio, sfx and bgm classes; filesystem path added as type to json lib

This commit is contained in:
Frank DeMarco 2020-09-11 18:01:27 -04:00
parent be360b8a47
commit 013f8279d4
11 changed files with 302 additions and 27 deletions

View File

@ -13,11 +13,15 @@
screen resolution, debug display, loading wheel animation, shadowed sprite,
separate update and draw, sprite movement cage, multiple windows, multiple
renderers, node children list, node animations list, copy constructor for all
base classes, private and public class members, pixel class iterator, sprite
movement history, input history, use seconds instead of milliseconds, store
a node's animations in list, frame object for sprite class, inline short
functions, add box2d to library, load in separate thread and display progress,
add anchor to box class
base classes (esp. sprite, audio), private and public class members, pixel
class iterator, sprite movement history, input history, use seconds instead of
milliseconds, store a node's animations in list, frame object for sprite
class, inline short functions, add box2d to library, load in separate thread
and display progress, add anchor to box class, add comments to code, generate
documentation from comments, get resource function, automatically update
animations, add arguments list to animation call, queue multiple calls to
animation, print list of chunk and music decoders available, allow nodes that
aren't connected to root
:) SWEATY HANDS :) OILY SNACKS :) AND BAD HYGIENE :)

140
src/Audio.cpp Normal file
View File

@ -0,0 +1,140 @@
#include "SDL.h"
#include "Box.hpp"
#include "Configuration.hpp"
#include "Display.hpp"
#include "Audio.hpp"
BGM::BGM(Node* parent) : Node(parent) {}
BGM::BGM() : BGM(nullptr) {}
BGM::BGM(Node* parent, const fs::path& path) : BGM(parent)
{
load_music(path);
}
BGM::BGM(const fs::path& path) : BGM(nullptr, path) {}
void BGM::load_music(const fs::path& path)
{
music = Mix_LoadMUS(path.c_str());
}
void BGM::set_volume(float v)
{
volume = v;
}
void BGM::play(int loops)
{
Mix_VolumeMusic(std::round(volume * 127));
Mix_PlayMusic(music, loops);
}
BGM::~BGM()
{
Mix_FreeMusic(music);
}
SoundEffect::SoundEffect(Node* parent) : Node(parent) {}
SoundEffect::SoundEffect() : SoundEffect(nullptr) {}
SoundEffect::SoundEffect(Node* parent, const fs::path& path) : SoundEffect(parent)
{
load_chunk(path);
}
SoundEffect::SoundEffect(const fs::path& path) : SoundEffect(nullptr, path) {}
void SoundEffect::load_chunk(const fs::path& path)
{
chunk = Mix_LoadWAV(path.c_str());
Mix_VolumeChunk(chunk, std::round(volume * 127));
}
void SoundEffect::play(float location)
{
play();
Box window_box = get_window_box();
location = std::clamp(location, window_box.get_left(), window_box.get_right());
float location_relative = (location - window_box.get_left()) / window_box.get_w();
int angle = 270 - location_relative * 180;
Mix_SetPosition(channel, angle, 0);
}
void SoundEffect::play()
{
channel = Mix_PlayChannel(-1, chunk, loops);
}
void SoundEffect::play_after(float delay)
{
play_animation.play_once(delay);
}
void SoundEffect::set_volume(float v)
{
volume = v;
Mix_VolumeChunk(chunk, std::round(volume * 127));
}
void SoundEffect::set_loop_count(int count)
{
loops = count;
}
void SoundEffect::set_loop_forever()
{
loops = -1;
}
bool SoundEffect::is_playing(bool include_delay)
{
if (include_delay)
{
if (play_animation.is_playing())
{
return true;
}
}
for (int channel_ii = 0; channel_ii < Mix_GroupAvailable(-1); channel_ii++)
{
if (Mix_GetChunk(channel_ii) == chunk)
{
return true;
}
}
return false;
}
void SoundEffect::update()
{
play_animation.update();
}
SoundEffect::~SoundEffect()
{
Mix_FreeChunk(chunk);
}
Audio::Audio(Node* parent) : Node(parent) {}
void Audio::load_sfx()
{
load_directory(get_configuration()["audio"]["default-sfx-root"], sfx, this);
}
void Audio::load_bgm()
{
load_directory(get_configuration()["audio"]["default-bgm-root"], bgm, this);
}
void Audio::update()
{
for (auto& [name, sound_effect] : sfx)
{
sound_effect.update();
}
}

89
src/Audio.hpp Normal file
View File

@ -0,0 +1,89 @@
#ifndef Audio_h_
#define Audio_h_
#include <regex>
#include <map>
#include "SDL_mixer.h"
#include "filesystem.hpp"
#include "Node.hpp"
#include "Animation.hpp"
#include "extension.hpp"
struct BGM : Node
{
Mix_Music* music;
float volume = 1.0f;
BGM();
BGM(Node*);
BGM(Node*, const fs::path&);
BGM(const fs::path&);
void load_music(const fs::path&);
void set_volume(float);
void play(int = -1);
~BGM();
};
struct SoundEffect : Node
{
Mix_Chunk* chunk = nullptr;
int channel, loops = 0;
float volume = 1.0f;
Animation play_animation = Animation(&SoundEffect::play, this);
SoundEffect(Node*);
SoundEffect();
SoundEffect(Node*, const fs::path&);
SoundEffect(const fs::path&);
void load_chunk(const fs::path&);
void play(float);
void play();
void play_after(float);
void set_volume(float);
void set_loop_count(int);
void set_loop_forever();
bool is_playing(bool = false);
void update();
~SoundEffect();
};
struct Audio : Node
{
std::map<std::string, SoundEffect> sfx;
std::map<std::string, BGM> bgm;
Audio(Node*);
void load_sfx();
void load_bgm();
void update();
template <typename T>
void load_directory(const fs::path& root, std::map<std::string, T>& storage, Node* parent = nullptr)
{
SDL_Log("looking for audio files in %s", root.c_str());
if (fs::exists(root))
{
for (const fs::path& path : sfw::glob(root / ".*"))
{
std::regex pattern("([^.]+).*$");
std::smatch match;
std::string basename = path.filename();
if (std::regex_match(basename, match, pattern))
{
std::string key = match[1].str();
SDL_Log("loading %s as %s", path.c_str(), key.c_str());
storage.try_emplace(key, parent, path);
}
}
}
}
};
#endif

View File

@ -51,6 +51,10 @@ void Configuration::set_defaults()
{"render-test-spacing", 2},
{"render driver", "opengl"}
};
sys_config["audio"] = {
{"default-sfx-root", "resource/sfx"},
{"default-bgm-root", "resource/bgm"}
};
sys_config["recording"] = {
{"enabled", false},
{"screenshot-prefix", "screenshot-"},

View File

@ -24,4 +24,39 @@ struct Configuration : Node
};
namespace glm
{
template <typename T>
void to_json(nlohmann::json& j, const vec<2, T, defaultp>& v)
{
j = nlohmann::json{v.x, v.y};
}
template <typename T>
void from_json(const nlohmann::json& j, vec<2, T, defaultp>& v)
{
j.at(0).get_to(v.x);
j.at(1).get_to(v.y);
}
}
#if defined(__MINGW32__)
namespace std::experimental::filesystem
#else
namespace std::filesystem
#endif
{
template <typename T>
void to_json(nlohmann::json& j, const path& p)
{
j = nlohmann::json{p};
}
template <typename T>
void from_json(const nlohmann::json& j, path& p)
{
j.at(0).get_to(p);
}
}
#endif

View File

@ -145,6 +145,8 @@ Game::Game()
SDL_Log("Found audio capture device %i: %s", ii,
SDL_GetAudioDeviceName(ii, SDL_TRUE));
}
audio.load_sfx();
audio.load_bgm();
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
SDL_Log("big endian");
#else
@ -511,6 +513,11 @@ Input& Game::get_input()
return input;
}
Audio& Game::get_audio()
{
return audio;
}
glm::vec2 Game::weight(glm::vec2 motion)
{
return {weight(motion.x), weight(motion.y)};
@ -556,7 +563,8 @@ void Game::frame(float ticks)
recorder.update();
}
delegate.dispatch();
get_root()->input.unsuppress_animation.update();
audio.update();
input.unsuppress_animation.update();
update();
framerate_indicator.update();
if (!is_gl_context)

View File

@ -29,6 +29,7 @@
#include "Recorder.hpp"
#include "extension.hpp"
#include "Sprite.hpp"
#include "Audio.hpp"
struct FramerateIndicator : Sprite
{
@ -60,6 +61,7 @@ struct Game : Node
Display display = Display(this);
Recorder recorder = Recorder(this);
Input input = Input(this);
Audio audio = Audio(this);
std::vector<float> frame_length_history;
TTF_Font* bp_mono_font = NULL;
FramerateIndicator framerate_indicator = FramerateIndicator(this);
@ -84,6 +86,7 @@ struct Game : Node
SDL_Renderer* get_renderer();
const Input& get_input() const;
Input& get_input();
Audio& get_audio();
glm::vec2 weight(glm::vec2);
void run();
void frame(float);

View File

@ -4,9 +4,10 @@
#include "Input.hpp"
#include "Box.hpp"
#include "Game.hpp"
#include "Audio.hpp"
#include "Node.hpp"
Node::Node() : Node(NULL) {}
Node::Node() : Node(nullptr) {}
Node::Node(Node* parent) : parent(parent)
{
@ -79,6 +80,11 @@ Input& Node::get_input()
return get_root()->get_input();
}
Audio& Node::get_audio()
{
return get_root()->get_audio();
}
const Game* Node::get_root() const
{
const Node* r = this;
@ -117,10 +123,10 @@ void Node::unsuppress_input()
void Node::print_branch()
{
Node* current = this;
while (current != NULL)
while (current != nullptr)
{
std::cout << current->get_class_name() << " @ " << current;
if (current->parent != NULL)
if (current->parent != nullptr)
{
std::cout << " -> ";
}

View File

@ -15,6 +15,7 @@ struct Delegate;
struct Display;
struct Input;
struct Box;
struct Audio;
struct Node
{
@ -41,6 +42,7 @@ struct Node
SDL_Window* get_window();
const Input& get_input() const;
Input& get_input();
Audio& get_audio();
const Game* get_root() const;
Box get_window_box();
void suppress_input();

View File

@ -269,11 +269,11 @@ void Sprite::toggle_hidden()
{
if (is_hidden())
{
hide();
unhide();
}
else
{
unhide();
hide();
}
}

View File

@ -168,22 +168,6 @@ namespace sfw
}
namespace glm
{
template <typename T>
void to_json(nlohmann::json& j, const vec<2, T, defaultp>& v)
{
j = nlohmann::json{{"x", v.x}, {"y", v.y}};
}
template <typename T>
void from_json(const nlohmann::json& j, vec<2, T, defaultp>& v)
{
j.at(0).get_to(v.x);
j.at(1).get_to(v.y);
}
}
int SDL_SetRenderDrawColor(SDL_Renderer*, const Color&);
int SDL_RenderFillRect(SDL_Renderer*, const Box&);
int lineColor(SDL_Renderer*, const Segment&, const Color&, std::uint8_t = 1);