diff --git a/.travis.yml b/.travis.yml index 7556dec..863e9f5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ before_install: - sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test - sudo add-apt-repository --yes ppa:libreoffice/libreoffice-4-2 - sudo apt-get update -qq - - sudo apt-get install -qq cmake libsdl2-dev libsdl2-image-dev libsdl2-ttf-dev g++-4.8 cppcheck doxygen + - sudo apt-get install -qq cmake libsdl2-dev libsdl2-image-dev libsdl2-ttf-dev libsdl2-mixer-dev g++-4.8 cppcheck doxygen - sudo sed -i -e 's|friend class hash|friend struct hash|' /usr/include/c++/4.8/bits/stl_bvector.h - if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi script: diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b65d7b..276a873 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,8 +21,9 @@ SET(SDL2PP_CXXSTD "c++11" CACHE STRING "Used c++ standard") IF(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) OPTION(SDL2PP_WITH_IMAGE "Enable SDL2_image support" ON) OPTION(SDL2PP_WITH_TTF "Enable SDL2_ttf support" ON) + OPTION(SDL2PP_WITH_MIXER "Enable SDL2_mixer support" ON) ELSE(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) - # please set SDL2PP_WITH_IMAGE, SDL2PP_WITH_TTF in parent project as needed + # please set SDL2PP_WITH_IMAGE, SDL2PP_WITH_TTF, SDL2PP_WITH_MIXER in parent project as needed ENDIF(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) # depends @@ -43,6 +44,13 @@ IF(SDL2PP_WITH_TTF) ELSE(SDL2PP_WITH_TTF) MESSAGE(STATUS "SDL2_ttf support disabled") ENDIF(SDL2PP_WITH_TTF) +IF(SDL2PP_WITH_MIXER) + FIND_PACKAGE(SDL2_mixer REQUIRED) + SET(SDL2_ALL_INCLUDE_DIRS ${SDL2_ALL_INCLUDE_DIRS} ${SDL2_MIXER_INCLUDE_DIR}) + SET(SDL2_ALL_LIBRARIES ${SDL2_ALL_LIBRARIES} ${SDL2_MIXER_LIBRARY}) +ELSE(SDL2PP_WITH_MIXER) + MESSAGE(STATUS "SDL2_mixer support disabled") +ENDIF(SDL2PP_WITH_MIXER) FIND_PACKAGE(Doxygen) @@ -100,12 +108,16 @@ SET(LIBRARY_SOURCES SDL2pp/AudioDevice.cc SDL2pp/AudioLock.cc SDL2pp/AudioSpec.cc + SDL2pp/Chunk.cc SDL2pp/Exception.cc + SDL2pp/Mixer.cc + SDL2pp/Music.cc SDL2pp/Point.cc SDL2pp/RWops.cc SDL2pp/Rect.cc SDL2pp/Renderer.cc SDL2pp/SDL.cc + SDL2pp/SDLMixer.cc SDL2pp/Surface.cc SDL2pp/SurfaceLock.cc SDL2pp/Texture.cc @@ -117,8 +129,11 @@ SET(LIBRARY_SOURCES SET(LIBRARY_HEADERS SDL2pp/AudioDevice.hh SDL2pp/AudioSpec.hh + SDL2pp/Chunk.hh SDL2pp/ContainerRWops.hh SDL2pp/Exception.hh + SDL2pp/Mixer.hh + SDL2pp/Music.hh SDL2pp/Optional.hh SDL2pp/Point.hh SDL2pp/RWops.hh @@ -126,6 +141,7 @@ SET(LIBRARY_HEADERS SDL2pp/Renderer.hh SDL2pp/SDL.hh SDL2pp/SDL2pp.hh + SDL2pp/SDLMixer.hh SDL2pp/StreamRWops.hh SDL2pp/Surface.hh SDL2pp/Texture.hh @@ -162,6 +178,15 @@ IF(SDL2PP_WITH_IMAGE) ) ENDIF(SDL2PP_WITH_IMAGE) +IF(SDL2PP_WITH_MIXER) + SET(LIBRARY_SOURCES + ${LIBRARY_SOURCES} + ) + SET(LIBRARY_HEADERS + ${LIBRARY_HEADERS} + ) +ENDIF(SDL2PP_WITH_MIXER) + # targets IF(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) MESSAGE(STATUS "libSDL2pp ${SDL2PP_VERSION} standalone build") diff --git a/README.md b/README.md index 5e48962..1fdfdf4 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ try { using namespace SDL2pp; // Init SDL; will be automatically deinitialized when the object is destroyed - SDL sdl(SDL_INIT_VIDEO); + SDL sdl(SDL_INIT_VIDEO | SDL_INIT_AUDIO); // Likewise, init SDL_ttf library SDLTTF sdl_ttf; @@ -29,6 +29,11 @@ try { Font font("Vera.ttf", 20); // SDL_ttf font + // Initialize audio mixer + Mixer mixer(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, MIX_DEFAULT_CHANNELS, 4096); + + Chunk sound("effect.ogg"); // OGG sound file + // Create texture from surface containing text rendered by SDL_ttf Texture text(renderer, font.RenderText_Solid("Hello, world!", SDL_Color{255, 255, 255, 255})); @@ -53,6 +58,9 @@ try { renderer.Present(); + // Play our sound one time on a first available mixer channel + mixer.PlayChannel(-1, sound); + // You can still access wrapped C SDL types SDL_Renderer* sdl_renderer = renderer.Get(); @@ -94,6 +102,11 @@ Currently, the library provides wrapper classes for * SDL_image * Library initialization/deinitialization * Loading functions integrated into Texture and Surface +* SDL_mixer + * Library initialization/deinitialization + * Sound sample handling (Mix_Chunk) + * Music handling (Mix_Music) + * Mixer class which encapsulates device handling and all playback functions * SDL_ttf * Library initialization/deinitialization * TTF_Font (all functions covered) @@ -122,8 +135,9 @@ example clang 3.4+ or gcc 4.8+. Dependencies: * cmake * SDL2 -* SDL_image2 (optional) -* SDL_ttf2 (optional) +* SDL2_image (optional) +* SDL2_mixer (optional) +* SDL2_ttf (optional) To build standalone version: @@ -132,6 +146,7 @@ To build standalone version: Following variables may be supplied to CMake to affect build: * ```SDL2PP_WITH_IMAGE``` - enable SDL_image support (default ON) +* ```SDL2PP_WITH_MIXER``` - enable SDL_mixer support (default ON) * ```SDL2PP_WITH_TTF``` - enable SDL_ttf support (default ON) * ```SDL2PP_WITH_WERROR``` - treat warnings as errors, useful for CI (default OFF) * ```SDL2PP_CXXSTD``` - override C++ standard (default C++11). With C++1y some additional features are enabled such as usage of [[deprecated]] attribute and using stock experimental/optional from C++ standard library @@ -183,6 +198,7 @@ Just place the library into dedicated directory in your project ```cmake SET(SDL2PP_WITH_IMAGE ON) # if you need SDL_image support +SET(SDL2PP_WITH_MIXER ON) # if you need SDL_mixer support SET(SDL2PP_WITH_TTF ON) # if you need SDL_ttf support ADD_SUBDIRECTORY(extlib/libSDL2pp) ``` diff --git a/SDL2pp/Chunk.cc b/SDL2pp/Chunk.cc new file mode 100644 index 0000000..f0edb95 --- /dev/null +++ b/SDL2pp/Chunk.cc @@ -0,0 +1,68 @@ +/* + 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 + +namespace SDL2pp { + +Chunk::Chunk(Mix_Chunk* chunk) : chunk_(chunk) { +} + +Chunk::Chunk(const std::string& file) { + if ((chunk_ = Mix_LoadWAV(file.c_str())) == nullptr) + throw Exception("Mix_LoadWAV"); +} + +Chunk::Chunk(RWops& rwops) { + if ((chunk_ = Mix_LoadWAV_RW(rwops.Get(), 0)) == nullptr) + throw Exception("Mix_LoadWAV_RW"); +} + +Chunk::~Chunk() { + if (chunk_ != nullptr) + Mix_FreeChunk(chunk_); +} + +Chunk::Chunk(Chunk&& other) noexcept : chunk_(other.chunk_) { + other.chunk_ = nullptr; +} + +Chunk& Chunk::operator=(Chunk&& other) noexcept { + if (&other == this) + return *this; + if (chunk_ != nullptr) + Mix_FreeChunk(chunk_); + chunk_ = other.chunk_; + other.chunk_ = nullptr; + return *this; +} + +Mix_Chunk* Chunk::Get() const { + return chunk_; +} + +int Chunk::Volume(int volume) { + return Mix_VolumeChunk(chunk_, volume); +} + +} diff --git a/SDL2pp/Chunk.hh b/SDL2pp/Chunk.hh new file mode 100644 index 0000000..b706b5a --- /dev/null +++ b/SDL2pp/Chunk.hh @@ -0,0 +1,150 @@ +/* + 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_CHUNK_HH +#define SDL2PP_CHUNK_HH + +#include + +#include + +namespace SDL2pp { + +class RWops; + +//////////////////////////////////////////////////////////// +/// \brief Chunk of SDL_mixer audio data +/// +/// \ingroup mixer +/// +/// \headerfile SDL2pp/Chunk.hh +/// +//////////////////////////////////////////////////////////// +class Chunk { +private: + Mix_Chunk* chunk_; ///< Managed Mix_Chunk object + +public: + //////////////////////////////////////////////////////////// + /// \brief Construct from existing Mix_Chunk structure + /// + /// \param[in] chunk Existing Mix_Chunk to manage + /// + //////////////////////////////////////////////////////////// + Chunk(Mix_Chunk* chunk); + + //////////////////////////////////////////////////////////// + /// \brief Load file for use as a sample + /// + /// \param[in] file File name to load sample from + /// + /// \throws SDL2pp::Exception + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC19 + /// + //////////////////////////////////////////////////////////// + Chunk(const std::string& file); + + //////////////////////////////////////////////////////////// + /// \brief Load sample using RWops + /// + /// \param[in] rwops SDL2pp::RWops used to access sample data + /// + /// \throws SDL2pp::Exception + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC20 + /// + //////////////////////////////////////////////////////////// + Chunk(RWops& rwops); + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC24 + /// + /// \note Despite what Mix_FreeChunk() documentation (and even + /// comment in function implementation) says, right after + /// the named comment there's a code which stops playback + /// of any channel which plays the chunk being freed. + /// Thus, it is safe to destroy Chunk before destroying + /// Mixer, even if it still plays the chunk. + /// + //////////////////////////////////////////////////////////// + ~Chunk(); + + //////////////////////////////////////////////////////////// + /// \brief Move constructor + /// + /// \param[in] other SDL2pp::Chunk object to move data from + /// + //////////////////////////////////////////////////////////// + Chunk(Chunk&& other) noexcept; + + //////////////////////////////////////////////////////////// + /// \brief Move assignment operator + /// + /// \param[in] other SDL2pp::Chunk object to move data from + /// + /// \returns Reference to self + /// + //////////////////////////////////////////////////////////// + Chunk& operator=(Chunk&& other) noexcept; + + //////////////////////////////////////////////////////////// + /// \brief Deleted copy constructor + /// + /// This class is not copyable + /// + //////////////////////////////////////////////////////////// + Chunk(const Chunk& other) = delete; + + //////////////////////////////////////////////////////////// + /// \brief Deleted assignment operator + /// + /// This class is not copyable + /// + //////////////////////////////////////////////////////////// + Chunk& operator=(const Chunk& other) = delete; + + //////////////////////////////////////////////////////////// + /// \brief Get pointer to managed Mix_Chunk structure + /// + /// \returns Pointer to managed Mix_Chunk structure + /// + //////////////////////////////////////////////////////////// + Mix_Chunk* Get() const; + + //////////////////////////////////////////////////////////// + /// \brief Set volume of a chunk + /// + /// \param[in] volume The volume to use from 0 to MIX_MAX_VOLUME(128) + /// + /// \returns Previous volume setting + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC23 + /// + //////////////////////////////////////////////////////////// + int Volume(int volume); +}; + +} + +#endif diff --git a/SDL2pp/Config.hh.in b/SDL2pp/Config.hh.in index 0c0e4dd..b4b3c05 100644 --- a/SDL2pp/Config.hh.in +++ b/SDL2pp/Config.hh.in @@ -30,6 +30,7 @@ #cmakedefine SDL2PP_WITH_IMAGE #cmakedefine SDL2PP_WITH_TTF +#cmakedefine SDL2PP_WITH_MIXER #cmakedefine SDL2PP_WITH_2_0_4 #cmakedefine SDL2PP_WITH_EXPERIMENTAL_OPTIONAL #cmakedefine SDL2PP_WITH_DEPRECATED diff --git a/SDL2pp/Mixer.cc b/SDL2pp/Mixer.cc new file mode 100644 index 0000000..7d649fc --- /dev/null +++ b/SDL2pp/Mixer.cc @@ -0,0 +1,285 @@ +/* + 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 + +namespace SDL2pp { + +Mixer::Mixer(int frequency, Uint16 format, int channels, int chunksize) : open_(true) { + if (Mix_OpenAudio(frequency, format, channels, chunksize) != 0) + throw Exception("Mix_OpenAudio"); +} + +Mixer::~Mixer() { + if (open_) + Mix_CloseAudio(); +} + +Mixer::Mixer(Mixer&& other) noexcept : open_(other.open_), current_music_hook_(std::move(other.current_music_hook_)) { + other.open_ = false; +} + +Mixer& Mixer::operator=(Mixer&& other) noexcept { + if (&other == this) + return *this; + if (open_) + Mix_CloseAudio(); + open_ = other.open_; + current_music_hook_ = std::move(other.current_music_hook_); + other.open_ = false; + return *this; +} + +int Mixer::AllocateChannels(int numchans) { + return Mix_AllocateChannels(numchans); +} + +int Mixer::GetNumChannels() const { + return Mix_AllocateChannels(-1); +} + +int Mixer::SetVolume(int channel, int volume) { + return Mix_Volume(channel, volume); +} + +int Mixer::GetVolume(int channel) const { + return Mix_Volume(channel, -1); +} + +int Mixer::PlayChannel(int channel, const Chunk& chunk, int loops) { + int chan; + if ((chan = Mix_PlayChannel(channel, chunk.Get(), loops)) == -1) + throw Exception("Mix_PlayChannel"); + return chan; +} + +int Mixer::PlayChannel(int channel, const Chunk& chunk, int loops, int ticks) { + int chan; + if ((chan = Mix_PlayChannelTimed(channel, chunk.Get(), loops, ticks)) == -1) + throw Exception("Mix_PlayChannelTimed"); + return chan; +} + +int Mixer::FadeInChannel(int channel, const Chunk& chunk, int loops, int ms) { + int chan; + if ((chan = Mix_FadeInChannel(channel, chunk.Get(), loops, ms)) == -1) + throw Exception("Mix_FadeInChannel"); + return chan; +} + +int Mixer::FadeInChannel(int channel, const Chunk& chunk, int loops, int ms, int ticks) { + int chan; + if ((chan = Mix_FadeInChannelTimed(channel, chunk.Get(), loops, ms, ticks)) == -1) + throw Exception("Mix_FadeInChannelTimed"); + return chan; +} + +void Mixer::PauseChannel(int channel) { + Mix_Pause(channel); +} + +void Mixer::ResumeChannel(int channel) { + Mix_Resume(channel); +} + +void Mixer::HaltChannel(int channel) { + Mix_HaltChannel(channel); +} + +int Mixer::ExpireChannel(int channel, int ticks) { + return Mix_ExpireChannel(channel, ticks); +} + +int Mixer::FadeOutChannel(int channel, int ms) { + return Mix_FadeOutChannel(channel, ms); +} + +void Mixer::SetChannelFinishedHandler(ChannelFinishedHandler channel_finished) { + Mix_ChannelFinished(channel_finished); +} + +int Mixer::IsChannelPlaying(int channel) const { + return Mix_Playing(channel); +} + +int Mixer::IsChannelPaused(int channel) const { + return Mix_Paused(channel); +} + +Mix_Fading Mixer::GetChannelFading(int which) const { + return Mix_FadingChannel(which); +} + +int Mixer::ReserveChannels(int num) { + return Mix_ReserveChannels(num); +} + +void Mixer::GroupChannel(int which, int tag) { + if (Mix_GroupChannel(which, tag) != 1) + throw Exception("Mix_GroupChannel"); +} + +void Mixer::GroupChannels(int from, int to, int tag) { + if (Mix_GroupChannels(from, to, tag) != to - from + 1) + throw Exception("Mix_GroupChannels"); +} + +int Mixer::GetGroupNumChannels(int tag) const { + return Mix_GroupCount(tag); +} + +int Mixer::GetGroupAvailableChannel(int tag) const { + return Mix_GroupAvailable(tag); +} + +int Mixer::GetGroupOldestChannel(int tag) const { + return Mix_GroupOldest(tag); +} + +int Mixer::GetGroupNewestChannel(int tag) const { + return Mix_GroupNewer(tag); +} + +int Mixer::FadeOutGroup(int tag, int ms) { + return Mix_FadeOutGroup(tag, ms); +} + +void Mixer::HaltGroup(int tag) { + Mix_HaltGroup(tag); +} + +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()); +} + +void Mixer::SetPanning(int channel, int left, int right) { + if (Mix_SetPanning(channel, left, right) == 0) + throw Exception("Mix_SetPanning"); +} + +void Mixer::UnsetPanning(int channel) { + if (Mix_SetPanning(channel, 255, 255) == 0) + throw Exception("Mix_SetPanning"); +} + +void Mixer::SetDistance(int channel, int distance) { + if (Mix_SetDistance(channel, distance) == 0) + throw Exception("Mix_SetDistance"); +} + +void Mixer::UnsetDistance(int channel) { + if (Mix_SetDistance(channel, 0) == 0) + throw Exception("Mix_SetDistance"); +} + +void Mixer::SetPosition(int channel, int angle, int distance) { + if (Mix_SetPosition(channel, angle, distance) == 0) + throw Exception("Mix_SetPosition"); +} + +void Mixer::UnsetPosition(int channel) { + if (Mix_SetPosition(channel, 0, 0) == 0) + throw Exception("Mix_SetPosition"); +} + +void Mixer::SetReverseStereo(int channel) { + if (Mix_SetReverseStereo(channel, 1) == 0) + throw Exception("Mix_SetReverseStereo"); +} + +void Mixer::UnsetReverseStereo(int channel) { + if (Mix_SetReverseStereo(channel, 0) == 0) + throw Exception("Mix_SetReverseStereo"); +} + +} diff --git a/SDL2pp/Mixer.hh b/SDL2pp/Mixer.hh new file mode 100644 index 0000000..53d032c --- /dev/null +++ b/SDL2pp/Mixer.hh @@ -0,0 +1,776 @@ +/* + 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_MIXER_HH +#define SDL2PP_MIXER_HH + +#include +#include + +#include + +#include + +namespace SDL2pp { + +class Chunk; +class Music; + +//////////////////////////////////////////////////////////// +/// \brief SDL_mixer's audio mixer +/// +/// \ingroup mixer +/// +/// \headerfile SDL2pp/Mixer.hh +/// +/// This class represents open SDL_mixer audio device. Object +/// of this class must be constructed before creating any +/// SDL2pp:Chunk's. +/// +//////////////////////////////////////////////////////////// +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: + //////////////////////////////////////////////////////////// + /// \brief Construct a mixer and open an audio device + /// + /// \param[in] frequency Output sampling frequency in samples + /// per second (Hz). You might use + /// MIX_DEFAULT_FREQUENCY(22050) since that + /// is a good value for most games + /// \param[in] format Output sample format + /// \param[in] channels Number of sound channels in output. Set + /// to 2 for stereo, 1 for mono. This has + /// nothing to do with mixing channels + /// \param[in] chunksize Bytes used per output sample + /// + /// \throws SDL2pp::Exception + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC11 + /// + //////////////////////////////////////////////////////////// + Mixer(int frequency, Uint16 format, int channels, int chunksize); + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC12 + /// + //////////////////////////////////////////////////////////// + ~Mixer(); + + //////////////////////////////////////////////////////////// + /// \brief Move constructor + /// + /// \param[in] other SDL2pp::Mixer object to move data from + /// + //////////////////////////////////////////////////////////// + Mixer(Mixer&& other) noexcept; + + //////////////////////////////////////////////////////////// + /// \brief Move assignment operator + /// + /// \param[in] other SDL2pp::Mixer object to move data from + /// + /// \returns Reference to self + /// + //////////////////////////////////////////////////////////// + Mixer& operator=(Mixer&& other) noexcept; + + //////////////////////////////////////////////////////////// + /// \brief Deleted copy constructor + /// + /// This class is not copyable + /// + //////////////////////////////////////////////////////////// + Mixer(const Mixer& other) = delete; + + //////////////////////////////////////////////////////////// + /// \brief Deleted assignment operator + /// + /// This class is not copyable + /// + //////////////////////////////////////////////////////////// + Mixer& operator=(const Mixer& other) = delete; + + //////////////////////////////////////////////////////////// + /// \brief Set the number of channels to mix + /// + /// \param[in] numchans Number of channels to allocate for mixing + /// + /// \returns The number of channels allocated + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC26 + /// + //////////////////////////////////////////////////////////// + int AllocateChannels(int numchans); + + //////////////////////////////////////////////////////////// + /// \brief Get the number of channels being mixed + /// + /// \returns The number of channels allocated + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC26 + /// + //////////////////////////////////////////////////////////// + int GetNumChannels() const; + + //////////////////////////////////////////////////////////// + /// \brief Set the mix volume of a channel + /// + /// \param[in] channel Channel to set mix volume for. + /// -1 will set the volume for all allocated + /// channels. + /// \param[in] volume The volume to use from 0 to MIX_MAX_VOLUME(128) + /// + /// \returns Current volume of the channel. If channel is -1, + /// the average volume is returned + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC27 + /// + //////////////////////////////////////////////////////////// + int SetVolume(int channel, int volume); + + //////////////////////////////////////////////////////////// + /// \brief Get the mix volume of a channel + /// + /// \param[in] channel Channel to set mix volume for. + /// -1 will return the average volume. + /// + /// \returns Current volume of the channel. If channel is -1, + /// the average volume is returned + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC27 + /// + //////////////////////////////////////////////////////////// + int GetVolume(int channel) const; + + //////////////////////////////////////////////////////////// + /// \brief Play loop + /// + /// \param[in] channel Channel to play on, or -1 for the first + /// free unreserved channel + /// \param[in] chunk Sample to play + /// \param[in] loops Number of loops, -1 is infinite loops. + /// Passing one here plays the sample twice (1 loop). + /// + /// \returns The channel the sample is played on + /// + /// \throws SDL2pp::Exception + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC28 + /// + //////////////////////////////////////////////////////////// + int PlayChannel(int channel, const Chunk& chunk, int loops = 0); + + //////////////////////////////////////////////////////////// + /// \brief Play loop and limit by time + /// + /// \param[in] channel Channel to play on, or -1 for the first + /// free unreserved channel + /// \param[in] chunk Sample to play + /// \param[in] loops Number of loops, -1 is infinite loops. + /// Passing one here plays the sample twice (1 loop). + /// \param[in] ticks Millisecond limit to play sample, at most. + /// If not enough loops or the sample chunk is not + /// long enough, then the sample may stop before + /// this timeout occurs. -1 means play forever + /// + /// \returns The channel the sample is played on + /// + /// \throws SDL2pp::Exception + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC29 + /// + //////////////////////////////////////////////////////////// + int PlayChannel(int channel, const Chunk& chunk, int loops, int ticks); + + //////////////////////////////////////////////////////////// + /// \brief Play loop with fade in + /// + /// \param[in] channel Channel to play on, or -1 for the first + /// free unreserved channel + /// \param[in] chunk Sample to play + /// \param[in] loops Number of loops, -1 is infinite loops. + /// Passing one here plays the sample twice (1 loop). + /// \param[in] ms Milliseconds of time that the fade-in effect + /// should take to go from silence to full volume + /// + /// \returns The channel the sample is played on + /// + /// \throws SDL2pp::Exception + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC30 + /// + //////////////////////////////////////////////////////////// + int FadeInChannel(int channel, const Chunk& chunk, int loops, int ms); + + //////////////////////////////////////////////////////////// + /// \brief loop with fade in and limit by time + /// + /// \param[in] channel Channel to play on, or -1 for the first + /// free unreserved channel + /// \param[in] chunk Sample to play + /// \param[in] loops Number of loops, -1 is infinite loops. + /// Passing one here plays the sample twice (1 loop). + /// \param[in] ms Milliseconds of time that the fade-in effect + /// should take to go from silence to full volume + /// \param[in] ticks Millisecond limit to play sample, at most. + /// If not enough loops or the sample chunk is not + /// long enough, then the sample may stop before + /// this timeout occurs. -1 means play forever + /// + /// \returns The channel the sample is played on + /// + /// \throws SDL2pp::Exception + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC31 + /// + //////////////////////////////////////////////////////////// + int FadeInChannel(int channel, const Chunk& chunk, int loops, int ms, int ticks); + + //////////////////////////////////////////////////////////// + /// \brief Pause a channel + /// + /// \param[in] channel Channel to pause on, or -1 for all channels + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC32 + /// + //////////////////////////////////////////////////////////// + void PauseChannel(int channel = -1); + + //////////////////////////////////////////////////////////// + /// \brief Resume a paused channel + /// + /// \param[in] channel Channel to resume playing, or -1 for all channels + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC33 + /// + //////////////////////////////////////////////////////////// + void ResumeChannel(int channel = -1); + + //////////////////////////////////////////////////////////// + /// \brief Stop playing on a channel + /// + /// \param[in] channel Channel to stop playing, or -1 for all channels + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC34 + /// + //////////////////////////////////////////////////////////// + void HaltChannel(int channel = -1); + + //////////////////////////////////////////////////////////// + /// \brief Change the timed stoppage of a channel + /// + /// \param[in] channel Channel to stop playing, or -1 for all channels + /// \param[in] ticks Millisecons until channel(s) halt playback + /// + /// \returns Number of channels set to expire. Whether or not they are active + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC35 + /// + //////////////////////////////////////////////////////////// + int ExpireChannel(int channel, int ticks); + + //////////////////////////////////////////////////////////// + /// \brief Stop playing channel after timed fade out + /// + /// \param[in] channel Channel to fade out, or -1 to fade all channels out + /// \param[in] ms Milliseconds of time that the fade-out effect should + /// take to go to silence, starting now + /// + /// \returns The number of channels set to fade out + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC36 + /// + //////////////////////////////////////////////////////////// + int FadeOutChannel(int channel, int ms); + + //////////////////////////////////////////////////////////// + /// \brief Set callback for when channel finishes playing + /// + /// \param[in] channel_finished Function to call when any channel + /// finishes playback + /// + /// \note Since Mix_ChannelFinished 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#SEC37 + /// + //////////////////////////////////////////////////////////// + void SetChannelFinishedHandler(ChannelFinishedHandler channel_finished); + + //////////////////////////////////////////////////////////// + /// \brief Get the active playing status of a channel + /// + /// \param[in] channel Channel to test whether it is playing or not. + /// -1 will tell you how many channels are playing + /// + /// \returns Zero if the channel is not playing. Otherwise if you passed + /// in -1, the number of channels playing is returned. If you + /// passed in a specific channel, then 1 is returned if it is + /// playing. + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC38 + /// + //////////////////////////////////////////////////////////// + int IsChannelPlaying(int channel) const; + + //////////////////////////////////////////////////////////// + /// \brief Get the pause status of a channel + /// + /// \param[in] channel Channel to test whether it is paused or not. + /// -1 will tell you how many channels are playing + /// + /// \returns Zero if the channel is not paused. Otherwise if you passed + /// in -1, the number of paused channels is returned. If you + /// passed in a specific channel, then 1 is returned if it is + /// paused. + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC39 + /// + //////////////////////////////////////////////////////////// + int IsChannelPaused(int channel) const; + + //////////////////////////////////////////////////////////// + /// \brief Get the fade status of a channel + /// + /// \param[in] which Channel to get the fade activity status from + /// + /// \returns The fading status + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC40 + /// + //////////////////////////////////////////////////////////// + Mix_Fading GetChannelFading(int which) const; + + //////////////////////////////////////////////////////////// + /// \brief Prevent channels from being used in default group + /// + /// \param[in] num Number of channels to reserve from default mixing. + /// Zero removes all reservations + /// + /// \returns The number of channels reserved. Never fails, + /// but may return less channels than you ask for, + /// depending on the number of channels previously + /// allocated + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC43 + /// + //////////////////////////////////////////////////////////// + int ReserveChannels(int num); + + //////////////////////////////////////////////////////////// + /// \brief Add/remove channel to/from group + /// + /// \param[in] which Channel number to assign tag to. + /// \param[in] tag A group number. Any positive numbers + /// (including zero). -1 is the default group. + /// Use -1 to remove a group tag essentially. + /// + /// \throws SDL2pp::Exception + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC44 + /// + //////////////////////////////////////////////////////////// + void GroupChannel(int which, int tag); + + //////////////////////////////////////////////////////////// + /// \brief Add/remove segment of channels to/from group + /// + /// \param[in] from First Channel number of channels to assign tag to. + /// \param[in] to Last Channel number of channels to assign tag to + /// \param[in] tag A group number. Any positive numbers (including zero). + /// -1 is the default group. Use -1 to remove a group tag + /// essentially. + /// + /// \throws SDL2pp::Exception + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC45 + /// + //////////////////////////////////////////////////////////// + void GroupChannels(int from, int to, int tag); + + //////////////////////////////////////////////////////////// + /// \brief Get number of channels in group + /// + /// \param[in] tag A group number + /// + /// \returns The number of channels found in the group + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC46 + /// + //////////////////////////////////////////////////////////// + int GetGroupNumChannels(int tag) const; + + //////////////////////////////////////////////////////////// + /// \brief Get first inactive channel in group + /// + /// \param[in] tag A group number + /// + /// \returns The channel found on success. -1 is returned when + /// no channels in the group are available + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC47 + /// + //////////////////////////////////////////////////////////// + int GetGroupAvailableChannel(int tag) const; + + //////////////////////////////////////////////////////////// + /// \brief Get oldest busy channel in group + /// + /// \param[in] tag A group number + /// + /// \returns The channel found on success. -1 is returned when + /// no channels in the group are available + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC48 + /// + //////////////////////////////////////////////////////////// + int GetGroupOldestChannel(int tag) const; + + //////////////////////////////////////////////////////////// + /// \brief Get youngest busy channel in group + /// + /// \param[in] tag A group number + /// + /// \returns The channel found on success. -1 is returned when + /// no channels in the group are available + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC49 + /// + //////////////////////////////////////////////////////////// + int GetGroupNewestChannel(int tag) const; + + //////////////////////////////////////////////////////////// + /// \brief Fade out a group over time + /// + /// \param[in] tag Group to fade out + /// \param[in] ms Milliseconds of time that the fade-out effect + /// should take to go to silence, starting now + /// + /// \returns The number of channels set to fade out + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC50 + /// + //////////////////////////////////////////////////////////// + int FadeOutGroup(int tag, int ms); + + //////////////////////////////////////////////////////////// + /// \brief Stop a group + /// + /// \param[in] tag Group to fade out + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC51 + /// + //////////////////////////////////////////////////////////// + void HaltGroup(int tag); + + //////////////////////////////////////////////////////////// + /// \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: custom effects + + //////////////////////////////////////////////////////////// + /// \brief Stereo panning + /// + /// \param[in] channel Channel number to register this effect on or + /// MIX_CHANNEL_POST to process the postmix stream + /// \param[in] left Volume for the left channel, range is 0 (silence) + /// to 255 (loud) + /// \param[in] right Volume for the right channel, range is 0 (silence) + /// to 255 (loud) + /// + /// \throws SDL2pp::Exception + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC80 + /// + //////////////////////////////////////////////////////////// + void SetPanning(int channel, int left, int right); + + //////////////////////////////////////////////////////////// + /// \brief Disable stereo panning + /// + /// \param[in] channel Channel number to unregister this effect from or + /// MIX_CHANNEL_POST to unregister from the postmix stream + /// + /// \throws SDL2pp::Exception + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC80 + /// + //////////////////////////////////////////////////////////// + void UnsetPanning(int channel); + + //////////////////////////////////////////////////////////// + /// \brief Distance attenuation (volume) + /// + /// \param[in] channel Channel number to register this effect on or + /// MIX_CHANNEL_POST to process the postmix stream + /// \param[in] distance Specify the distance from the listener, + /// from 0 (close/loud) to 255 (far/quiet) + /// + /// \throws SDL2pp::Exception + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC81 + /// + //////////////////////////////////////////////////////////// + void SetDistance(int channel, int distance); + + //////////////////////////////////////////////////////////// + /// \brief Disable distance attenuation + /// + /// \param[in] channel Channel number to unregister this effect from or + /// MIX_CHANNEL_POST to unregister from the postmix stream + /// + /// \throws SDL2pp::Exception + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC81 + /// + //////////////////////////////////////////////////////////// + void UnsetDistance(int channel); + + //////////////////////////////////////////////////////////// + /// \brief Panning (angular) and distance + /// + /// \param[in] channel Channel number to register this effect on or + /// MIX_CHANNEL_POST to process the postmix stream + /// \param[in] angle Direction in relation to forward from 0 to 360 degrees. + /// Larger angles will be reduced to this range using angles + /// % 360. 0 = directly in front, 90 = directly to the right + /// etc. + /// \param[in] distance Specify the distance from the listener, + /// from 0 (close/loud) to 255 (far/quiet) + /// + /// \throws SDL2pp::Exception + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC82 + /// + //////////////////////////////////////////////////////////// + void SetPosition(int channel, int angle, int distance); + + //////////////////////////////////////////////////////////// + /// \brief Disable panning and distance + /// + /// \param[in] channel Channel number to unregister this effect from or + /// MIX_CHANNEL_POST to unregister from the postmix stream + /// + /// \throws SDL2pp::Exception + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC82 + /// + //////////////////////////////////////////////////////////// + void UnsetPosition(int channel); + + //////////////////////////////////////////////////////////// + /// \brief Swap stereo left and right + /// + /// \param[in] channel Channel number to register this effect on or + /// MIX_CHANNEL_POST to process the postmix stream + /// + /// \throws SDL2pp::Exception + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC83 + /// + //////////////////////////////////////////////////////////// + void SetReverseStereo(int channel); + + //////////////////////////////////////////////////////////// + /// \brief Disable stereo swapping + /// + /// \param[in] channel Channel number to unregister this effect from or + /// MIX_CHANNEL_POST to unregister from the postmix stream + /// + /// \throws SDL2pp::Exception + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC83 + /// + //////////////////////////////////////////////////////////// + void UnsetReverseStereo(int channel); +}; + +} + +#endif 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..d5695f9 --- /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] music 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 + /// + /// \returns Pointer to managed Mix_Music + /// + //////////////////////////////////////////////////////////// + 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 833477a..c5bf5b7 100644 --- a/SDL2pp/SDL2pp.hh +++ b/SDL2pp/SDL2pp.hh @@ -116,4 +116,17 @@ # include #endif +//////////////////////////////////////////////////////////// +/// \defgroup mixer SDL_mixer +/// +/// \brief Functions that are specific to SDL_mixer library +/// +//////////////////////////////////////////////////////////// +#ifdef SDL2PP_WITH_MIXER +# include +# include +# include +# include +#endif + #endif diff --git a/SDL2pp/SDLMixer.cc b/SDL2pp/SDLMixer.cc new file mode 100644 index 0000000..fd8551f --- /dev/null +++ b/SDL2pp/SDLMixer.cc @@ -0,0 +1,51 @@ +/* + libSDL2pp - C++11 bindings/wrapper for SDL2 + Copyright (C) 2014-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 + +namespace SDL2pp { + +SDLMixer::SDLMixer(int flags) { + if ((Mix_Init(flags) & flags) != flags) + throw Exception("Mix_Init"); +} + +SDLMixer::~SDLMixer() { + // see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC10 + while (Mix_Init(0)) + Mix_Quit(); +} + +int SDLMixer::InitMore(int flags) { + int ret; + if (((ret = Mix_Init(flags)) & flags) != flags) + throw Exception("Mix_Init"); + return ret; +} + +int SDLMixer::GetInitFlags() { + return Mix_Init(0); +} + +} diff --git a/SDL2pp/SDLMixer.hh b/SDL2pp/SDLMixer.hh new file mode 100644 index 0000000..e105ba7 --- /dev/null +++ b/SDL2pp/SDLMixer.hh @@ -0,0 +1,123 @@ +/* + libSDL2pp - C++11 bindings/wrapper for SDL2 + Copyright (C) 2014-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_SDLMIXER_HH +#define SDL2PP_SDLMIXER_HH + +namespace SDL2pp { + +//////////////////////////////////////////////////////////// +/// \brief Object taking care of SDL_mixer library initialization and deinitialization +/// +/// \ingroup mixer +/// +/// \headerfile SDL2pp/SDLMixer.hh +/// +/// Usage example: +/// \code +/// int main() { +/// SDL2pp::SDL sdl(SDL_INIT_VIDEO); +/// SDL2pp::SDLMixer mixer(MIX_INIT_OGG); +/// +/// // SDL_mixer library is automatically deinitialized before exit +/// return 0; +/// } +/// \endcode +/// +//////////////////////////////////////////////////////////// +class SDLMixer { +public: + //////////////////////////////////////////////////////////// + /// \brief Initializes SDL_mixer library + /// + /// \param[in] flags Flags to pass to Mix_Init() + /// + /// \throws SDL2pp::Exception + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC9 + /// + //////////////////////////////////////////////////////////// + SDLMixer(int flags = 0); + + //////////////////////////////////////////////////////////// + /// \brief Destructor, deinitializes SDL_mixer library + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC10 + /// + //////////////////////////////////////////////////////////// + virtual ~SDLMixer(); + + //////////////////////////////////////////////////////////// + /// \brief Try to init more SDL_mixer formats + /// + /// \param[in] flags Flags to pass to Mix_Init() + /// + /// \throws SDL2pp::Exception + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC9 + /// + //////////////////////////////////////////////////////////// + int InitMore(int flags); + + //////////////////////////////////////////////////////////// + /// \brief Get mask of initialized SDL_mixer formats + /// + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC9 + /// + //////////////////////////////////////////////////////////// + int GetInitFlags(); + + //////////////////////////////////////////////////////////// + /// \brief Deleted copy constructor + /// + /// This class is not copyable + /// + //////////////////////////////////////////////////////////// + SDLMixer(const SDLMixer& other) = delete; + + //////////////////////////////////////////////////////////// + /// \brief Deleted assignment operator + /// + /// This class is not copyable + /// + //////////////////////////////////////////////////////////// + SDLMixer& operator=(const SDLMixer& other) = delete; + + //////////////////////////////////////////////////////////// + /// \brief Deleted move constructor + /// + /// This class is not movable + /// + //////////////////////////////////////////////////////////// + SDLMixer(SDLMixer&& other) = delete; + + //////////////////////////////////////////////////////////// + /// \brief Deleted move assignment operator + /// + /// This class is not movable + /// + //////////////////////////////////////////////////////////// + SDLMixer& operator=(SDLMixer&& other) = delete; +}; + +} + +#endif diff --git a/cmake/FindSDL2_mixer.cmake b/cmake/FindSDL2_mixer.cmake new file mode 100644 index 0000000..d42e3b8 --- /dev/null +++ b/cmake/FindSDL2_mixer.cmake @@ -0,0 +1,17 @@ +# - Try to locate SDL2_mixer +# This module defines: +# +# SDL2_MIXER_INCLUDE_DIR +# SDL2_MIXER_LIBRARY +# SDL2_MIXER_FOUND +# + +FIND_PATH(SDL2_MIXER_INCLUDE_DIR NAMES SDL2/SDL_mixer.h) + +FIND_LIBRARY(SDL2_MIXER_LIBRARY NAMES SDL2_mixer) + +INCLUDE(FindPackageHandleStandardArgs) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2_mixer REQUIRED_VARS SDL2_MIXER_INCLUDE_DIR SDL2_MIXER_LIBRARY) + +MARK_AS_ADVANCED(SDL2_MIXER_INCLUDE_DIR SDL2_MIXER_LIBRARY) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 2bd01e5..12b1dfb 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -27,3 +27,17 @@ IF(SDL2PP_WITH_TTF) ADD_EXECUTABLE(ttf ttf.cc) TARGET_LINK_LIBRARIES(ttf SDL2pp) 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) + + ADD_EXECUTABLE(mixer_effects mixer_effects.cc) + TARGET_LINK_LIBRARIES(mixer_effects SDL2pp) +ENDIF(SDL2PP_WITH_MIXER) diff --git a/examples/mixer.cc b/examples/mixer.cc new file mode 100644 index 0000000..c8ba66d --- /dev/null +++ b/examples/mixer.cc @@ -0,0 +1,80 @@ +/* + 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); + + Chunk sound(TESTDATA_DIR "/test.ogg"); + + mixer.SetChannelFinishedHandler([](int channel){ + std::cerr << "Channel " << channel << " finished playback" << std::endl; + }); + + int chan; + + // Fade in + chan = mixer.FadeInChannel(-1, sound, 0, 1000); + std::cerr << "Fading sound in on channel " << chan << "\n"; + + SDL_Delay(2000); + + // Mix 3 sounds + chan = mixer.PlayChannel(-1, sound); + std::cerr << "Playing sound on channel " << chan << "\n"; + + SDL_Delay(250); + + chan = mixer.PlayChannel(-1, sound); + std::cerr << "Playing sound on channel " << chan << "\n"; + + SDL_Delay(250); + + chan = mixer.PlayChannel(-1, sound); + std::cerr << "Playing sound on channel " << chan << "\n"; + + SDL_Delay(2000); + + // Fade out + chan = mixer.PlayChannel(-1, sound); + std::cerr << "Fading out sound on channel " << chan << "\n"; + mixer.FadeOutChannel(chan, 2000); + + SDL_Delay(2000); + + return 0; +} catch (std::exception& e) { + std::cerr << "Error: " << e.what() << std::endl; + return 1; +} diff --git a/examples/mixer_effects.cc b/examples/mixer_effects.cc new file mode 100644 index 0000000..28286a9 --- /dev/null +++ b/examples/mixer_effects.cc @@ -0,0 +1,118 @@ +/* + 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); + + Chunk sound(TESTDATA_DIR "/test.ogg"); + + // Panning + std::cerr << "Panning: left" << std::endl; + mixer.SetPanning(MIX_CHANNEL_POST, 255, 0); + mixer.PlayChannel(-1, sound); + SDL_Delay(2000); + mixer.HaltChannel(-1); + + std::cerr << "Panning: right" << std::endl; + mixer.SetPanning(MIX_CHANNEL_POST, 0, 255); + mixer.PlayChannel(-1, sound); + SDL_Delay(2000); + mixer.HaltChannel(-1); + + mixer.UnsetPanning(MIX_CHANNEL_POST); + + // Distance + std::cerr << "Distance: somewhat far" << std::endl; + mixer.SetDistance(MIX_CHANNEL_POST, 128); + mixer.PlayChannel(-1, sound); + SDL_Delay(2000); + mixer.HaltChannel(-1); + + std::cerr << "Distance: further" << std::endl; + mixer.SetDistance(MIX_CHANNEL_POST, 192); + mixer.PlayChannel(-1, sound); + SDL_Delay(2000); + mixer.HaltChannel(-1); + + std::cerr << "Distance: even further" << std::endl; + mixer.SetDistance(MIX_CHANNEL_POST, 224); + mixer.PlayChannel(-1, sound); + SDL_Delay(2000); + mixer.HaltChannel(-1); + + mixer.UnsetDistance(MIX_CHANNEL_POST); + + // Position + std::cerr << "Position: closest left" << std::endl; + mixer.SetPosition(MIX_CHANNEL_POST, 270, 0); + mixer.PlayChannel(-1, sound); + SDL_Delay(2000); + mixer.HaltChannel(-1); + + std::cerr << "Position: somewhat far front" << std::endl; + mixer.SetPosition(MIX_CHANNEL_POST, 0, 128); + mixer.PlayChannel(-1, sound); + SDL_Delay(2000); + mixer.HaltChannel(-1); + + std::cerr << "Position: further right" << std::endl; + mixer.SetPosition(MIX_CHANNEL_POST, 90, 192); + mixer.PlayChannel(-1, sound); + SDL_Delay(2000); + mixer.HaltChannel(-1); + + std::cerr << "Position: even further back" << std::endl; + mixer.SetPosition(MIX_CHANNEL_POST, 180, 224); + mixer.PlayChannel(-1, sound); + SDL_Delay(2000); + mixer.HaltChannel(-1); + + mixer.UnsetPosition(MIX_CHANNEL_POST); + + // Position + std::cerr << "Reverse stereo" << std::endl; + + mixer.SetReverseStereo(MIX_CHANNEL_POST); + mixer.PlayChannel(-1, sound); + SDL_Delay(2000); + mixer.HaltChannel(-1); + + mixer.UnsetReverseStereo(MIX_CHANNEL_POST); + + return 0; +} catch (std::exception& e) { + std::cerr << "Error: " << e.what() << std::endl; + return 1; +} 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..f4d6618 --- /dev/null +++ b/examples/mixer_music_sine.cc @@ -0,0 +1,53 @@ +/* + 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; + + // Set custom music hook which generates a sine wave + 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; +} diff --git a/testdata/README.md b/testdata/README.md index 5342e3f..eb20304 100644 --- a/testdata/README.md +++ b/testdata/README.md @@ -4,6 +4,10 @@ Some test data was taken from third party sources: * https://www.freesound.org/people/junggle/sounds/30341/ * http://profiles.google.com/jun66le +* ```test.ogg``` is (0) CC0 by fins from www.freesound.org + * http://www.freesound.org/people/fins/sounds/171671/ + * http://www.freesound.org/people/fins/ + * ```Vera.ttf``` is (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. See Bitstream-Vera-COPYRIGHT.TXT for details diff --git a/testdata/test.ogg b/testdata/test.ogg new file mode 100644 index 0000000..0c3ed71 Binary files /dev/null and b/testdata/test.ogg differ