ST_engine  0.3-ALPHA
audio_manager.cpp
1 /* This file is part of the "ST" project.
2  * You may use, distribute or modify this code under the terms
3  * of the GNU General Public License version 2.
4  * See LICENCE.txt in the root directory of the project.
5  *
6  * Author: Maxim Atanasov
7  * E-mail: maxim.atanasov@protonmail.com
8  */
9 
10 #include <audio_manager.hpp>
11 
12 static bool singleton_initialized = false;
13 
19 void audio_manager::update_task(void *arg) {
20  auto mngr = static_cast<audio_manager *>(arg);
21  mngr->handle_messages();
22 }
23 
30 audio_manager::audio_manager(task_manager &gTask_manager, message_bus &gMessageBus) : gMessage_bus(gMessageBus),
31  gTask_manager(gTask_manager) {
32  if (singleton_initialized) {
33  throw std::runtime_error("The audio manager cannot be initialized more than once!");
34  } else {
35  singleton_initialized = true;
36  }
37 
38  if (SDL_Init(SDL_INIT_AUDIO) < 0) {
39  fprintf(stderr, "Failed to initialize SDL audio subsystem: %s\n", SDL_GetError());
40  exit(1);
41  }
42  if (Mix_OpenAudio(48000, AUDIO_F32SYS, 2, 1024) == -1) {
43  fprintf(stderr, "Failure to open audio device\n");
44  exit(1);
45  }
46  if (Mix_Init(MIX_INIT_OGG) == 0) {
47  fprintf(stderr, "Failed to initialize SDL_Mixer: %s\n", Mix_GetError());
48  exit(1);
49  }
50  Mix_Volume(-1, chunk_volume);
51  Mix_VolumeMusic(music_volume);
52  Mix_AllocateChannels(32);
53 
54  //subscribe to messages
55  gMessage_bus.subscribe(PLAY_SOUND, &msg_sub);
56  gMessage_bus.subscribe(PLAY_MUSIC, &msg_sub);
57  gMessage_bus.subscribe(STOP_MUSIC, &msg_sub);
58  gMessage_bus.subscribe(SET_AUDIO_ENABLED, &msg_sub);
59  gMessage_bus.subscribe(MUSIC_ASSETS, &msg_sub);
60  gMessage_bus.subscribe(CHUNKS_ASSETS, &msg_sub);
61  gMessage_bus.subscribe(STOP_ALL_SOUNDS, &msg_sub);
62  gMessage_bus.subscribe(SET_MUSIC_VOLUME, &msg_sub);
63  gMessage_bus.subscribe(SET_SOUNDS_VOLUME, &msg_sub);
64  gMessage_bus.subscribe(PAUSE_MUSIC, &msg_sub);
65 }
66 
71 void audio_manager::handle_messages() {
72  message *temp = msg_sub.get_next_message();
73  while (temp != nullptr) {
74  switch (temp->msg_name) {
75  case PLAY_SOUND: {
76  auto data = temp->base_data0;
77  uint16_t name = data & 0x0000ffffU;
78  uint8_t volume = (data >> 16U) & 0x000000ffU;
79  int8_t loops = (data >> 24U) & 0x000000ffU;
80  play_sound(name, volume, loops);
81  break;
82  }
83  case PLAY_MUSIC: {
84  auto data = temp->base_data0;
85  uint16_t name = data & 0x0000ffffU;
86  uint8_t volume = (data >> 16U) & 0x000000ffU;
87  int8_t loops = (data >> 24U) & 0x000000ffU;
88  play_music(name, volume, loops);
89  break;
90  }
91  case STOP_MUSIC: {
92  stop_music();
93  gMessage_bus.send_msg(new message(LOG_SUCCESS, make_data<std::string>("Music stopped")));
94  break;
95  }
96  case PAUSE_MUSIC: {
97  pause_music();
98  gMessage_bus.send_msg(new message(LOG_SUCCESS, make_data<std::string>("Music paused")));
99  break;
100  }
101  case STOP_ALL_SOUNDS: {
102  stop_channels();
103  gMessage_bus.send_msg(new message(LOG_SUCCESS, make_data<std::string>("Sounds stopped")));
104  break;
105  }
106  case SET_AUDIO_ENABLED: {
107  auto arg = static_cast<bool>(temp->base_data0);
108  if (!arg) {
109  gMessage_bus.send_msg(new message(LOG_SUCCESS, make_data<std::string>("Audio muted")));
110  mute();
111  } else {
112  unmute();
113  gMessage_bus.send_msg(new message(LOG_SUCCESS, make_data<std::string>("Audio unmuted")));
114  }
115  gMessage_bus.send_msg(new message(AUDIO_ENABLED, arg));
116  break;
117  }
118  case MUSIC_ASSETS:
119  music_ptr = *static_cast<ska::bytell_hash_map<uint16_t, Mix_Music *> **>(temp->get_data());
120  break;
121  case CHUNKS_ASSETS:
122  chunks_ptr = *static_cast<ska::bytell_hash_map<uint16_t, Mix_Chunk *> **>(temp->get_data());
123  break;
124  case SET_SOUNDS_VOLUME: {
125  set_chunk_volume(static_cast<uint8_t>(temp->base_data0));
126  if (!muted) {
127  gMessage_bus.send_msg(new message(SOUNDS_VOLUME_LEVEL, chunk_volume));
128  }
129  break;
130  }
131  case SET_MUSIC_VOLUME: {
132  set_music_volume(static_cast<uint8_t>(temp->base_data0));
133  if (!muted) {
134  gMessage_bus.send_msg(new message(MUSIC_VOLUME_LEVEL, music_volume));
135  }
136  break;
137  }
138  }
139  delete temp;
140  temp = msg_sub.get_next_message();
141  }
142 }
143 
148  gTask_manager.start_task_lockfree(new ST::task(update_task, this, nullptr));
149 }
150 
151 
156 void audio_manager::set_chunk_volume(uint8_t arg) {
157  chunk_volume = arg;
158  chunk_playback_volume_ratio = static_cast<float>(MIX_MAX_VOLUME) / static_cast<float>(chunk_volume);
159  if (!muted) {
160  Mix_Volume(-1, chunk_volume);
161  }
162 }
163 
168 void audio_manager::set_music_volume(uint8_t arg) {
169  music_volume = arg;
170  music_playback_volume_ratio = static_cast<float>(MIX_MAX_VOLUME) / static_cast<float>(music_volume);
171  if (!muted) {
172  Mix_VolumeMusic(music_volume);
173  }
174 }
175 
179 void audio_manager::mute() {
180  Mix_Volume(-1, 0);
181  Mix_VolumeMusic(0);
182  muted = true;
183 }
184 
188 void audio_manager::unmute() {
189  Mix_Volume(-1, chunk_volume);
190  Mix_VolumeMusic(music_volume);
191  muted = false;
192 }
193 
200 void audio_manager::play_sound(uint16_t arg, uint8_t volume, int8_t loops) const {
201  auto data = chunks_ptr->find(arg);
202  auto chunk = reinterpret_cast<Mix_Chunk *>((data != chunks_ptr->end()) * reinterpret_cast<uint64_t>(data->second));
203  if (!muted) { //null-check not necessary in the newest version of SDL_Mixer
204  Mix_VolumeChunk(chunk, static_cast<int>(static_cast<float>(volume) / chunk_playback_volume_ratio));
205  }
206  if (Mix_PlayChannel(-1, chunk, loops) == -1) {
207  gMessage_bus.send_msg(
208  new message(LOG_ERROR, make_data<std::string>("Mix_PlayChannel Error " + std::string(Mix_GetError()))));
209  }
210 }
211 
218 void audio_manager::play_music(uint16_t arg, uint8_t volume, int8_t loops) const {
219  auto data = music_ptr->find(arg);
220  auto music = reinterpret_cast<Mix_Music *>((data != music_ptr->end()) * reinterpret_cast<uint64_t>(data->second));
221  if (!muted) {
222  Mix_VolumeMusic(static_cast<int>(static_cast<float>(volume) / music_playback_volume_ratio));
223  }
224  if (Mix_PlayMusic(music, loops) == -1) {
225  gMessage_bus.send_msg(
226  new message(LOG_ERROR, make_data<std::string>("Mix_PlayMusic Error " + std::string(Mix_GetError()))));
227  }
228 }
229 
233 void audio_manager::stop_music() {
234  Mix_HaltMusic();
235 }
236 
240 void audio_manager::pause_music() {
241  Mix_PauseMusic();
242 }
243 
247 void audio_manager::stop_channels() {
248  Mix_HaltChannel(-1);
249 }
250 
255  Mix_CloseAudio();
256  SDL_QuitSubSystem(SDL_INIT_AUDIO);
257  Mix_Quit();
258  singleton_initialized = false;
259 }
An object representing a task to be run by the task manager.
Definition: task.hpp:24
This object is responsible for playing sounds and music.
audio_manager(task_manager &tsk_mngr, message_bus &gMessageBus)
The central messaging system of the engine. All subsystem make extensive use of it.
Definition: message_bus.hpp:29
void subscribe(uint8_t msg, subscriber *sub)
Definition: message_bus.cpp:71
void send_msg(message *msg)
Definition: message_bus.cpp:19
A message object passed around in the message bus. Holds anything created with make_data<>().
Definition: message.hpp:21
void * get_data() const
Definition: message.hpp:80
message * get_next_message()
Definition: subscriber.hpp:39
The Task Manager handles all things multi-threaded in the engine.
void start_task_lockfree(ST::task *arg)