diff --git a/CMakeLists.txt b/CMakeLists.txt index 724c022..276a873 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,6 +111,7 @@ SET(LIBRARY_SOURCES SDL2pp/Chunk.cc SDL2pp/Exception.cc SDL2pp/Mixer.cc + SDL2pp/Music.cc SDL2pp/Point.cc SDL2pp/RWops.cc SDL2pp/Rect.cc @@ -132,6 +133,7 @@ SET(LIBRARY_HEADERS SDL2pp/ContainerRWops.hh SDL2pp/Exception.hh SDL2pp/Mixer.hh + SDL2pp/Music.hh SDL2pp/Optional.hh SDL2pp/Point.hh SDL2pp/RWops.hh diff --git a/SDL2pp/Mixer.cc b/SDL2pp/Mixer.cc index b2d9605..7f1a763 100644 --- a/SDL2pp/Mixer.cc +++ b/SDL2pp/Mixer.cc @@ -21,6 +21,7 @@ #include #include +#include #include namespace SDL2pp { @@ -35,7 +36,7 @@ Mixer::~Mixer() { Mix_CloseAudio(); } -Mixer::Mixer(Mixer&& other) noexcept : open_(other.open_) { +Mixer::Mixer(Mixer&& other) noexcept : open_(other.open_), current_music_hook_(std::move(other.current_music_hook_)) { other.open_ = false; } @@ -45,6 +46,7 @@ Mixer& Mixer::operator=(Mixer&& other) noexcept { if (open_) Mix_CloseAudio(); open_ = other.open_; + current_music_hook_ = std::move(other.current_music_hook_); other.open_ = false; return *this; } @@ -129,4 +131,77 @@ Mix_Fading Mixer::GetChannelFading(int which) const { return Mix_FadingChannel(which); } +void Mixer::PlayMusic(const Music& music, int loops) { + if (Mix_PlayMusic(music.Get(), loops) == -1) + throw Exception("Mix_PlayMusic"); +} + +void Mixer::FadeInMusic(const Music& music, int loops, int ms) { + if (Mix_FadeInMusic(music.Get(), loops, ms) == -1) + throw Exception("Mix_FadeInMusic"); +} + +int Mixer::SetMusicVolume(int volume) { + return Mix_VolumeMusic(volume); +} + +int Mixer::GetMusicVolume() const { + return Mix_VolumeMusic(-1); +} + +void Mixer::PauseMusic() { + Mix_PauseMusic(); +} + +void Mixer::ResumeMusic() { + Mix_ResumeMusic(); +} + +void Mixer::RewindMusic() { + Mix_RewindMusic(); +} + +void Mixer::SetMusicPosition(double position) { + if (Mix_SetMusicPosition(position) == -1) + throw Exception("Mix_SetMusicPosition"); +} + +void Mixer::HaltMusic() { + Mix_HaltMusic(); +} + +bool Mixer::FadeOutMusic(int ms) { + return Mix_FadeOutMusic(ms); +} + +bool Mixer::IsMusicPlaying() const { + return Mix_PlayingMusic(); +} + +bool Mixer::IsMusicPaused() const { + return Mix_PausedMusic(); +} + +Mix_Fading Mixer::GetMusicFading() const { + return Mix_FadingMusic(); +} + +void Mixer::SetMusicFinishedHandler(MusicFinishedHandler music_finished) { + Mix_HookMusicFinished(music_finished); +} + +void Mixer::SetMusicHook(MusicHook&& hook) { + if (!hook) { + Mix_HookMusic(nullptr, nullptr); + current_music_hook_.reset(nullptr); + return; + } + + current_music_hook_.reset(new MusicHook(std::move(hook))); + + Mix_HookMusic([](void *udata, Uint8 *stream, int len) { + static_cast*>(udata)->operator()(stream, len); + }, current_music_hook_.get()); +} + } diff --git a/SDL2pp/Mixer.hh b/SDL2pp/Mixer.hh index 210ca3f..4ce6073 100644 --- a/SDL2pp/Mixer.hh +++ b/SDL2pp/Mixer.hh @@ -22,11 +22,16 @@ #ifndef SDL2PP_MIXER_HH #define SDL2PP_MIXER_HH +#include + +#include + #include namespace SDL2pp { class Chunk; +class Music; //////////////////////////////////////////////////////////// /// \brief SDL_mixer's audio mixer @@ -43,9 +48,13 @@ class Chunk; class Mixer { public: typedef void (*ChannelFinishedHandler)(int); ///< Function type for channel finished callback + typedef void (*MusicFinishedHandler)(); ///< Function type for music finished callback + + typedef std::function MusicHook; ///< Custom music hook private: bool open_; + std::unique_ptr current_music_hook_; public: //////////////////////////////////////////////////////////// @@ -363,7 +372,165 @@ public: Mix_Fading GetChannelFading(int which) const; // TODO: Groups - // TODO: Music + + //////////////////////////////////////////////////////////// + /// \brief Play music + /// + /// \param[in] music Music to play + /// \param[in] loops number of times to play through the music. + /// 0 plays the music zero times... + /// -1 plays the music forever + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC57 + /// + //////////////////////////////////////////////////////////// + void PlayMusic(const Music& music, int loops = -1); + + //////////////////////////////////////////////////////////// + /// \brief Play music, with looping, and fade in + /// + /// \param[in] music Music to play + /// \param[in] loops number of times to play through the music. + /// 0 plays the music zero times... + /// -1 plays the music forever + /// \param[in] ms Milliseconds for the fade-in effect to complete + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC57 + /// + //////////////////////////////////////////////////////////// + void FadeInMusic(const Music& music, int loops = -1, int ms = 0); + + //////////////////////////////////////////////////////////// + /// \brief Set music volume + /// + /// \param[in] volume The volume to use from 0 to MIX_MAX_VOLUME(128) + /// + /// \returns The previous volume setting + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC61 + /// + //////////////////////////////////////////////////////////// + int SetMusicVolume(int volume); + + //////////////////////////////////////////////////////////// + /// \brief Get music volume + /// + /// \returns Current volume setting + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC61 + /// + //////////////////////////////////////////////////////////// + int GetMusicVolume() const; + + //////////////////////////////////////////////////////////// + /// \brief Pause music + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC62 + /// + //////////////////////////////////////////////////////////// + void PauseMusic(); + + //////////////////////////////////////////////////////////// + /// \brief Resume paused music + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC63 + /// + //////////////////////////////////////////////////////////// + void ResumeMusic(); + + //////////////////////////////////////////////////////////// + /// \brief Rewind music to beginning + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC64 + /// + //////////////////////////////////////////////////////////// + void RewindMusic(); + + //////////////////////////////////////////////////////////// + /// \brief Set position of playback in stream + /// + /// \param[in] position Posistion to play from + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC65 + /// + //////////////////////////////////////////////////////////// + void SetMusicPosition(double position); + + //////////////////////////////////////////////////////////// + /// \brief Stop music playback + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC67 + /// + //////////////////////////////////////////////////////////// + void HaltMusic(); + + //////////////////////////////////////////////////////////// + /// \brief Stop music, with fade out + /// + /// \param[in] ms Milliseconds of time that the fade-out effect + /// should take to go to silence, starting now. + /// + /// \returns True in success, false on failure + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC68 + /// + //////////////////////////////////////////////////////////// + bool FadeOutMusic(int ms); + + //////////////////////////////////////////////////////////// + /// \brief Test whether music is playing + /// + /// \returns True if music is actively playing + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC71 + /// + //////////////////////////////////////////////////////////// + bool IsMusicPlaying() const; + + //////////////////////////////////////////////////////////// + /// \brief Test whether music is paused + /// + /// \returns True if music is paused + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC72 + /// + //////////////////////////////////////////////////////////// + bool IsMusicPaused() const; + + //////////////////////////////////////////////////////////// + /// \brief Get status of current music fade activity + /// + /// \returns The fading status + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC73 + /// + //////////////////////////////////////////////////////////// + Mix_Fading GetMusicFading() const; + + //////////////////////////////////////////////////////////// + /// \brief Set a callback for when music stops + /// + /// \param[in] music_finished Function to call when music stops + /// + /// \note Since Mix_HookMusicFinished doesn't take any custom data + /// pointer, unfortunately there's no safe way of using + /// std::function here. + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC69 + /// + //////////////////////////////////////////////////////////// + void SetMusicFinishedHandler(MusicFinishedHandler music_finished); + + //////////////////////////////////////////////////////////// + /// \brief Hook for a custom music player + /// + /// \param[in] hook Music player mixer function + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC60 + /// + //////////////////////////////////////////////////////////// + void SetMusicHook(MusicHook&& hook); + // TODO: Effects }; diff --git a/SDL2pp/Music.cc b/SDL2pp/Music.cc new file mode 100644 index 0000000..ec7eb1c --- /dev/null +++ b/SDL2pp/Music.cc @@ -0,0 +1,62 @@ +/* + libSDL2pp - C++11 bindings/wrapper for SDL2 + Copyright (C) 2015 Dmitry Marakasov + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include + +namespace SDL2pp { + +Music::Music(Mix_Music* music) : music_(music) { +} + +Music::Music(const std::string& file) { + if ((music_ = Mix_LoadMUS(file.c_str())) == nullptr) + throw Exception("Mix_LoadMUS"); +} + +Music::~Music() { + if (music_ != nullptr) + Mix_FreeMusic(music_); +} + +Music::Music(Music&& other) noexcept : music_(other.music_) { + other.music_ = nullptr; +} + +Music& Music::operator=(Music&& other) noexcept { + if (&other == this) + return *this; + if (music_ != nullptr) + Mix_FreeMusic(music_); + music_ = other.music_; + other.music_ = nullptr; + return *this; +} + +Mix_Music* Music::Get() const { + return music_; +} + +Mix_MusicType Music::GetType() const { + return Mix_GetMusicType(music_); +} + +} diff --git a/SDL2pp/Music.hh b/SDL2pp/Music.hh new file mode 100644 index 0000000..c3258a4 --- /dev/null +++ b/SDL2pp/Music.hh @@ -0,0 +1,125 @@ +/* + libSDL2pp - C++11 bindings/wrapper for SDL2 + Copyright (C) 2015 Dmitry Marakasov + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef SDL2PP_MUSIC_HH +#define SDL2PP_MUSIC_HH + +#include + +#include + +namespace SDL2pp { + +//////////////////////////////////////////////////////////// +/// \brief SDL_mixer music data +/// +/// \ingroup mixer +/// +/// \headerfile SDL2pp/Music.hh +/// +//////////////////////////////////////////////////////////// +class Music { +private: + Mix_Music* music_; ///< Managed Mix_Music object + +public: + //////////////////////////////////////////////////////////// + /// \brief Construct from existing Mix_Music pointer + /// + /// \param[in] chunk Existing Mix_Music to manage + /// + //////////////////////////////////////////////////////////// + Music(Mix_Music* music); + + //////////////////////////////////////////////////////////// + /// \brief Load music file + /// + /// \param[in] file Name of music file to use + /// + /// \throws SDL2pp::Exception + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC55 + /// + //////////////////////////////////////////////////////////// + Music(const std::string& file); + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC56 + /// + //////////////////////////////////////////////////////////// + ~Music(); + + //////////////////////////////////////////////////////////// + /// \brief Move constructor + /// + /// \param[in] other SDL2pp::Music object to move data from + /// + //////////////////////////////////////////////////////////// + Music(Music&& other) noexcept; + + //////////////////////////////////////////////////////////// + /// \brief Move assignment operator + /// + /// \param[in] other SDL2pp::Music object to move data from + /// + /// \returns Reference to self + /// + //////////////////////////////////////////////////////////// + Music& operator=(Music&& other) noexcept; + + //////////////////////////////////////////////////////////// + /// \brief Deleted copy constructor + /// + /// This class is not copyable + /// + //////////////////////////////////////////////////////////// + Music(const Music& other) = delete; + + //////////////////////////////////////////////////////////// + /// \brief Deleted assignment operator + /// + /// This class is not copyable + /// + //////////////////////////////////////////////////////////// + Music& operator=(const Music& other) = delete; + + //////////////////////////////////////////////////////////// + /// \brief Get pointer to managed Mix_Music object + /// + /// \returns Pointer to managed Mix_Music object + /// + //////////////////////////////////////////////////////////// + Mix_Music* Get() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the music encoding type + /// + /// \returns The type of music + /// + //////////////////////////////////////////////////////////// + Mix_MusicType GetType() const; +}; + +} + +#endif diff --git a/SDL2pp/SDL2pp.hh b/SDL2pp/SDL2pp.hh index c24954a..c5bf5b7 100644 --- a/SDL2pp/SDL2pp.hh +++ b/SDL2pp/SDL2pp.hh @@ -125,6 +125,7 @@ #ifdef SDL2PP_WITH_MIXER # include # include +# include # include #endif diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 87c7611..c7e8d1f 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -31,4 +31,10 @@ ENDIF(SDL2PP_WITH_TTF) IF(SDL2PP_WITH_MIXER) ADD_EXECUTABLE(mixer mixer.cc) TARGET_LINK_LIBRARIES(mixer SDL2pp) + + ADD_EXECUTABLE(mixer_music mixer_music.cc) + TARGET_LINK_LIBRARIES(mixer_music SDL2pp) + + ADD_EXECUTABLE(mixer_music_sine mixer_music_sine.cc) + TARGET_LINK_LIBRARIES(mixer_music_sine SDL2pp) ENDIF(SDL2PP_WITH_MIXER) diff --git a/examples/mixer.cc b/examples/mixer.cc index 59e61b8..c8ba66d 100644 --- a/examples/mixer.cc +++ b/examples/mixer.cc @@ -38,7 +38,7 @@ int main() try { Chunk sound(TESTDATA_DIR "/test.ogg"); - mixer.ChannelFinished([](int channel){ + mixer.SetChannelFinishedHandler([](int channel){ std::cerr << "Channel " << channel << " finished playback" << std::endl; }); diff --git a/examples/mixer_music.cc b/examples/mixer_music.cc new file mode 100644 index 0000000..e456e80 --- /dev/null +++ b/examples/mixer_music.cc @@ -0,0 +1,49 @@ +/* + libSDL2pp - C++11 bindings/wrapper for SDL2 + Copyright (C) 2015 Dmitry Marakasov + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include + +#include +#include + +#include +#include +#include +#include + +using namespace SDL2pp; + +int main() try { + SDL sdl(SDL_INIT_AUDIO); + SDLMixer mixerlib(MIX_INIT_OGG); + Mixer mixer(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, MIX_DEFAULT_CHANNELS, 4096); + + Music music(TESTDATA_DIR "/test.ogg"); + + mixer.FadeInMusic(music, -1, 2000); + + SDL_Delay(5000); + + return 0; +} catch (std::exception& e) { + std::cerr << "Error: " << e.what() << std::endl; + return 1; +} diff --git a/examples/mixer_music_sine.cc b/examples/mixer_music_sine.cc new file mode 100644 index 0000000..15159f8 --- /dev/null +++ b/examples/mixer_music_sine.cc @@ -0,0 +1,54 @@ +/* + libSDL2pp - C++11 bindings/wrapper for SDL2 + Copyright (C) 2013-2015 Dmitry Marakasov + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include + +#include + +#include +#include + +using namespace SDL2pp; + +int main() try { + SDL sdl(SDL_INIT_AUDIO); + + Mixer mixer(MIX_DEFAULT_FREQUENCY, AUDIO_S16SYS, 1, 4096); + + float frequency = 2093.00f; // C7 tone + int64_t nsample = 0; + + // Open audio device + mixer.SetMusicHook([&nsample, frequency](Uint8* stream, int len) { + // fill provided buffer with sine wave + for (Uint8* ptr = stream; ptr < stream + len; ptr += 2) + *(Uint16*)ptr = (Uint16)(32766.0f * sin(nsample++ / (float)MIX_DEFAULT_FREQUENCY * frequency)); + } + ); + + // Play for 1 second, after which everything is stopped and closed + SDL_Delay(1000); + + return 0; +} catch (std::exception& e) { + std::cerr << "Error: " << e.what() << std::endl; + return 1; +}