Toast Engine
Loading...
Searching...
No Matches
music_player.hpp
Go to the documentation of this file.
1
8#pragma once
9#include "../assets.hpp"
10
11#include <atomic>
12#include <mutex>
13#include <string>
15#include <toast/export.hpp>
16#include <toast/world/node.hpp>
17#include <unordered_map>
18#include <vector>
19
20namespace toast {
21
30class TOAST_API [[ToastNode, Color("Beige"), Icon("AudioStreamMP3")]] MusicPlayer : public Node {
31public:
32 void play(int track_index = 0, float fade_in = 0.0f);
33 void stop(bool allow_fadeout = true);
34 void crossfadeTo(int track_index, float duration);
35 void pause(bool value);
36 void keyOff(int track_index = -1);
37
38 [[nodiscard]]
39 auto getTimelinePosition(int track_index = 0) const -> int;
40 void setTimelinePosition(int ms, int track_index = 0);
41
42 void setParameter(std::string_view name, float value, int track_index = -1);
43 void setParameter(std::string_view name, bool value, int track_index = -1);
44
45 void masterVolume(float value);
46 void masterPitch(float value);
47
48private:
49 void updateInspectorMessages() override;
50 void onEnable();
51 void onDisable();
52 void tick();
53 void destroy();
54
55public:
56 struct ActiveTrack {
57 uint64_t instance_id = 0;
58 int track_index = -1;
59 float current_vol = 0.0f;
60 float target_vol = 1.0f;
61 float fade_duration = 0.0f;
62 float fade_elapsed = 0.0f;
63 };
64
65 // public so the static FMOD callback can read it without needing friend
66 //
67 // FMOD's event callback fires from its own Studio update thread, asynchronously from the game thread
68 // that stops/destroys the player. Unregistering the callback (SetCallback(nullptr)) before destruction
69 // only prevents FUTURE dispatches - it doesn't guarantee a callback already in flight on FMOD's thread
70 // has finished, so `player` must be checked atomically rather than trusted as always valid. Instances
71 // of this struct are deliberately never freed (see startTrack()) so the raw pointer FMOD holds via
72 // SetUserData is always safe to dereference, even after the MusicPlayer it refers to is long gone
73 struct CallbackData {
74 std::atomic<MusicPlayer*> player {nullptr};
75 uint64_t instance_id = 0;
76 };
77
78 enum class CbType : uint8_t {
79 beat,
80 marker,
81 stopped
82 };
83
84 struct QueuedCb {
85 CbType type = CbType::beat;
86 uint64_t instance_id = 0;
87 int bar = 0;
88 int beat = 0;
89 int position_ms = 0;
90 float tempo = 0.0f;
91 int sig_upper = 4;
92 int sig_lower = 4;
93 std::string marker_name;
94 };
95
96 struct ParamID {
97 unsigned int data1 = 0;
98 unsigned int data2 = 0; // FMOD caches parameter IDs so we don't string-lookup every frame
99 };
100
101 void queueCallback(const QueuedCb& cb);
102
103 signals::Signal<std::string_view> audio_started;
104 signals::Signal<std::string_view> audio_stopped;
105 signals::Signal<bool> audio_paused;
106
107private:
108 void startTrack(int track_index, float fade_in);
109 void stopTrack(ActiveTrack& at, bool allow_fadeout);
110
111 [[Reflect, Name("Tracks")]]
112 std::vector<assets::Handle<assets::AudioEvent>> m_tracks;
113
114 [[Reflect]]
115 bool m_play_on_enable = true;
116
117 [[Reflect, Range(0.0, 1.0)]]
118 float m_volume = 1.0f;
119
120 [[Reflect, Range(0.5, 2.0)]]
121 float m_pitch = 1.0f;
122
123 std::vector<ActiveTrack> m_active_tracks;
124 // Each CallbackData is individually heap-allocated and deliberately never freed (see CallbackData's own
125 // comment for why) - this vector just tracks the pointers for our own bookkeeping, so reallocating IT is
126 // harmless; the addresses FMOD actually holds via SetUserData never move
127 std::vector<CallbackData*> m_callback_data;
128
129 std::mutex m_cb_mutex;
130 std::vector<QueuedCb> m_pending_cbs;
131
132 std::unordered_map<uint64_t, std::unordered_map<std::string, ParamID>>
133 m_param_ids;
134};
135
136}
Manages multi-track music with smooth crossfades between tracks.
Definition music_player.hpp:30
auto name() const noexcept -> std::string_view
Display name. Siblings may share one - this is not an identifier.
Definition node.cpp:45
Definition reflect.hpp:244
Base object type for the scene graph.
Definition music_player.hpp:56
float fade_elapsed
current_vol/target_vol and fade_duration/elapsed drive the crossfade lerp
Definition music_player.hpp:62
Definition music_player.hpp:73
Definition music_player.hpp:96
Definition music_player.hpp:84
int bar
0-based bar and beat for musical timing
Definition music_player.hpp:87
int sig_upper
time signature numerator and denominator
Definition music_player.hpp:91