Brenta Engine 1.2
Loading...
Searching...
No Matches
sound.hpp
1// SPDX-License-Identifier: MIT
2// Author: Giovanni Santini
3// Mail: giovanni.santini@proton.me
4// Github: @San7o
5
6#pragma once
7
8#include <brenta/subsystem.hpp>
9
10#include <tenno/memory.hpp>
11
12#include <filesystem>
13
14namespace brenta
15{
16
17// An identifier for a stream
18typedef int Stream;
19
20class SoundManager;
21
22//
23// SoundAsset
24// ----------
25//
26// The sound assets stores all the information of an audio file. You
27// cannot play a `SoundAsset` because it is just data, you can play a
28// `Sound` instead.
29//
30// A sound asset needs to be created from SoundManager::load(...). It
31// uses RAII to automatically deallocate its resources.
32//
34{
35public:
36
37 // Nagetive values are considered invalid
38 using SoundAssetId = int;
39
40 class Builder;
41 friend class SoundManager;
42
43 SoundAsset() = default;
44 SoundAsset(SoundAssetId id)
45 : id(id) {}
46 SoundAsset(SoundAsset&& other)
47 {
48 this->id = other.id;
49 other.id = -1;
50 }
52
53 SoundAssetId get_id() const;
54
55private:
56
57 SoundAssetId id = -1;
58
59};
60
62{
63public:
64
65 Builder& path(const std::filesystem::path& path);
66
67 std::optional<SoundAsset> build();
68
69private:
70
71 std::filesystem::path _path;
72
73};
74
75//
76// Sound
77// -----
78//
79// This class represents an instance of a sound asset that can be
80// played.
81//
82// It uses RAII to automatically allocate / deallocate its stream in
83// from the `SoundManager`.
84//
85// To play the sound you need to have a sound asset first, then you
86// can construct the sound simply by:
87//
88// Sound sound = Sound(my_sound_sset);
89// sound.play();
90//
91class Sound
92{
93public:
94
95 // Nagetive values are considered invalid
96 friend class SoundManager;
97
98 Sound() = default;
99 // A sound is constructed from a sound asset, which you can get from
100 // the AssetManager or SoundManager
101 Sound(const SoundAsset& asset);
102 Sound(Sound&& other)
103 {
104 this->stream = other.stream;
105 other.stream = -1;
106 }
107 Sound(const Sound&) = delete;
108 Sound &operator=(const Sound&) = delete;
109 ~Sound();
110
111 void play();
112 void stop();
113 void set_volume(float v);
114
115private:
116
117 Stream stream = -1;
118
119};
120
121//
122// SoundManager
123// ------------
124//
125// Primary interface to manage all sound resources.
126//
127// The sound manager itself is a light wrapper around a sound
128// `Driver`, which actually implements the entire logic.
129//
131{
132public:
133
134 class Driver;
135 class Builder;
136
137 //
138 // Subsystem interface
139 //
140
141 static const std::string subsystem_name;
142 std::string name() override;
143 bool is_initialized() override;
144 std::expected<void, Subsystem::Error> initialize() override;
145 std::expected<void, Subsystem::Error> terminate() override;
146
147 //
148 // Member functions
149 //
150
151 static SoundManager &instance();
152
153 static std::optional<SoundAsset> load(const std::filesystem::path& path);
154 static void unload(const SoundAsset& sound);
155
156 static std::optional<Stream> request_stream(const SoundAsset& id);
157 static void release_stream(Stream stream);
158
159 static void play(const Sound& sound);
160 static void stop(const Sound& sound);
161 static void set_volume(const Sound& sound, float volume);
162
163private:
164
165 static bool initialized;
166 static tenno::shared_ptr<Driver> driver;
167
168 SoundManager() = default;
169
170};
171
173{
174public:
175
176 virtual ~Driver() = default;
177
178 virtual std::expected<void, std::string> initialize() = 0;
179 virtual std::expected<void, std::string> terminate() = 0;
180
181 virtual std::optional<Stream> request_stream(const SoundAsset& sound) = 0;
182 virtual void release_stream(Stream stream) = 0;
183
184 virtual std::optional<SoundAsset> load(const std::filesystem::path& path) = 0;
185 virtual void unload(const SoundAsset& sound) = 0;
186
187 virtual void play(Stream stream) = 0;
188 virtual void stop(Stream stream) = 0;
189 virtual void set_volume(Stream stream, float volume) = 0;
190
191};
192
194{
195public:
196
197 Builder() = default;
198 ~Builder() = default;
199
200 brenta::Subsystem &build() override;
201
202};
203
204} // namespace brenta