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, screen resolution, debug display, loading wheel animation, shadowed sprite,
separate update and draw, sprite movement cage, multiple windows, multiple separate update and draw, sprite movement cage, multiple windows, multiple
renderers, node children list, node animations list, copy constructor for all renderers, node children list, node animations list, copy constructor for all
base classes, private and public class members, pixel class iterator, sprite base classes (esp. sprite, audio), private and public class members, pixel
movement history, input history, use seconds instead of milliseconds, store class iterator, sprite movement history, input history, use seconds instead of
a node's animations in list, frame object for sprite class, inline short milliseconds, store a node's animations in list, frame object for sprite
functions, add box2d to library, load in separate thread and display progress, class, inline short functions, add box2d to library, load in separate thread
add anchor to box class 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 :) :) 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-test-spacing", 2},
{"render driver", "opengl"} {"render driver", "opengl"}
}; };
sys_config["audio"] = {
{"default-sfx-root", "resource/sfx"},
{"default-bgm-root", "resource/bgm"}
};
sys_config["recording"] = { sys_config["recording"] = {
{"enabled", false}, {"enabled", false},
{"screenshot-prefix", "screenshot-"}, {"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 #endif

View File

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

View File

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

View File

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

View File

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

View File

@ -269,11 +269,11 @@ void Sprite::toggle_hidden()
{ {
if (is_hidden()) if (is_hidden())
{ {
hide(); unhide();
} }
else 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_SetRenderDrawColor(SDL_Renderer*, const Color&);
int SDL_RenderFillRect(SDL_Renderer*, const Box&); int SDL_RenderFillRect(SDL_Renderer*, const Box&);
int lineColor(SDL_Renderer*, const Segment&, const Color&, std::uint8_t = 1); int lineColor(SDL_Renderer*, const Segment&, const Color&, std::uint8_t = 1);