1 #include "ge211_audio.h" 2 #include "ge211_resource.h" 12 using namespace detail;
16 static inline int unit_to_volume(
double unit_volume)
18 return int(unit_volume * MIX_MAX_VOLUME);
21 static inline double volume_to_unit(
int int_volume)
23 return int_volume / double(MIX_MAX_VOLUME);
26 std::shared_ptr<Mix_Music> Music_track::load_(
const std::string& filename)
28 File_resource file_resource(filename);
29 Mix_Music* raw = Mix_LoadMUS_RW(std::move(file_resource).release(), 1);
30 if (raw)
return {raw, &Mix_FreeMusic};
32 throw Mixer_error::could_not_load(filename);
36 : ptr_{load_(filename)}
41 return ptr_ ==
nullptr;
44 Music_track::operator bool()
const 49 std::shared_ptr<Mix_Chunk> Sound_effect::load_(
const std::string& filename)
51 File_resource file_resource(filename);
52 Mix_Chunk* raw = Mix_LoadWAV_RW(std::move(file_resource).release(), 1);
53 if (raw)
return {raw, &Mix_FreeChunk};
55 throw Mixer_error::could_not_load(filename);
59 : ptr_{load_(filename)}
64 return ptr_ ==
nullptr;
67 Sound_effect::operator bool()
const 72 std::unique_ptr<Mixer> Mixer::open_mixer()
74 if (Mix_OpenAudio(MIX_DEFAULT_FREQUENCY,
78 return std::unique_ptr<Mixer>{
new Mixer};
84 : channels_(MIX_CHANNELS)
85 , available_effect_channels_(MIX_CHANNELS)
87 int music_decoders = Mix_GetNumMusicDecoders();
88 info_sdl() <<
"Number of music decoders is " << music_decoders;
89 for (
int i = 0; i < music_decoders; ++i) {
90 info_sdl() <<
" [" << i <<
"] " << Mix_GetMusicDecoder(i);
93 int chunk_decoders = Mix_GetNumChunkDecoders();
94 info_sdl() <<
"Number of chunk decoders is " << chunk_decoders;
95 for (
int i = 0; i < chunk_decoders; ++i) {
96 info_sdl() <<
" [" << i <<
"] " << Mix_GetChunkDecoder(i);
113 switch (music_state_) {
125 current_music_ = std::move(music);
127 if (current_music_) {
136 switch (music_state_) {
142 Mix_FadeInMusicPos(current_music_.ptr_.get(),
161 switch (music_state_) {
175 music_position_.
pause();
187 switch (music_state_) {
189 music_position_.
reset();
196 "Mixer::rewind_music: must be paused");
210 int Mixer::find_empty_channel_()
const 212 auto iter = std::find_if(channels_.begin(),
214 [](
const auto& handle) {
215 return handle.empty();
217 if (iter == channels_.end()) {
218 throw Mixer_error::out_of_channels();
221 return (
int) std::distance(channels_.begin(), iter);
224 void Mixer::poll_channels_()
226 if (current_music_) {
227 if (!Mix_PlayingMusic()) {
228 switch (music_state_) {
234 music_position_.
pause();
235 music_position_.
reset();
240 music_position_.
pause();
247 for (
int channel = 0; channel < channels_.size(); ++channel) {
248 if (channels_[channel] && !Mix_Playing(channel))
250 unregister_effect_(channel);
258 int channel = find_empty_channel_();
259 Mix_Volume(channel, unit_to_volume(volume));
260 Mix_PlayChannel(channel, effect.ptr_.get(), 0);
261 return register_effect_(channel, std::move(effect));
266 switch (ptr_->state) {
272 Mix_Resume(ptr_->channel);
286 switch (ptr_->state) {
296 Mix_Pause(ptr_->channel);
306 switch (ptr_->state) {
311 ptr_->mixer.unregister_effect_(ptr_->channel);
312 Mix_HaltChannel(ptr_->channel);
316 ptr_->mixer.unregister_effect_(ptr_->channel);
317 Mix_HaltChannel(ptr_->channel);
329 for (
const auto& handle : channels_) {
339 for (
const auto& handle : channels_) {
347 return available_effect_channels_;
351 Mixer::register_effect_(
int channel,
Sound_effect effect)
353 assert(!channels_[channel]);
355 --available_effect_channels_;
356 return channels_[channel];
359 void Mixer::unregister_effect_(
int channel)
361 assert(channels_[channel]);
363 channels_[channel] = {};
364 ++available_effect_channels_;
369 return volume_to_unit(Mix_VolumeMusic(-1));
374 Mix_VolumeMusic(unit_to_volume(unit_value));
379 return ptr_ ==
nullptr;
382 Sound_effect_handle::operator bool()
const 390 : ptr_(
std::make_shared<Impl_>(mixer,
std::move(effect), channel))
398 return volume_to_unit(Mix_Volume(ptr_->channel, -1));
404 Mix_Volume(ptr_->channel, unit_to_volume(unit_value));
void rewind_music()
Rewinds the music to the beginning.
void resume_all_effects()
Unpauses all currently-paused effects.
Mixer::State get_state() const
Gets the state of this effect.
Duration reset()
Resets the timer, returning the elapsed time since starting or the most recent reset().
void pause_all_effects()
Pauses all currently-playing effects.
In the process of fading out from playing to paused (for music) or to halted and detached (for sound ...
Sound_effect_handle play_effect(Sound_effect effect, double volume=1.0)
Plays the given effect track on this mixer, at the specified volume.
void set_volume(double unit_value)
Sets the playing sound effect's volume as a number from 0.0 to 1.0.
The game engine namespace.
double get_music_volume() const
Returns the music volume as a number from 0.0 to 1.0.
bool empty() const
Recognizes the empty sound effect track.
Used to control a Sound_effect after it is started playing on a Mixer.
double get_volume() const
Returns the playing sound effect's volume as a number from 0.0 to 1.0.
A music track, which can be attached to the Mixer and played.
A sound effect track, which can be attached to a Mixer channel and played.
double seconds() const
Gets this duration in seconds.
Duration elapsed_time() const
The elapsed time since the start or most recent reset, not counting paused times. ...
void resume()
Unpauses the timer. If the timer is already running, has no effect.
Sound_effect_handle()
Default-constructs the empty sound effect handle.
void pause_music(Duration fade_out=Duration(0))
Pauses the currently attached music, fading out if requested.
void resume_music(Duration fade_in=Duration(0))
Plays the currently attached music from the current saved position, fading in if requested.
int available_effect_channels() const
How many effect channels are currently unused? If this is positive, then we can play an additional so...
void play_music(Music_track)
Attaches the given music track to this mixer and starts it playing.
The entity that coordinates playing all audio tracks.
void set_music_volume(double unit_value)
Sets the music volume, on a scale from 0.0 to 1.0.
bool empty() const
Recognizes the empty music track.
Duration pause()
Pauses the timer.
Attached but not playing.
const Sound_effect & get_effect() const
Gets the Sound_effect being played by this handle.
long milliseconds() const
Gets this duration, approximately, in milliseconds.
An exception that indicates that a logic error was performed by the client.
~Mixer()
Destructor, to clean up the mixer's resources.
State
The state of an audio channel.
bool empty() const
Recognizes the empty sound effect handle.
void stop()
Stops the effect from playing and detaches it.
No track is attached to the channel, or no channel is attached to the handle.
Music_track()
Default-constructs the empty music track.
Sound_effect()
Default-constructs the empty sound effect track.
void resume()
Unpauses the effect.
void pause()
Pauses the effect.
void attach_music(Music_track)
Attaches the given music track to this mixer.