Wizard Engine
2D cross-platform game engine built around SDL2
 
Loading...
Searching...
No Matches
speaker.cpp
1/*
2 Wizard Engine
3 Copyright (C) 2023-2024 Zana Domán
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
23#define __WIZARD_ENGINE_INTERNAL__
24
30
31std::vector<wze::speaker*> wze::speaker::_instances = {};
32
33void wze::speaker::set_angle([[maybe_unused]] float _) {}
34
35float wze::speaker::angle_offset() const {
36 return 0;
37}
38
39bool wze::speaker::attach_angle() const {
40 return false;
41}
42
43std::vector<wze::speaker*> const& wze::speaker::instances() {
44 return _instances;
45}
46
47std::shared_ptr<wze::sound> const& wze::speaker::sound() const {
48 return _sound;
49}
50
51void wze::speaker::set_sound(std::shared_ptr<wze::sound> const& sound) {
52 _sound = sound;
53}
54
55int8_t wze::speaker::volume() const {
56 return (int8_t)Mix_Volume(_channel, -1);
57}
58
59// NOLINTNEXTLINE(readability-make-member-function-const)
60void wze::speaker::set_volume(int8_t volume) {
61 Mix_Volume(_channel, volume);
62}
63
64float wze::speaker::range() const {
65 return _range;
66}
67
68void wze::speaker::set_range(float range) {
69 _range = range;
70}
71
72bool wze::speaker::auto_panning() const {
73 return _auto_panning;
74}
75
76void wze::speaker::set_auto_panning(bool auto_panning) {
77 _auto_panning = auto_panning;
78}
79
80float wze::speaker::x() const {
81 return _x;
82}
83
84void wze::speaker::set_x(float x) {
85 _x = x;
86}
87
88float wze::speaker::y() const {
89 return _y;
90}
91
92void wze::speaker::set_y(float y) {
93 _y = y;
94}
95
96float wze::speaker::z() const {
97 return _z;
98}
99
100void wze::speaker::set_z(float z) {
101 _z = z;
102}
103
104bool wze::speaker::spatial() const {
105 return _spatial;
106}
107
108void wze::speaker::set_spatial(bool spatial) {
109 _spatial = spatial;
110}
111
112float wze::speaker::x_offset() const {
113 return _x_offset;
114}
115
116void wze::speaker::set_x_offset(float x_offset) {
117 _x_offset = x_offset;
118}
119
120float wze::speaker::y_offset() const {
121 return _y_offset;
122}
123
124void wze::speaker::set_y_offset(float y_offset) {
125 _y_offset = y_offset;
126}
127
128bool wze::speaker::attach_x() const {
129 return _attach_x;
130}
131
132void wze::speaker::set_attach_x(bool attach_x) {
133 _attach_x = attach_x;
134}
135
136bool wze::speaker::attach_y() const {
137 return _attach_y;
138}
139
140void wze::speaker::set_attach_y(bool attach_y) {
141 _attach_y = attach_y;
142}
143
144bool wze::speaker::x_angle_lock() const {
145 return _x_angle_lock;
146}
147
148void wze::speaker::set_x_angle_lock(bool x_angle_lock) {
149 _x_angle_lock = x_angle_lock;
150}
151
152bool wze::speaker::y_angle_lock() const {
153 return _y_angle_lock;
154}
155
156void wze::speaker::set_y_angle_lock(bool y_angle_lock) {
157 _y_angle_lock = y_angle_lock;
158}
159
160bool wze::speaker::playing() const {
161 return (bool)Mix_Playing(_channel);
162}
163
164bool wze::speaker::paused() const {
165 return (bool)Mix_Paused(_channel);
166}
167
168wze::speaker::speaker(std::shared_ptr<wze::sound> const& sound, int8_t volume,
169 float range, bool auto_panning, float x, float y, float z,
170 bool spatial, float x_offset, float y_offset,
171 bool attach_x, bool attach_y, bool x_angle_lock,
172 bool y_angle_lock) {
173 _channel = audio::request_channel();
174 set_sound(sound);
175 set_volume(volume);
176 set_range(range);
177 set_auto_panning(auto_panning);
178 set_x(x);
179 set_y(y);
180 set_z(z);
181 set_spatial(spatial);
182 set_x_offset(x_offset);
183 set_y_offset(y_offset);
184 set_attach_x(attach_x);
185 set_attach_y(attach_y);
186 set_x_angle_lock(x_angle_lock);
187 set_y_angle_lock(y_angle_lock);
188 _instances.push_back(this);
189}
190
191wze::speaker::speaker(speaker const& other) {
192 _channel = audio::request_channel();
193 *this = other;
194 _instances.push_back(this);
195}
196
197wze::speaker::speaker(speaker&& other) noexcept(false) {
198 _channel = audio::request_channel();
199 *this = std::move(other);
200 _instances.push_back(this);
201}
202
203wze::speaker::~speaker() {
204 _instances.erase(std::find(instances().begin(), instances().end(), this));
205 audio::drop_channel(_channel);
206}
207
208wze::speaker& wze::speaker::operator=(speaker const& other) {
209 if (&other != this) {
210 set_sound(other.sound());
211 set_volume(other.volume());
212 set_range(other.range());
213 set_auto_panning(other.auto_panning());
214 set_x(other.x());
215 set_y(other.y());
216 set_z(other.z());
217 set_spatial(other.spatial());
218 set_x_offset(other.x_offset());
219 set_y_offset(other.y_offset());
220 set_attach_x(other.attach_x());
221 set_attach_y(other.attach_y());
222 set_x_angle_lock(other.x_angle_lock());
223 set_y_angle_lock(other.y_angle_lock());
224 }
225
226 return *this;
227}
228
229wze::speaker& wze::speaker::operator=(speaker&& other) noexcept(false) {
230 if (&other != this) {
231 *this = other;
232 std::swap(_channel, other._channel);
233 }
234
235 return *this;
236}
237
238// NOLINTNEXTLINE(readability-make-member-function-const)
239void wze::speaker::play(uint16_t fade_in, uint16_t loops) {
240 if (Mix_FadeInChannel(_channel, sound().get(), loops, fade_in) == -1) {
241 throw exception(Mix_GetError());
242 }
243}
244
245// NOLINTNEXTLINE(readability-make-member-function-const)
246void wze::speaker::pause() {
247 Mix_Pause(_channel);
248}
249
250// NOLINTNEXTLINE(readability-make-member-function-const)
251void wze::speaker::resume() {
252 Mix_Resume(_channel);
253}
254
255// NOLINTNEXTLINE(readability-make-member-function-const)
256void wze::speaker::stop(uint16_t fade_out) {
257 Mix_FadeOutChannel(_channel, fade_out);
258}
259
260// NOLINTNEXTLINE(readability-make-member-function-const)
261void wze::speaker::align_panning() {
262 constexpr uint16_t circle = 360;
263 constexpr uint16_t top = 270;
264 constexpr uint16_t bottom = 90;
265
266 float x_distance;
267 float y_distance;
268 float z_ratio;
269 float distance;
270 int32_t angle;
271 float left;
272 float right;
273
274 x_distance = x() - camera::x();
275 y_distance = y() - camera::y();
276 z_ratio = spatial() ? 1 - abs(z() - camera::z()) / range() : 1;
277 distance = math::length(x_distance, y_distance);
278 angle = (int32_t)roundf(math::to_degrees(
279 math::angle(x_distance, y_distance) - camera::angle())) %
280 circle;
281 if (angle < 0) {
282 angle += circle;
283 }
284
285 if (range() <= distance || z_ratio <= 0) {
286 left = 0;
287 right = 0;
288 } else if (bottom < angle && angle < top) {
289 left = 1 - distance / range();
290 right = powf(left, 2);
291 if (spatial()) {
292 left *= z_ratio;
293 right *= z_ratio;
294 }
295 } else if (top < angle || angle < bottom) {
296 right = 1 - distance / range();
297 left = powf(right, 2);
298 if (spatial()) {
299 right *= z_ratio;
300 left *= z_ratio;
301 }
302 } else {
303 left = z_ratio;
304 right = z_ratio;
305 }
306
307 if (!(bool)Mix_SetPanning(
308 _channel,
309 (uint8_t)roundf(std::numeric_limits<uint8_t>::max() * left),
310 (uint8_t)roundf(std::numeric_limits<uint8_t>::max() * right))) {
311 throw exception(Mix_GetError());
312 }
313}
Subsystem to handle global audio.
Subsystem to handle transformations and spatial projections.
static float angle(float x, float y)
Returns the angle of a vector.
Definition math.cpp:37
static constexpr float to_degrees(float radians)
Converts radians to degrees.
Definition math.hpp:75
static float length(float x, float y)
Returns the length of a vector.
Definition math.cpp:33
Generic exception.
Math modul.
Audio component.