From 5c1bf3e6cf47bd3ed5134614ec63ab13b41059b4 Mon Sep 17 00:00:00 2001 From: Vladimir Gamalian Date: Tue, 14 Jul 2015 14:15:18 +0700 Subject: [PATCH 01/32] Create SDLMixer class skeleton --- SDL2pp/SDLMixer.cc | 49 +++++++++++++++++ SDL2pp/SDLMixer.hh | 132 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 SDL2pp/SDLMixer.cc create mode 100644 SDL2pp/SDLMixer.hh diff --git a/SDL2pp/SDLMixer.cc b/SDL2pp/SDLMixer.cc new file mode 100644 index 0000000..b9c0b0f --- /dev/null +++ b/SDL2pp/SDLMixer.cc @@ -0,0 +1,49 @@ +/* + 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 ((IMG_Init(flags) & flags) != flags) + throw Exception("IMG_Init"); +} + +SDLMixer::~SDLMixer() { + IMG_Quit(); +} + +int SDLMixer::InitMore(int flags) { + int ret; + if (((ret = IMG_Init(flags)) & flags) != flags) + throw Exception("IMG_Init"); + return ret; +} + +int SDLMixer::GetInitFlags() { + return IMG_Init(0); +} + +} diff --git a/SDL2pp/SDLMixer.hh b/SDL2pp/SDLMixer.hh new file mode 100644 index 0000000..91fd2b0 --- /dev/null +++ b/SDL2pp/SDLMixer.hh @@ -0,0 +1,132 @@ +/* + 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 +/// +/// Though it's possible to use SDL_mixer without initializing it, +/// library provides initialization/deinitialization functions to +/// be able to preload libraries for specific file format support +/// (png, jpeg or tiff) beforehand. In SDL2pp, this is handled by +/// this class. +/// +/// Usage example: +/// \code +/// int main() { +/// SDL2pp::SDL sdl(SDL_INIT_VIDEO); +/// SDL2pp::SDLMixer mixer(IMG_INIT_PNG); +/// +/// // use SDL_mixer functions +/// SDL2pp::Texture t("/path/to/file.png"); +/// +/// // 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 IMG_Init() + /// + /// \throws SDL2pp::Exception + /// + /// \see TODO: + /// + //////////////////////////////////////////////////////////// + SDLMixer(int flags = 0); + + //////////////////////////////////////////////////////////// + /// \brief Destructor, deinitializes SDL_mixer library + /// + /// \see TODO: + /// + //////////////////////////////////////////////////////////// + virtual ~SDLMixer(); + + //////////////////////////////////////////////////////////// + /// \brief Try to init more SDL_mixer formats + /// + /// \param[in] flags Flags to pass to IMG_Init() + /// + /// \throws SDL2pp::Exception + /// + /// \see TODO: + /// + //////////////////////////////////////////////////////////// + int InitMore(int flags); + + //////////////////////////////////////////////////////////// + /// \brief Get mask of initialized SDL_mixer formats + /// + /// \see TODO: + /// + //////////////////////////////////////////////////////////// + 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 From 5078b44ec842da4bb4b749fa9c969a65daecfb51 Mon Sep 17 00:00:00 2001 From: Vladimir Gamalian Date: Fri, 14 Aug 2015 11:27:47 +0700 Subject: [PATCH 02/32] Add sdl mixer lib init/deinit --- SDL2pp/SDLMixer.cc | 13 +++++++------ SDL2pp/SDLMixer.hh | 15 +++------------ 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/SDL2pp/SDLMixer.cc b/SDL2pp/SDLMixer.cc index b9c0b0f..7d106d3 100644 --- a/SDL2pp/SDLMixer.cc +++ b/SDL2pp/SDLMixer.cc @@ -27,23 +27,24 @@ namespace SDL2pp { SDLMixer::SDLMixer(int flags) { - if ((IMG_Init(flags) & flags) != flags) - throw Exception("IMG_Init"); + if ((Mix_Init(flags) & flags) != flags) + throw Exception("Mix_Init"); } SDLMixer::~SDLMixer() { - IMG_Quit(); + while(Mix_Init(0)) + Mix_Quit(); } int SDLMixer::InitMore(int flags) { int ret; - if (((ret = IMG_Init(flags)) & flags) != flags) - throw Exception("IMG_Init"); + if (((ret = Mix_Init(flags)) & flags) != flags) + throw Exception("Mix_Init"); return ret; } int SDLMixer::GetInitFlags() { - return IMG_Init(0); + return Mix_Init(0); } } diff --git a/SDL2pp/SDLMixer.hh b/SDL2pp/SDLMixer.hh index 91fd2b0..bd2225d 100644 --- a/SDL2pp/SDLMixer.hh +++ b/SDL2pp/SDLMixer.hh @@ -31,20 +31,11 @@ namespace SDL2pp { /// /// \headerfile SDL2pp/SDLMixer.hh /// -/// Though it's possible to use SDL_mixer without initializing it, -/// library provides initialization/deinitialization functions to -/// be able to preload libraries for specific file format support -/// (png, jpeg or tiff) beforehand. In SDL2pp, this is handled by -/// this class. -/// /// Usage example: /// \code /// int main() { /// SDL2pp::SDL sdl(SDL_INIT_VIDEO); -/// SDL2pp::SDLMixer mixer(IMG_INIT_PNG); -/// -/// // use SDL_mixer functions -/// SDL2pp::Texture t("/path/to/file.png"); +/// SDL2pp::SDLMixer mixer(MIX_INIT_OGG); /// /// // SDL_mixer library is automatically deinitialized before exit /// return 0; @@ -57,7 +48,7 @@ public: //////////////////////////////////////////////////////////// /// \brief Initializes SDL_mixer library /// - /// \param[in] flags Flags to pass to IMG_Init() + /// \param[in] flags Flags to pass to Mix_Init() /// /// \throws SDL2pp::Exception /// @@ -77,7 +68,7 @@ public: //////////////////////////////////////////////////////////// /// \brief Try to init more SDL_mixer formats /// - /// \param[in] flags Flags to pass to IMG_Init() + /// \param[in] flags Flags to pass to Mix_Init() /// /// \throws SDL2pp::Exception /// From b5561e9b306811e8677a0275aa5be29810cbcbce Mon Sep 17 00:00:00 2001 From: Vladimir Gamalian Date: Tue, 11 Aug 2015 17:58:30 +0700 Subject: [PATCH 03/32] Update cmake infrastructure for SDL2_mixer --- CMakeLists.txt | 19 ++++++++++++++++++- SDL2pp/Config.hh.in | 1 + cmake/FindSDL2_mixer.cmake | 17 +++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 cmake/FindSDL2_mixer.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b65d7b..d81e622 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) @@ -162,6 +170,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/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/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) From 88ff4a1065ea973cc43e6f28937e3a292366c916 Mon Sep 17 00:00:00 2001 From: Vladimir Gamalian Date: Tue, 11 Aug 2015 18:25:59 +0700 Subject: [PATCH 04/32] Add libsdl2-mixer-dev to travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7df93e2..5cbb578 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: From a0843fbb7d42078aec9e058d4795f0fff398d0b6 Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Thu, 27 Aug 2015 20:34:54 +0300 Subject: [PATCH 05/32] Add mixer to main SDL2pp include file --- SDL2pp/SDL2pp.hh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/SDL2pp/SDL2pp.hh b/SDL2pp/SDL2pp.hh index 833477a..836c44c 100644 --- a/SDL2pp/SDL2pp.hh +++ b/SDL2pp/SDL2pp.hh @@ -116,4 +116,14 @@ # include #endif +//////////////////////////////////////////////////////////// +/// \defgroup mixer SDL_mixer +/// +/// \brief Functions that are specific to SDL_mixer library +/// +//////////////////////////////////////////////////////////// +#ifdef SDL2PP_WITH_MIXER +# include +#endif + #endif From 01ca026a252538a04619b02bbd67578d089c4288 Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Thu, 27 Aug 2015 20:38:22 +0300 Subject: [PATCH 06/32] Add correct seealso URLs --- SDL2pp/SDLMixer.hh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SDL2pp/SDLMixer.hh b/SDL2pp/SDLMixer.hh index bd2225d..e105ba7 100644 --- a/SDL2pp/SDLMixer.hh +++ b/SDL2pp/SDLMixer.hh @@ -52,7 +52,7 @@ public: /// /// \throws SDL2pp::Exception /// - /// \see TODO: + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC9 /// //////////////////////////////////////////////////////////// SDLMixer(int flags = 0); @@ -60,7 +60,7 @@ public: //////////////////////////////////////////////////////////// /// \brief Destructor, deinitializes SDL_mixer library /// - /// \see TODO: + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC10 /// //////////////////////////////////////////////////////////// virtual ~SDLMixer(); @@ -72,7 +72,7 @@ public: /// /// \throws SDL2pp::Exception /// - /// \see TODO: + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC9 /// //////////////////////////////////////////////////////////// int InitMore(int flags); @@ -80,7 +80,7 @@ public: //////////////////////////////////////////////////////////// /// \brief Get mask of initialized SDL_mixer formats /// - /// \see TODO: + /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC9 /// //////////////////////////////////////////////////////////// int GetInitFlags(); From ba7888ec2d1f00ed6b43eec596d15a8b3401c7bc Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Thu, 27 Aug 2015 20:39:00 +0300 Subject: [PATCH 07/32] Style fix and note for the unapparent code --- SDL2pp/SDLMixer.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SDL2pp/SDLMixer.cc b/SDL2pp/SDLMixer.cc index 7d106d3..fd8551f 100644 --- a/SDL2pp/SDLMixer.cc +++ b/SDL2pp/SDLMixer.cc @@ -32,7 +32,8 @@ SDLMixer::SDLMixer(int flags) { } SDLMixer::~SDLMixer() { - while(Mix_Init(0)) + // see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC10 + while (Mix_Init(0)) Mix_Quit(); } From c84be04bf2552e9e852436ef6a1cbf8bfff473f5 Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Thu, 27 Aug 2015 20:40:14 +0300 Subject: [PATCH 08/32] Connect SDLMixer to build --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index d81e622..38e7d24 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,6 +114,7 @@ SET(LIBRARY_SOURCES SDL2pp/Rect.cc SDL2pp/Renderer.cc SDL2pp/SDL.cc + SDL2pp/SDLMixer.cc SDL2pp/Surface.cc SDL2pp/SurfaceLock.cc SDL2pp/Texture.cc @@ -134,6 +135,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 From 7cc56a1ccb16b472516249d7664f7ad6e9f6d08b Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Thu, 27 Aug 2015 20:43:41 +0300 Subject: [PATCH 09/32] Add basic example for SDLMixer --- examples/CMakeLists.txt | 5 +++++ examples/mixer.cc | 42 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 examples/mixer.cc diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 2bd01e5..87c7611 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -27,3 +27,8 @@ 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) +ENDIF(SDL2PP_WITH_MIXER) diff --git a/examples/mixer.cc b/examples/mixer.cc new file mode 100644 index 0000000..a957cfd --- /dev/null +++ b/examples/mixer.cc @@ -0,0 +1,42 @@ +/* + 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 + +using namespace SDL2pp; + +int main() try { + SDL sdl(SDL_INIT_VIDEO); + SDLMixer mixer(MIX_INIT_OGG); + + // that's all for now + + return 0; +} catch (std::exception& e) { + std::cerr << "Error: " << e.what() << std::endl; + return 1; +} From 732bc1a7c66f61a2489cfe02eae541b87a5d7066 Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Fri, 28 Aug 2015 19:26:32 +0300 Subject: [PATCH 10/32] Add SDL_mixer chunk wrapper class --- CMakeLists.txt | 2 + SDL2pp/Chunk.cc | 68 ++++++++++++++++++++++ SDL2pp/Chunk.hh | 143 +++++++++++++++++++++++++++++++++++++++++++++++ SDL2pp/SDL2pp.hh | 1 + 4 files changed, 214 insertions(+) create mode 100644 SDL2pp/Chunk.cc create mode 100644 SDL2pp/Chunk.hh diff --git a/CMakeLists.txt b/CMakeLists.txt index 38e7d24..1dbd83b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -108,6 +108,7 @@ SET(LIBRARY_SOURCES SDL2pp/AudioDevice.cc SDL2pp/AudioLock.cc SDL2pp/AudioSpec.cc + SDL2pp/Chunk.cc SDL2pp/Exception.cc SDL2pp/Point.cc SDL2pp/RWops.cc @@ -126,6 +127,7 @@ SET(LIBRARY_SOURCES SET(LIBRARY_HEADERS SDL2pp/AudioDevice.hh SDL2pp/AudioSpec.hh + SDL2pp/Chunk.hh SDL2pp/ContainerRWops.hh SDL2pp/Exception.hh SDL2pp/Optional.hh 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..4029c55 --- /dev/null +++ b/SDL2pp/Chunk.hh @@ -0,0 +1,143 @@ +/* + 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 + /// + //////////////////////////////////////////////////////////// + ~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/SDL2pp.hh b/SDL2pp/SDL2pp.hh index 836c44c..46c10b0 100644 --- a/SDL2pp/SDL2pp.hh +++ b/SDL2pp/SDL2pp.hh @@ -123,6 +123,7 @@ /// //////////////////////////////////////////////////////////// #ifdef SDL2PP_WITH_MIXER +# include # include #endif From 9a2a741120493c64f64fb734cdee9026be5d5714 Mon Sep 17 00:00:00 2001 From: Vladimir Gamalian Date: Wed, 12 Aug 2015 02:36:53 +0700 Subject: [PATCH 11/32] Add test ogg sound --- testdata/README.md | 4 ++++ testdata/test.ogg | Bin 0 -> 114206 bytes 2 files changed, 4 insertions(+) create mode 100644 testdata/test.ogg 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 0000000000000000000000000000000000000000..0c3ed7151e4160de50c33257fcd22c2be563149d GIT binary patch literal 114206 zcmdSBc|25a`#63NgRwUDwVJUt7_w*0key`T$)0_ULd#gQXBk8Y*|#hat;ntrSz0Ni zl0rqv^1Vmz=lML(@At>|^Y8b59p~KFIrq7jYrn5^jZkTKm&leCXe>1Y@M?8i7pGX^{FA(gj?Heed@`zf1g}2vTp1E zAOQiV&vM(%)KFi<9ix*Lyr8H%bfg@oL+A2d@uTWbH=mOgWeavE^+kkGj5bsd3xI&a z#-dGiv2kqsCzk|l#Wj~9k;;PLA(4Je41@FlCdN?uQ`yZlrO$OOBTDq*x5qRXntE8w z{N$!yhMFOwQ&q*Q`1;Gr`AO9{31Bh^$pg)~W zuZeH?8Q-aO!BG}*6IRs;8LbJ~lM`}&7Ux1OtU@O(;o%O!33#>$waN{(Y72Fk{;%Ur zo8SJ@F$hpBpqT$&q|}9{;k{A~Rcw461#lyyq?on{D_Sb-UHZYRXes=E4x0TYkj6;z zY(TMBe#QUq@UY2o|Gysu&jC?DfVk`r64Wsj)G`(95608%V|V~Sni5R$y{5ALAu2j& zHT%yR>Sdb`X4?<9+B5!(2xPJYKwy>aHO2R5;!nxQS(vF>n9EI?sZN?({4Yw>KEJ?) zAPPOMc@t4GnKt`qA;NCaQ4D4be@B81bTbwN6Em|+^%WU1dj+dWuoljir6V`U^*>}J z*BCMvar;m8QZjpS)u!Zjq^u)ygEO%954mAGoAk^h;-7e6O)o8zTKvUx_%zX!gj0M` z^k4n`E+!q&*nh3#WtNZXevSJkX;trkx+2$x{<04N0AkP-?n-Ia#|uI6!4)F9q9oz7 z0hu=!6bI{Xzb^3aR;;{kHu8I+AS8N!|D2ZVAV1TK#Aa&H*H3c!q%=*wEx0YGBU1Qx(j$sX1%D+95)}ivn3~(24R#8- zcu1tGER*g(35OP%kUIE`nf)0vJd=;nHNI0ULeJLmCNipKke@Bwt#U$NQ3mZwu^6OQfC#?=Ktex{kP-*4JecQ;z)8yl3FT`L2HQoCGbCzqZP!~pTVb>DQM7) ze>x6FkUjN7bM%M#D7*VJ4!_Z#zRx!7PyIAM#cp9H=P+SG7MV#4cZ)E;S8e}!xc`Ub zWb%t-@++nZC}tx6LvnCQ?-kM(tTTnYGI4o1-W~a0J{3uPE@k>}$#F`E_e@ChOw7k6 zmHzLJe+%KiB}XS%u-_C9C8zhlB&Sn~?*x>lX1u|K?C5tDxJ(l^tqJp!?EgssC^;r< z!%cj8;}Gu$O(5!7giKiFV9Wo-0TG+MucBksHyvZ8ShA*U>9swR6j4~}cOr3YAc!EY}llnio`>wSheznlQd1_obz)k!ug;61)rHa=NIAfg(u7{DynPjt-`Rw zMMub>!#yly7M zVt!IE)6E9X#BAk}-#L?0?$S$(p))YbQ_zn!D!Ro}`XBLYvZLglw}Y?sS!Lx&)bH09z` z4?rU&4g8}o*?iqr=_sF)#`0`D116N~;}B$7n+f$|!&W zUw^ag)9;$kWQF>h@P83C9E2Yx-*L!=Msz8!0%B&k89ypR##Y6raL6-!=a~ zSV4d}S|&iClCMh(G?nGB_8ia$7)n5tqQ<&0Xbf=0qRAQ(%>sbr9JFsv8r(QpHzANt z2fek&*Fwg94Z5-Eomld2?{LZ9fGnLflj#AhYMyVQpQc!FdY zfhS0YC33sIg1;a%JY;aGAcXuHbV?}549=@1e}s!xl)OeXR2k$oj*wSDhwa0hHHOo+ zKta^RnwolxdyPQN1s9q;2%DfvNY8Bz8sgCUxN^uH!Q!zj5EtcKS*HB4nkIrl#|A7eWZlT-SFk4DcfWz$ZFmqtsHhKdLpj&`sHvv;(c5fdDC{aA|N=&;g7^>zBjj!int@j-(G!5b~FV8aVF% zc>w1$`0odB?&FF}n14tOU59_VB2(T}|9>8!Aam|>!~O{}?7!ZCXfV~M{W~PQ2i;Vb zf44IxdocgZ01gBh8w&@ACunK^vp=$@Lwy?%?lJ{r!O;X#BVPe?K6D z`vd)J8YPgPP%eo;KvcZPNTxxBLWfP-kPsJNhXmtLAGwz8cS$~R|GQRBV))ls|8b8z z^#4QPKbS|%Q1LPmpRsDQn4aObF7@XBaO8ly1l{d?Zs_Z>csq>=NHTV^4C~ z#ioahDMDtS@=k6##{`i|%Ct?9ntG{B1F2#Pd638Ti(EV;Ty-m#Yih>^*9n>uQjSYq z>y&-uH!=l$#QUzfjy%{*S3d&T*>$va>TP67c^O+sUDK4c&o|XJ7E|wfSgUW{d>ff8 z0(*Jdy74;l+k^6{;FP95`;e6fn~ovLO_K#a4T>9YBfqKky^L7y_+Sg0Y!>;{cWl1( zfwi}hw_4i@g1>Yu!6pxu9DUN@gOL3PqaA%OBcDD1#MdYq&_g9aTiwDSmfF6CWMI}U z!PWvI7XUq6(9P5RZ>lDV^j?D=AXF2E060Zc0s@i!%MiZ)CC>k|;L$D2D(%50Zth?a zb{R$HAG(aK*0f=sRL9OE_7x_P0e~RukhG>LIz=z>Hfw`8QVhu@^`Nw_dSYt@1NTDl zWR6lC%7w*FrFAc@AxL=g~dTmyhe8H*GF z#f_a~QuavX0*f>q16_Y?$ha3JK*tU+WF%NKu&~xdmAWVaYkQ{w8X`k#>g{CukJ6n~ z4zEKou^1#ap%=lHPK;soKmVTbRC^b#l2$nrw0^Kq0BEN&88Q6%d5URFR}ZA4Gng}3 zvRFxM?0@Ygl6U|R-KY{0JUr5FKAxu()O3q2>sdewX$e6iK-ljDi6#<>`!zC5B>vsf z6M6S<0rHj}E(*NI?==V`U`g=W^Kx@?aditie|qn<<7sjcy}ok#_rsqk5gj8D>`hXcY-Y98jUN$9^>fSlM+HHw(w?xYr<(t4OPp5K_ki)_ zjrk2#JL|eC>leECib-u>Mpvv>628(!3K5OTj*cPn^Ru%^8WmNEvv)@6Y6d50YNBmA z9|I0br-as_7Wb|y={DvUkR>CS&|@Qy zV5B96=<7i!mOz0cCIWhObS%MCzubmD79(h+KSG=NA;s(vz$oZy-ENx`p3VQ_h;9~1 zdLPeBOcSrhHX5TEL*ncZ0@Jz*T7yy&g3acN&n&9ak)`7eut<+>UYy+|?|oP=&eDrK zt?=?om3l18V$Noc>~zwtGR_#t#|vd>N(7IH7@Af+ymHj>b0Ra9K2v%(f4nwI9Tmk_ zj?-dR2*h9rCKsGuB(W~Vv2nffK}HJ-Yh)HvMfOyx-;HIz8L{$q_|wt@+o4G1E<^n- z|5x|p0z9`4#i+fUd=~N4=lT4>KgSxC8;dsRpVV`Icf(Xj>(5*ol3Yu8Z5CM)YhQZi z@fAJC4XkH@S5}egO67+#;kel%(?RKWNfB;0d9~|$H+NpVReRiQ=JCBHPxj@Fl^q$p za`?gG8dQ=)ttuTLj*X4R(V>hp!H`0b3rUv<+G*(Tt-N@@(ESPZH202n{F5w-XwHB& zom3=+mPuFTmT8{g5zb9bhv}b4J`3P0|0w@N?aqrE-)zn`z1a3jEZ zYoqu7SYJw`B(ETOb>q(HXycdra0L?GuVyc)J-+*$pM99W3Wc%$(j*5JfYRX~Jp2S^ zOyW_T&`JD(T!keDE`j$1WHHz3gX}ZISFvxp4EfD1LqOKKoa=h$X$AP zy5XtEYfyLmj&jv5#c8+gTHA~M=Ztr#9uEAnQY|eAPIfnV!PRE@m??}i$s|3`eVCHJ z`w%a^Fvom>79kTVc+E5mNz{A_r4+`+nae2=T+y(%AOAI!kS@f$L;zIC-e8}`Ax*+7 z`HS{HS`ME!-5JQ@@_Y9sBvq>w0em%1KTz~sTXYwDyX}AcbUmz>k67NHw4uaEdvKQ} z(G?qFb42I?>K#aD*B6S70vyS4Xctr?lFLR~ljstNT1Y_hLtmbW;vGpQtZ@uwdhT*! zd6xZBSEBJa(zNYtVe2l%D$Un^J6-xow}qF|VdGo$1U1CBd8S$_A8GG`gl^)xK|G%= zE+NGQfbDP!AT1;Fj--nWi_?bMU&^RgUxTdJk)#A&X$D%jjo|!L*~cEysI2{}Uia=b zQ~k%z@#uJHZnV>piGVqQ315uRGSyG2FlJ@YpcF~B&7lIgo}0#rE)3xe%uI)vn3$-S zD(Nm5JKf~5d{^R;XaD8uc9{5S(YSC{w6t`BkQ2&5CVaD*K9 zJN|le6r7qR8vMC9eNuM&m%db9n;c%2bnvk7MaPCTQ14s|_AjCl9u z8kP7>qYn;m`}IBq4Qk!971z3nLepSbH^Qst&hK zT@9fcxR^S&`|2h3e_7LBOd12&lk7EK*(6B0Yg`A$4Q6fm`0j?%~k!iJ+sD zvC@z3bKc*1x8AJtXgmHd@ag>EW^Kdi95Y+I?twPEv3b=&UIYxmGh5slRnP%;$MB>+7Gl#7zAK!dTBrsMRO`+{pbgj{Uv*s$=`M#MGFCBB| zE0=z4HfEMIpqHL~N|%tCZX(3giAe;K3K><{3^cO0RTJsP%%&BH&SE`cWSWTzc2}<{ zBMl{`ua8I6ak{PK&oas#-OawtC%VjfLFs7Ush~F89zK`bo`3t(eJ-`$7p2=Rcc09$ z%(2J0u!+SUv>;eu#l2fy*(WMYY2(V9T|i2=h#909NC<2a#Pww8SZ7F4C!`Q%(hQkx zOo>jqYZgZFDo~L^hs>D)(+wSBDo??~ksC|rGh@Dj#oV!2*DF`{_n{3P&wtIKyoOo%kOWPs|pVLROw%vc>u> ztku0j&dSd#OiWU^xcI0fDk>E= zUE_C84pUKkc#;|u-eF^D4kP#|!qO9^O}d2y(J>6D9?Pak8?<1AI^I)&!pV^ubnS9! zA(%Yk-9+5=52?rrN;7vz5C{OAw7ffsPC{#HA}J8Wm@cPeK|OX=(3lu0zB%twdizeM zd>Btmm)hH<#bTlC<1f_eJjdRgO^v1uT3l!v*j~36SfJ1S zDcQr<&FOb5#xs39F?^Hh5T84d<;IFB9g)m_3G3-wL+&$HFhbfbPGt(LgcrA@!a z!~oEkZUDi!%-dBKh=SGHS{ zLanV>ihK{bt=@il;P)B1g_$Xf=xKntMPrtkzP*@Dct;YaU`PZH42w55`tD_CU$#je zWYH4gf_rG9Ze}A?P(YI(0ol@|d$D~79~faE4p}Z?XzbyUmys`)tFanysPFMH-LEig zbO%4gYYl(?^!2%ZW6@WqA^rOuUulcnmU{QgIn(%t^m(h`CpeL(=TJfx%%rFK5W#gD z0I*9+x^tviXmVDF$k>}ON__KY25clfE-s4wMH=!iF#wIC6M|E(R0Q3Zn)|lG);hjz zM=(Wx;W@MP?RR*G&d+E{L-}*8XBA&vT^}6}n-lFyeR{H@J#FK}md&ZR9QIVpq2Pdr zqv=ccs~!h+6TELG z{!)GC4^^obA#H4SzPsC)&hCN!54TK-xWU7+qE?G>EpfXo>C4-{&Eyv?io}i7hSeJm zjVcjrh}{kv9$zU27Daf$P03mZP<#PFVH?lY*EkaLob)-E=ePGyhI2u9bh`!-A`}+0 z&O`tlZynQVV>5c^?Cq)y6eHR>|7kk%#ApIA*UX9;;d6;IIN^ z@;mGbf4=AZs=D{`$2sHit9Bcfn=WP}fxYk8n{S(AlN94LIO3Z3X46V6d)^;CX<}-c zK1@wC|D~a)PC*Cs{62WRD$4i2bg0aS`C*neH#6c2z8~2YnlliXcA^VS9iK=9uC&yt zBW{>jEK8y_U$RM#7|K*+;9}r>)-s{x5=q)ne&^#-&=hjk{m!M|d1vDXM~53|FXq14 zviRmQCe_LZOolo`B>fP&~IDIQ26XUKHZ`jA!Es$Cb$ zT-^79!9MC+D5u40dlQk>mDmI{#C~xZLmXirN!M_BcrhFW5<~e^f34r?czfm z`-L`h<#-?A%L|nu7PLZ-s((~&z7MnhRDRp>CCx1LsJfO`Z|K7#T$yt34+BESA__5p zPZJLaFC|A&jYW|lKa8>D-WdEI&4p$TqK|h%(KO)7W8pWu#6)(xJ`3{~8hO!Z5Su

$2~stSqsU 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�%WsUmJSu}bXK?Yl@r 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∈9qRIgit0-YgkreAaQ|xbaDS+3^kZf$1D;~Maa&6gu>(G~5kv^!E9TSb zXtCDnxGAXX6OpUS(#^#SNQy4$#K8h%f^eO_Sn@pLK=Fv?ZKH*rz#W9($Y_FoIwAMFGQa0u{n`j_P!*0 zR)n^@%rJ35Wc-0HGsp;`ae5{f6kdN}K>7lM6}DWas8$Gmn9Ls#jF|_2+#)l&r*8HK z+rk~PO4A$Qso~|1$KMrZ)f(tTTl+dB(V7JvNv`j_s#bnkF7Ji<^nA}Mk36wY7pNp2 z9=?E!guWE1ZwO|LhE{sFkAgv>PQbJ+u|fPf-sN!>jjL(M**$o*fd)u$llIOPzrgZhwpzl8`xCsF)YT4uHnIq#Z~`Le>nU<{ozB71`t&(LE~tB=nems zfc(eK)c+Xyk69%=S3cA3yEM6q(i#}|m}|a#=e#r~mKk?j+=7f5k5pZ7A1H2E*4Qkz zGyL#Cp@0V}aUTNrZs{JEQdUl?*WAPR4&FCy&*)QEBGNFCLcXD=C7jcJTTC zV$8lewp(!79KI8}hX?qfL-0&f;wp6cEo|M~^(X>%OIUK%H2%)O<6nUv%Ao?0ahL&$ zVCFz%Wm=k-D?Skz9NB>o((A!gS`3k-dV`1ygqRbKp(WlC>Fjm%K=T6wL3Lbp8}o?B zD9$0PLYKg!?r_b2ev!b#puZ@VIJ7uRQbEuFbtuQL$N;<#7(5CM6yUmX#29iyp(KYV zP@wp-r^%Ffb76Z$YTX`<4Kc5v^d$%DEs^7W!c;l@b0Ycof^z-wZ&9{)6fRW^g<`3W zXbQfGI-bAPXzs}`i9CdrY7H+d9qwwI6*YHf*H#y^B{Tpq*=tlt9h$ZavU)6tRY8Iy zb}haoJ9D488DRHGW%Dc`dgT`QtgZAq&%d>)nZH4eHGdnVG1YJX61|_Ef+_Fjn^ia; zCf}Ix(5z^&ft|!nTl+jXWxzGEwiS=u&dRl(qR({Q)qn&FWFGt=?TD{D`)+(Fgw^gQeO zyEJX1$#~E6h@y8L!s#+;c~wvV@&$KZxYk_iJ4_l-8CxXI{$YQjlDwMn5qrp#M|jbW z*2|gkKzFeC9@WJ!NP>68dis`TbA%ee=W)}41L_VAcq_vIqN)cl(+xakll)tngiIP- zNo2;=ym!6Zysh2LON`e$co0-VynJ;_XGd>$Y^50Nyv%)~&Npud>R;(+bo?XD?XGo< z^XxDJO6K18rV6^Ps7ze+MnAD8`DE}ovR2)`b7&5GN3}g%wtn6x=^K_=9rZc?(P^v` zH#=^>hmiB*C*72^?@7US9G7A`4-;_s$1hBbI9cRjyeirsBT8O46RvzABL!654ouchz=(^+)zD^8Af@eFoL5c7DAdKh3!$|R_3?JWf z!zRJKVDgXDE=r{#%NkQZW5PBEoXqYA0gypgg7J2{FaypPe@V~5ii*SNZDpGPAA$gA z*(ie=_IRT+ZtEPHIum3-1dvYx176kj0oIi0>v5uTymg*e5U`-LJbt{fU>6n-(x>l5 zrO-V~`%_Av0cD5c1aVreKv828kPewCVuFJm=lVL6?hH4U?ar$S zo0YO^N_sIJdc4Q-?zI&T&8r#m;QcZ5xVz3PjE5)lA?}wHO}Zn+e|%}a{T>~9Klh%l zA&WZY5#<|BSmv@IyH(lXe*@x0&(_T`EXJN#`Gc$fZQ_Ews|%NuunY61RAKR>!PBpB zEujE5jrx|#R*ivt0}<+wnsq;?K<866U&{8#01NCk4(V!Kn2J}-1;Syd3vLBPL0uAW z&;&(VJbanfZElD*!N~BEOYRXUbuZIFbDr8!ui`8LruLQTmnb*b5=td zQWUMw^-d<)&_7vyBOBq|-5x3YU?G@zk+VAVoQD}*$4+G*Iawd?MXIwUAF5uGQO$fH zgz^B2!nWlJCS@tf(%jUBOmyuOF)Y>5(s zE*dG?-sroz9HPejjwRzFI;qM~P@gip)b;ZE0z?vaR!f$hq5b>q8Q%~K>i(yl_n$G^ zu-~7Tw^z5`lz96z|ziB``PcDz#x3}#e3of6QPo|ScBu8+^W-*~J zy0l^-)pel6W|?6%%8W``FhlR&&ioe zF$IEPP4r*H=c#z4%DN4Y!Z?Eoy9M~o5w_7tKH5Z$zT-i507Nan=y_H?Qb?XZLy#QB zsN0n2EC-Ic zKMh0<$K{GrX=V+R>2)djpMqm3ct03YV`b3Rfk;H(3X|f3@o1|6IJu=(y`i50BA(mW znNWc3TSNkRtHk{SqNg*}Vgivjfu1^z=Yb1`d;D>x2b5ce0iMeWqd(_hcO%ZRp6aqU z(89s=l`%@PMu2R6qsS6n@gv(aY$Jpnnyb~W zhqSVgEGegEN1mH^A;!O?3)KIHnFC(-Y<=Jq>JF;zH;Q%a7?zwaN~&(o7}^NpC1ta( zOOV*oA?QoY^&;<5RKw`d?$J{z3O4BL^H5ot{(5QTjL?#1s8(aFWW1G9CHO4lm;sZ; z6*Yh52^<)ZexRMyHH|FOE>G#3Z7)&akC@!N;w*zafvn8!Tw~Q)=j*#(Q*O?3?W-#u z28Xgo9Z2vRH9|`kXIa^25m(QycGo-fs6&MRL@>0r2E?|0191#&@cAJKeO9u})-MmO%NO_gX&zG#+DCtCtVRMcrr_(N&%)rv-o>d1i2eznRi8?3UP z9YodA0t4E65LFw-LGQ|^Yvaeus)+>)7)$@RjXwA@kBRVhfO2erf#w+e^lFvgQZ3kV z>omsRkyc!i?k#lyAF}95B1Mq|hN&or<^&wR(dEyyv000oV%)KW| zB>(`2=0+b55GX;Qj1%kxk}Qy|+*`Eu(yBMHL`qZMNcfZdhV)fjFYe6n_#`jZLXoWj zn(xL``0H5?A83d_MdC+dCqcZ7WU)&@6ug9^20*#!3o@W69m-zDh$xL`cqA8AZ>|#= z>0{M=3-yuffLR7)e)Is3N1YA+bimWrx5!O)?pUAkb1-_Ue0T4F=_|8v=0I);-j$yF z4mZ4EnHpOIXdKD;Zo|IV(tlS1#4fA1=pEGgNr1Xu72w*{E~-}_WI${ZI$YJguU4hV zPv^mY4HGH(1UFA2Nx{MH#+5#nI4Cvy{sDdBb#)X366*f|??=wk8WQIPTQ@ODPUO=& zLl1mG(I-hylBq_FUF8aNQ2jiFi}a^=DPRDs=9?xMkW`xIs#p^HQxV70awi%9;joKjwQt1J|=}qol#qO1)2!*sq8c zw#c=Ptc1=De{q%Ob1O%0 z>)6^mRcJ!ho%OXrrP zwr!@%P7KHAvAJBSt`v%KxFeUC+I{%h)e!EveWE3&X~?^}yH-2n*=PbUo`3H%6~MKn zeNd=sBLWxzB7hz%eNs3mfDvPg9E>Z6!wR)BqFJPQRmbzvHVKFredsS#4>dn;8ASe%$ybl z3-|C_F=@moW2JSCJghf#7#jgJd;{owOUB_TwU--}^upLsN1J%J??Lc(E|U3I!}R<&W_Cd z`B#RadWOFwj%NyP3{9fEbb`furVENmnF^ONFMPhUTdRZQBB{>!!?`FN7Zb>)zbYsS zjfLzy|KKG%&6@T@I1RdEEpzXiJ!jS(qN_SxX2qUcVZO-g11tLbN|-67p3&ab_<9Y2 zOwBDj5B1R@sY+Rw6YeCKSs_O{;QcGfrG^e^x-!_O;xFlD}Loq6Rz~MVlmxYdy zm&XCOeZbBR;FoXphU+aiI8p7!z6GR3MVy2_q%GF^BndIT&EjpKS~jmM0GDqPjGJPB zXQbF&|GpTL2=0{np9LR(d-<0mK)C=rP#yZsQ{~WNmRaq(Y-5Pvv~+bKD}dL8k$$m% z`X?wHx&P=$tAU0U(MWND?)N}lu^1FU z%la2VMYcaQk6JG#Q;XiNHPfKGHc4ex`YY2QJdo|wC(l_|0A+*$Kr62r4d8g|tuQUp zr&U2;(URBRq5E<2?ElW~#U{u5EmFVUa%nmbqFGGKuDpM$X0X+LEpEW5xTkb6>)*fx zh()&PwER`Lh_U}U-EmBW!9<*ag*}@i@;8$IRDyziG)&%u_cA*Ue!*6@4j_cD0U&LG z6KaVAUktM5`N5xIYId@i$gM))B4M**K ze-7+6YVkbxcto#t^){%VxvX9{AVLci6#)fwStg2~GMV6OVv95X5j(SGq%$C9VP_v!MhTZDddsV?1mJsrfG(Ld33fHC{nHX71`r`)%y(<;ysG^9Ej9#qRnt0r z;OFxa$CO1dN{Jq?1%d2#I3g*{X-eyRv>#uL^p4zC(fovkn{TpEMN^5z(j|@lr&CMtJEkDFJE5(x^*Mt& zULP4T6yKZ1=s5ZZcA|dXFt(7YN@e-JlnzdJw`>s-l7;ns8&D`Bq$BV5DCMdGMSb;t z3x5u}^DzwAgVR~s9Vx!Q03cetJSf<;O#)E>Wb3|fZC-c8@c@iMj_57qSl_I5a0p!B z`&vGMTP|u69&PF8K_HmH4+=bweO0^0{gK5_=nO>JymqNp-XY`dVzNzbG)qAr`<8BG9XoBOa0k~Q< zZ{G)%6eJp#eY)J_(LPUSEV9P~C5s7R7ryDWPYFW)gx`PK#&fhwUYq?ZdHkk zPz8-o=)3D)q2@I&zfs_td?i(HPR?VeVaQ>6&q;Z9kt`&VKjYa-d<9wd?eLTs z)nwg&teTXBNQ(GVR-&XiurscvD(*%zc}>@=ED%-XS+nDopMm-npyJMKj#!m|4!;rR zPdWpWFAogYR&T0#(pqQEOIi97;)P`R6&H;*>ACS5v?>wqhn<(Szkf@akauuDGo;*a z^fuEPDLC_%)u{M8Qz*-2>;c#5tW#q5mT9uYy~pTo*epp9;!ry3tlkGl#<-l`J|A~> zbMIl+5_5BsZ+`O6u!LPg*V_JRW-}WnL1$r#heG`8B8POIy*paK~rTg!)RCu0HO~9i=yDxmO2u^ zk=Mf zP{HVZwNEXS+bB#U)}gPy=ORBIhORqC2XGk}!ThR2g#rX>865D998$OSK;f~_h=_!D z>9*FpN;16C!Jl|56Jly5ZE+aTnumRXHrlN` zzDEjZ-Y!34H3=UClx@C)XLL2?voXmv7i@DC}gd( zmMrv3tV(V^LwMmhB!OW$28zz$&mlD#Uuzo>xaef{{R?$Bx)Vb7lbH_6Ta1EjB+m+4 zUa!NWl+|4hP_zP4LSOh7RxBhmtsqIiBL|qVGc{;x}dEyYtc}a&j*3 zjv3!5IXd{O+V6N<50s4Dd0n(1-k5*sn!%}38^4Yu{fbh+Sr;BQkCt}H?~}IQ_O-1! zgYAj7x}8!p*-BQMNqjX~s`wIyARG*THmvGjNO>I4!*yA;)n^ zh}8Fq(!~$Gu--e`W}Wogg6BX}a2sQ%Ul^h%YbM3-!#6zZw5cn({zpdS5AAIx2Elpa7-A09b&OIrKLQ;T;y@vZ}c2 zVE!hn7e%;lGk_!L4du)pO>KD51i$KB1W?SqSowCDxNU_z=@&jW7lnNeUS49%^Zs!r zd5avfhm=$m>zyKK67hkXH~*OiXPT@+4lgS{47eBwvW5$E#7}n)TIdonl|due^(~xb7`-g7bonAI1mVQam1* zAzZZk7OK=_Tt!XEztXz;0kqrU7d7k~afoTW%F$r5y(`@IF%2ud9-_wxY9=xK-l~Vc zniItUG9&G5UM;$^BId&hF#*CtDfgSXs*9uZe$@qQ&5ERsjiv(*V@PL`&daVfv=VA~ zQ;uv+`sr+*^5ze#ABJZCy0TBGMOGs`@*h}&;Q8|V1|SX>J`@S}Q?g?Gu&tt50k06= zImTGL>VMscxD~apPugp&%G9J&8ga_6dW2g^&C_$uM$#Bd!SzWjVUP+E?ho+21Nv3Wxkq6t-5##79?xIo{tD+sd_)l&TZ(bEm6W4-oBhf+-@Dh zn+Z$&6ojrV3>h$Iv7w9EBBHz2YNTfZ9bOjlsS|{o5*g;3`u}8jE{IH(_PqZZcY8jA z*Wv4jpnjdKUTgbHBIUW$fzIy7S_zp)O8^t)Q*aIz^UprDf9;1=6_(JL;ck|Dp&ayq zEONQD%y(n7%v=;!qdX1Sz~-dMtlIT}-y2I`Oyj_?UVGid~Gr7V1|CgY%|3o6LQ1wT+} zMX-qm&&6RqP3yQ$R4PJG=lnGY?7RAz-WdV-tvdl9xK$sc1ChPLwj4p+enkQFz`}$9SvNo66EuL@DHHhdF^HrLC-61?&!~|Z z7ztsT^aPaay`X45XwK5}h$A~QSW z@NCWMHy(#jUM&>OXm!Lu)(?&$?8(fzh&j!5jJpRyO;CZoy*-=z!a9GpQl`-2V(s%iOi2`vxDBUC%eSACT~_w+`)qRJX7m+?!&0k*l#8u09dWw^ShU z`)BAJ>z6O6iOyfYHp9E1}vp#ui_YsUPTW-?Qxs`8p%K@}jnZB9;}9 zIkc$Eyd(e5)C2{+u4@iAjHTRJF{JA4>r3UB*iW-|S5;ZytB;;ux8}+Mn3!!k;O+~F zd)})f0m>eI^)LtnRbO+Z$} zcT@D6#dEnZI8GXx)sl@e%AIN<57F4*jn!f}>R12il>LU3oh~;~w&flj6<9bkiEu7N zM8EK>oqduW9-=M2MX+Fo>Hjp2k6ZJ<3l$vz_`n4{Fwht z8a-6(LpUCfnD9nOuv#F+^?E93npF#;AXKf zu<5Z_>)9`7z({%_Kp8Lb5nr!};equ{;^QA(h$<{^lBH_fXp7?PF|hEVS_UK=-- zO(nBWN}#D_ZB>5?0Zl+Hbh|&v1xDmuJ*?=$<=(xYc|@MvHbXQ?6lVwr0VfZ)xCI81 zXn>Vmg4Dr>0>g`DbQZw>3wbVv(I)cq;6=5Bn4YHv0C}gc?j&fu+T(9f8!Z!Xm?RbJAuD*IQHWt?ACexSf zKR1lBO2oEaJrQdTirVFpY(&`|5H16s3;;*zo>oWMLQyDyg28uzT`bBofs5w@1s4Dh zC{8i}0O&g^tO5G4RaYFn4WEqkPbVD<-7muZr(kh?u^W!mE!!u2?_52ENCt?-?;!tN zFMN~i2F;2i*~&!3q-MC_$!C?8zh1C*KUkhv(j!H$JE-~6a&ys2hjgB$2h`3TEh=S-6gu-jUma z80UQIPtTHY4F zwj$~H*rDG=yz;48p0X#MS4qlK&-V5;^9YIEgD?C}&IOJ-XC<6xzSWDHJ+@w*hloqb z8{Uck(tj6lNBMv9|ALP(>f2(d6Vg@wHP+|j3Aq@XKc@q@m3qBRwEmAqeQQ0ppLbH# z<rB_rRh*0B*69(QUi+8y*Z*uW>%Bhf zXu0}|h8tZ9!~ST*u~R%&gTFxdOi|a#;*&fMuPaj8LN?-Z`@_-Cy9&eDZ{IT#d~NDj zXJ2)-)+}EFHTo2i8}YQV(jR(d1JgW~#**RCHqy5#5?|oH#@8Vod~+9|{_o#3C(WSK1jUK+9N1{VC(bVa=-`$K&Lo^?M z_xE{Y{F}d<^V_d(!}z*RzxX*{j!$gwlb0ts0}G_Tn`y*)PI;Xcy~}fa?=|A*_}9_a zcv-UzOB4UwzOa+EY?4xR_Vq4Jdog0(v5B+%q`Fmll_7wrp|1hxJcYyBl%e$En zdH4m;Kq9lP_>=zt0MgPyBkaH8pPxwpSik9y?FB+_(+ z-gQcB3F^33l`SG71R?iOwD_YWeb&k1m@5NL|5EfEth#s1*QQJb;sw1OVjk(ub?;vVDdpJV-e~ z_tl0&i;Wd6dU`F7aCO>jC5hqjnI%nRd(-|MSk+G{OaJO#u?0 zmr{)`=?iFN0@$h@!235^WCKMi(Y|!_*cw7q#o0}Fg;R3n2^Y!y{rGk|5;^UMQ-#-M z6MWRJ8)d6w1d213V3N&^wRz4`kyCAjKdpJz2J5fU{un-B_hZ3^I>z=^_5@+6Q;?Q(uR3ztjJqzI%R z-|1l#;s2aL`}~u)Ua-8MUd|2eMQKd~Ke}8|V)p0ctF-pzJol>r$q3Lb4oNLrNyqyu zR%CGU^let<`89GCW;;ajSc3_1g~xG?g^*Bsw3yZo2*KgZYSJ*2H83<{SJ3b`w? z$2di$Phk`}F`l(LDYQ}A(_#dahZ-jB?qTBRpZ(q}=246sZqO2xgsTC-Hrw^APsREG zn!X%axXhbiVm%g?V&?7{9Dea~uw>>@eRkYa{PkU4p4SHYkKHL=wBx@0{*@d47}i~A zLzO*vI$Br+y#Mo|g|3^mxuJixB1cdiPsK0*qQ5 z6FO+Unm~pE$pHjan6uj4e*l2WoZ+Lm)Bx&YDD!*KHHj8jx@$Q|uK)BPmh-Y70nCwVo?@Zu*zL zW@hJUiN2YX5u9xOf5;>9Zy{fh@&OM? zX*fn|12KHIO2rdkT-P^^4UP~f2L~S-WSwd8_K}=l$eP2rmW@?ERo6dbENvF==YaUm zXESn__qf}CZa=B`S!>3Y*}oF45U+A+E=BT6HrwvM4MeEMd!_7j>f4=jW)R+^!XI4wp%es^#R@KX?|Ju=)?)qoE*V(79L8Idt@(L*3;5XaW1WQ8fD{7%(bzJy>3-(Hy;aNn zi=tX@i@Wx12WaGW)M0-wlP+sXGcocDB(%~BeD>OsC2P~C6`2c?+gvNgO>Aisn^ehUVkh(d^aZr|79!n!kT zBR?=OWVH+9M2Qyzzx3ZPbDUq-U1?fhKgRWo{++%mhmQMwN?U2w#S7Pm3x324g5M7l zC9Z@*>b`*$>(--7}>dZoDQ;!AI{mDnyAikfa!&$2V(|TA?1Mrof8YsR{ zPq5N}4=y{Rvi*AcAGZd8Df8PznH^PG5d4RsaB~ z0)VSj>8Aj|#_>6l;7GCrAW2}>7)p9(#-K_lNvzZ$EP*IYTm+=sdsNqml-x4ZZD)4M zE^cH}>xe*_HHn!Fn5-o|6Ooa7x9gz+&z}qw!;lx`%0)K9M8_Q{xK44Afl}^-K*M000mK$R_? z8|CPBEAq>PUh4n76D1& zPp>Z{n!G*^QC*j$#mG`>zi*ubk^g2<`HuSdqQFmmYs5&KHOS`C9Kv--bj_(~!UWc^ZY3vxwc+}u`(;&yJv7gSq)QO)Sxe z3^E;&%jj;rWp6_yILEqYN|UOouo@BN(=ys|WvKM3u_{g!wj{rJv|E!XD{s)0NU+V7 z`*fC*NO!yB8fZ=E4T=v?DOizjpe)OFIvEpzmbA-J%JOyzPzpc+3BUs+fCRuP001Cw zh{nb6uyuLO5h>&YXpg`SWH_qMXE~gHw=!m-1UmCJ%}~$vtnC#*(G?q^E&W4sT(NUq zvlo44T7Z!6x4SFr%dB~K$vh^L?7PhV>~Zz2jc&u;87pfDwJ~D85~D+R(`xN7Znu3@ zB+_ei{%+2$g4$K6B-;*$j9*ITx1VcRBpHJAefZYMVBph4+uLpC(Hxzr(V1|@6E19S z&6Ahn3MmRXAX-iLak>1J4wv<)g{IiFly2YoGX7ix#&^Kac$F;bUHZz)e9yyr-`~ymON!d4n553fv~1-L z-OjI=_r-nddO{~|0h6Y5D5YCM4ol_f@)}kt?K%<5ij}w4boHKo zPyOsoXB+*y=9nh_bmqF$>irxr*17sSCXUaz-|0sQt_{uA?fd(X%85RnP1I_g+VA}(UMHeYXJ=CY0l*>x00000 z01OWR02%-Q0FBA@>=aS||NmY8|Nm_N|Nm0||Nlw<|Nnb@#`xLEd13GW3vPzyJVEu8 zLv!I+zT+zkLU)@eudl)a$e{`E_e zaFD;AYz^4fHu(a6)Kd$8U&c#|NiOu@4N%lm?4LW^jU)v67Bs-%K>X^7=bO9sp8n3R zs*)cb|Ea6y-+KS^%lyax{vx2N96$S8TpKE=T<@l>wDjGEtj9v6%McjG>02+~x1V2r z?7@u5oa0t&pg#kDh2Z)J0MIY30Ucxomk=ijM9d1ip0Pc4YUlt!%y7&1A~ZlU4q(Dp zVG>9HBo!269tm`>igQAU55G{`SfP=ep6oq3bJ~m0i284Oa%zamP^eq0vc}cm)6BfU z_7&3dLZah_y#vrL)dj<*aJ1wMJwsWt>1HH4I$ZDoJrXe787U(LFNH$lOAWWK3;5t= z_T~_1MPW36%Y%;sOH{g|x z?)vQaBLY_!ff&EjlNYe<*EhyV@0JcDAh~!|7n6yT>5jsyIO>*wx!YLb7KT8o>^j&*RNkM`!^d0cgn2) zoWIrTQmJdnI#z{q9|o_ zbF9PeVY1lKa(!{X>(ztRaK7BhxC<}OIX)UZ7`qc~8RCk0tEG3N{vr$q*X!q)hmvt< z2Qw%9`QM*w_g~ewk~DgMH{Q01LN=q-^DJRjpo_}R0Ga?B9>6ikR49;}T0f zh5!4`jzVDwS4jzs!<=)5x$>w?007^Ae!1FD7En(YZ?Dw=?DqTE!FsY5paGcjSYOY7 z?#%>%lX4B^>ga6oKkfa;@b%9d(9zpv0dy$Tu+Z=8ob8YG1{F|HP>EN84^H~dj1k5P z$a~e7xidk9YLskA*`{P#iP%y>K(a%h%%lTurAigeW&=X5lGJ&ob)uUnClnMex3~5) zJ>Bp~OrDgqM#uYR>fNk6B1V3Ow)FHZ;%KJ>@u*%&sler~``vn+LcxQVZ#oAe5)(bw zBtS*sEASwgZ0d9vHw*wkQM)yO1W*8gY5)Mv!T?`-$CWkdP8gKeBIzVJXrey0D9$ z4Bv=0bnyvU>nFM%e{Yh?H7RcFE+_c%S8J5prTqEw+3XpQuCePr!(1gjia6A7!|k_Q zw*no|FlwDnznaR)>#NRG0~os70)1X+)2Pvd+jf+N*AKia%!45qfuTi0+lQyTt`CM~$`YIz@r z&gQXfy|ezC!yT@<9@3N1p>N=8b5PyYaM>K4-)c%Du+p7nI4Jqq64s5nrF=sB4L^ZC%P$u0$=9s8nG#HX@tr38S&Pv(nUud`zA4tD zf131t{~h*+HR-vq+m0TYpK{Q>o7s+epDRB8oqDY|njaFs&HuH!Q}IkXTm}b5U2ptN zd*k-^R%*`g)|X#Mq&}~<&0YJk=-jo>Z!>&+LrWkOzWkhaGzn>lM`61@ZL-i#<;C*W zV}0+jI=|N0zl152%kAPt=^JQ4y0$5QnHtxd{Zc*pbQhgmN_^)lWG8YEFcyR5uGE<4 zSY%w}rNs?W8ecvPqq}Vm5gbHHRx*7k0Cp3-xdz-n!Ri;3;&l|AX=@1WbtDAjrYQ!S zE0kgwpV*T!x+3qe@5i$&d?E6A+U`%NO&se-cUF_AbwxBCZmDC$w3t17uF2q^Mc}(< z`Teu`xuga#GGH`9H^-hQFlZ)ca~c3@B>}$Fu)e)(wNsb?(Nuy*!n%Qmc}5gP3xQ*Ux_?$+*UK{{X=_|OUw3I4hG#-8+EtLO^eu|HJ_%9lKyq@xS#`S;TE;#!drkg}jdxq$HZNlT9Iw-uzJ%T!-4 zZn_T&q9j7}$@x8AZ7m-EzS?$kklm^I>-C7=>z%vkM?2o7-LRNq>1=#`{Ef@++h@JF zQexK|GnerJU$_+9R~F@25h=f`w7AGs2G1VE$(L?O;M_U8qFyY<8fUI8J&&y#A)htj zl;>3~6el^mO7pe7ogSxcR0|`A%=0oI{>-_)TQ=5<&kNxtHDAa(yRV=BF6U?H%oZ<= zg*L#~o^@vP4D^x|;u?J6pnn??bySDlxC;e0*^& zMI2e1Ep;0Q8^v}fU*ly}j)i1S*`0_vJh%2JwKBhgFOj@>k2u!TARin34gT?N^{~X_ z!f)S7*hwISkrSIsK{#S;Q5%g+pSI4|B+)j)$~N(F9KZgyU-z#Em!un+*(C3Y^69Tx z4hpdz_IoI`dv@bAnLe-Z6hKo~@x*!=UuJD!LSut=#RgkONZCvP8Q#R>lwUt-Buf*9 zaL14WXyOt4uM*lL5lF(GPzq!yPDP%Se=B^Mlwk7V><;kzRegBf0Cnr-8QLtM60^Vf z?<%YPFw|o;Pyw0n{nzY2t^k0DOd+5pGB*8Pz8WV0ob44lKmi4~Go~lSTkjQsI5}?( z0C1%#xff<5Nc4tgS*XqO&XI1&gbcjPpYe#ht`bOhlu@$9kr}yZx97(}eyvOVHf!n{ zj~9=mUwU7qD!G3`Hd`Z~*)RS+|EK=zNQFu5U)2d^RJk0mAxO`GnF~Y`GMoxj>S}=R z1M~*~PelQ21w{b`An^eYAVPsPTrd;>fcWNZSpgR6yUzF8#n}CepYz{rNM&Yo5HIs0 zO|wjLuAZ9`XZAdxz5nK<0%7Zt$*%icY4DQnXV&i!`M0P1RXQJ&f!1~g;=S3c*l`(e zzgiV)&W@N6bT_+oIZk=?SJY(|xH$gI3pe>&ZC#Xpom_HW@oQ&vo2epyEEaXjhVr5I z)j}d`+rim74Y`b~6G^@Uj(N4Ye2geb`F(F6#@HJ=i%;P;&4vD6_6x}cCn{^AW-S_J zt1MRr_8|L~^JAJ5O(VsZR-V_%BY7s(;p$}Wz+=-?lDX+jjYm{b&ucA~x#b0y$c$e8-`&RruZpHGB>(9!ct~HLoha#r*G1rPI{Y+qUwMQ#`|4_qEn^LF_ zH{f~q`qe*iHnLsUljq-d<5Bt<_EN`CPouJ)D!RoZ!F&jQg|DcmW~B@}TXhw0arCX! z{d$?T+%|j8-6QY+XfY*dNd0y~YoV0e!L|@DlrBR&z6Rmu>XiO2JE{{>H>;xJ^m19K zKaHPY5uh=k9rUoY7jrzd^t*VDi~}w`4vpe<@$s7nvQM=xJqlm}3E%(PCdX2+DB*)x z4oma4W{{Az;JZJghvN%xo4-$T(0e$t6r>l950vazp5sH)8;(_fOp$v{E_&Tmih`I@kEk+5(^g@BkC` zZ%s%55P*rgFOmkr1p{%Jx)YLUpa6iP>~y#eAdzu#`GY_oDv55rZ%6RwVAB>IVT+{i z^!Y0#TcfsA>+AKNibbE-2jG%{!j)16LdbB@kpU&AXB-)mTp(Euw>Spi2G?%riBnvn zD+1tZKra9a3Kak~8z2Fwq$B`9;uJkMu5FEx#77sFkrJu>ib&)Wjk%U`WYgxUmR0b) zc3JPK|NH!d+>uS2@0Y|8t|FIg6sDB`VWT@YI7(cgr4?gdKq~Lf`Eb551T%a<#WoYE zhMxwrNVO}!0Du8blc^6A!o@8vOJ+|{U4K&-S5%#cDt5izUG7S97VE93^P|KgS&@>Yc0Dcxl6XQ~ zNIazHtccMSqJpgQ-;f{7w)GOJMur@6dUXTKu_n}#Uz^)qlBG`m2Ryy$zEKLQ@3)hJ zh@$hF9A)eBc2d7gOrIez_x1l-cwO;0b_uu8V8vZ1CqMsv&5FenzyAt;sZ3E3vQHp) zvT-QYul|HvF#pC^{Qf)B;q;J1nB>=yx^q;V?jJ>sHy`v!taj&g`GmH4T#6w|v(A1t zby&OJHdHEKRGWAg?@^g|X5_GsRk*x3qwqh(L76S^`}O*qP#ub2!!hWBN3ENCSw0#) zf9$;A^bIxT2Qb0jKTiixE%Djwr7z3)^+sM_jkWr6=F6`Gw&J4?d>C#yfGG|58(zx9v}03Pr9-`8l$ zAO4&fKnJLrm*a2!*);&5@btQmZ|>M801W>CfBlWE z#))GMM;(-!qUO6$FBi}VOduo=lu&`DTB8&g;i6Wk;e#r%hK~XOcmQMJBnbe3B-Y@9 ziNTpNCgoxJOQw@z?z7EU-)cC2R*T$DJlh{RXe`L(%X49IR-x)V9a(}dDm1t8{MU3P}k{raW76%{_t{C(B$pl>t2b%OG>gKtCWql0*m_vtQrpX19H7b}fdF zK#$;{ZOnVMCzG6Qi7WsjMYrlOZ`Ps?@G_^g1Q>89ZpT%D` zN?{!VI@A2?EnZ&~`6M5s{x2_T$Dr2N=gGZ5$!^O5E2H6){a*rO*yewK8czK4{WEIo zZ_Z-(`1t4&%!7tPU_JJTP-b^O3)^E9mepvXTAlh?=f0W!OP?WYc}N=wP7a^{EAHn7 zK;I2^zRtK<|0AuMyT~so(b|N6OSw4_*}m*pdo08BHKcs~_G}|(Jq<^*Q)Izqgts>{ zG3x38Y{30MoJ(?Rcex>ss*20wbleHiY~{|oa`A3$;AzB8)nws)N}n(^M7K_sQj%$l z<)6=6Ct3G+xxco$r~Bq6P_z8#9B;p!iLPuL8=S62=k47056+8g4*+dYAgbpQpO9g! z{D3W5O=0+DB5 z!_oiH@de|i8wZZhTCrnKS?<#+;$dmEP4=}Pt`~-8m6l#~@1+~_(!IQsXHc2v2N*>= zJY&vfbQ1_RjaHCw6$$`;)C1&O2{}@@GDCoKkODa30r>r&F2`WD0H-^J7Jpz|y{}E{ z>jaPwM?!o*nWj&B0@!Ce0PtJK8rJJ*0RTYW?;kJorL{yw1xQIUR0-LRzx(Ccojm~1 z&(R>RY?uy2z&-8%o|OauZdFza9!HRtr?kkfKp$8ivoZr z1)uS z$BI@`@!@HOnTkvS)c?|(hNUK>K<>#{tmmsO0vsfRqizK{zcJ|ViE zQEL}+EM>C4E^RWY9>yKrjx%a2I{%;Ot+&z<*?d1oa=khpPkO7z)R3)NUMb6r{Uj!x zcsbwcUMxj=V`8CED(ShU^3|=*Kc=6vl8lrH^k=`*yproN`}Rx8@(RAUaBpJ`hjMTF z!RkG_Y(nw5`~67bha_xyvO-n`XJ(1V1zL%Lbn^BNM1Nn$Ya?39$yy6MIQ>{3``HIm zN|IL6Z?Bh>UCp->?dJYANo=7)SQ(Ioe83ThQM3qOf!lcN&ke`@SHDlo^Zf2IzQkX8 z1n267;0|?1`r&Ztv98M+ql{}epdt#m)D|8zQldgfPk@o01cRA z5-di?1G@V!373BXUc{sHOQO6&e0ItxNykbdl6Z`LN#$3tG;x#wJ-E}x1I@a| zs#G9};caS=-}n@7KF(VLvx+BW4FF%cU|;-pD3Ac!1mlwZ{C9FYG5`=zp@Lh!*!|Z& zP5@QAOn{M!jVhcrmroTy^VN-p-c+|Q5v@hWxs(V>ov*w@GEj7t)6s8@&~wrC2Gldx z!%fn;xz-i&y>lPfX6fI!<+&u@_>pq~?)A`dsq2hm+{9|$?MG*lgSW+91`kbxBR8oZ zKI-Ha?6?q=6%C22R3TtosB8!@uMNt9WJt)K{ea@THI?th_-3vevB^{3f>W{-E>hv* zD*#ZDpoU>3B>?~cHI;G=1+~=Yz3=Le4okGIH#51m9jGoLqvm4ThEKdUZb(+i$pgZ= z^nFD2-zUsaEhjiTnW-pphQxBIV`fgt-1{aWddEK7T#iXzI$L(v{4^I-TB-Z#`NMQl zV^g|lMTy$zeI2E9>owQi%CO#gO3eI)!OX+3A+Xja610-dmA&4{juKYO=(f3B@kX)j znAxcAz-0_VVhxX*@5W24ntwX-9f|vccRSmlYn<(_s^)S}{H{N{_{uvj=q?Kg)b{ z`?jC?f4k4(>7){~?cdZrP2EN(aj#H_f7fO_wer-aXYEG$a%SfJue8TIYUS~tE0W%E zcN(#-C;F;5<}mk`@lF3|sa z+w3cNJpOikqE; z3}UhP`q`@OLsqX_YYls^SpSJv(2}+nwLJ%(ad? zcsaAg63-uLIy*NV_OSj8xPo1Hcf31jI54=?)`5<%+E*J{PeSFpeAPe&?*YSWTY=Ya ztb*b0@%2d<-ahLWOQ6m1YA%tU_ax=29s^#)Gx#MiDRk{oFj9I80u=F_ehQ2dh9jwl z-;Q?xxM-;V4f(Z=AacN^!F=1f|M38@HiNHc15ow$sx^uJ`mSW#008iL|NgSrDgY#r z4%{HAk@x+0%?SWOmeJ4yhnHVR>a*sEA7v8LWpAP%(DW+uuFYfSNb5=Tb~uD&X6pfQ z^KuFzZqv~2ARi4)yA!$*8bB-S6RF)XD!38RbrX8R57|P?9zeyRuo2UlE90arH5$!Z zIww_}q#P4*LAlO3CfWyr=Di4TKxAv6^Zq3!DWNqw>q%fj6ObiqIvjXIR`xYfD_tlI z+1!UX$|$u$5cIE70QH9!0Q~&%=ns7Xd>4epaRvYY02qG9p*(9^PEKfts>k`H&L88k zm<^*LFSjqHyhYot9G#sPFRE}g0Vd^wXfyk3cUttoS-pSyx#!brq(Cw)Wpnu)O92`H z#HlOsu<&g$63xJ1oOh`4Tc!`PVsYmjp!`?OQD}-bfoZXJ7BOmzr_BY@mDLaVWbQDb z6JtnP>&^X2SFO?6wW_t7$qVy8JT<3~+5kz&M#2I&-mg-Y@TtLRy;4&Db!AWC*OK*- ze}1i&?yUD+MA}gE!Diy`P~q`s-i=+%kyLJ9e{G>OY0`8vU0ccq4#>x3S59zbElXdW zlSCUKX`K@9)i=Ps?W>?zuYQ0jn{qZrfMEc-|J7X_=VY%d_AGyuHDp7K&0&6Mf;;&N z`|({VEw2v9v9_LnRTfL?{Sf`_L=^`^97j31QQU6(ijDs2(YW(4uMJWO18rFVJLK_aH90ecs0KW`kJMg&Cdq;CcwmF{pmD zd*Avm7+}EB?tSe);D7;y2w-3kwB6fhwdTByr{F_dOu79ewqLP00000 z000000Fe=L6kPWo{mNSDSK+~}RDFL0JJsxu{jXJLJyF($MI-?(x9hTB-rbOH@g3jh NLj)d&Q910i003wZW Date: Fri, 28 Aug 2015 19:32:18 +0300 Subject: [PATCH 12/32] Include Chunk into example --- examples/mixer.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/mixer.cc b/examples/mixer.cc index a957cfd..2f06827 100644 --- a/examples/mixer.cc +++ b/examples/mixer.cc @@ -26,14 +26,17 @@ #include #include +#include using namespace SDL2pp; int main() try { - SDL sdl(SDL_INIT_VIDEO); + SDL sdl(SDL_INIT_AUDIO); SDLMixer mixer(MIX_INIT_OGG); - // that's all for now + // currently fails as audio device hasn't been opened + Chunk chunk(TESTDATA_DIR "/test.ogg"); + chunk.Volume(128); return 0; } catch (std::exception& e) { From a25c84932c9b61731e029ff5d50a73166366abc3 Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Fri, 28 Aug 2015 23:04:09 +0300 Subject: [PATCH 13/32] Add stub Mixer class --- CMakeLists.txt | 2 + SDL2pp/Mixer.cc | 51 ++++++++++++++++++++ SDL2pp/Mixer.hh | 115 ++++++++++++++++++++++++++++++++++++++++++++++ SDL2pp/SDL2pp.hh | 1 + examples/mixer.cc | 4 +- 5 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 SDL2pp/Mixer.cc create mode 100644 SDL2pp/Mixer.hh diff --git a/CMakeLists.txt b/CMakeLists.txt index 1dbd83b..724c022 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,6 +110,7 @@ SET(LIBRARY_SOURCES SDL2pp/AudioSpec.cc SDL2pp/Chunk.cc SDL2pp/Exception.cc + SDL2pp/Mixer.cc SDL2pp/Point.cc SDL2pp/RWops.cc SDL2pp/Rect.cc @@ -130,6 +131,7 @@ SET(LIBRARY_HEADERS SDL2pp/Chunk.hh SDL2pp/ContainerRWops.hh SDL2pp/Exception.hh + SDL2pp/Mixer.hh SDL2pp/Optional.hh SDL2pp/Point.hh SDL2pp/RWops.hh diff --git a/SDL2pp/Mixer.cc b/SDL2pp/Mixer.cc new file mode 100644 index 0000000..32901ef --- /dev/null +++ b/SDL2pp/Mixer.cc @@ -0,0 +1,51 @@ +/* + 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 { + +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_) { + other.open_ = false; +} + +Mixer& Mixer::operator=(Mixer&& other) noexcept { + if (&other == this) + return *this; + if (open_) + Mix_CloseAudio(); + open_ = other.open_; + other.open_ = false; + return *this; +} + +} diff --git a/SDL2pp/Mixer.hh b/SDL2pp/Mixer.hh new file mode 100644 index 0000000..6b52c08 --- /dev/null +++ b/SDL2pp/Mixer.hh @@ -0,0 +1,115 @@ +/* + 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 + +namespace SDL2pp { + +class RWops; + +//////////////////////////////////////////////////////////// +/// \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 { +private: + bool open_; + +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; +}; + +} + +#endif diff --git a/SDL2pp/SDL2pp.hh b/SDL2pp/SDL2pp.hh index 46c10b0..c24954a 100644 --- a/SDL2pp/SDL2pp.hh +++ b/SDL2pp/SDL2pp.hh @@ -124,6 +124,7 @@ //////////////////////////////////////////////////////////// #ifdef SDL2PP_WITH_MIXER # include +# include # include #endif diff --git a/examples/mixer.cc b/examples/mixer.cc index 2f06827..43ab0ba 100644 --- a/examples/mixer.cc +++ b/examples/mixer.cc @@ -26,13 +26,15 @@ #include #include +#include #include using namespace SDL2pp; int main() try { SDL sdl(SDL_INIT_AUDIO); - SDLMixer mixer(MIX_INIT_OGG); + SDLMixer mixerlib(MIX_INIT_OGG); + Mixer mixer(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, 2, 4096); // currently fails as audio device hasn't been opened Chunk chunk(TESTDATA_DIR "/test.ogg"); From c5dc35574db657e24b84b6ce3b94ce50af510c56 Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Fri, 28 Aug 2015 23:23:47 +0300 Subject: [PATCH 14/32] Implement mixer playback --- SDL2pp/Mixer.cc | 77 +++++++++++++++ SDL2pp/Mixer.hh | 247 +++++++++++++++++++++++++++++++++++++++++++++- examples/mixer.cc | 19 +++- 3 files changed, 338 insertions(+), 5 deletions(-) diff --git a/SDL2pp/Mixer.cc b/SDL2pp/Mixer.cc index 32901ef..af90352 100644 --- a/SDL2pp/Mixer.cc +++ b/SDL2pp/Mixer.cc @@ -20,6 +20,7 @@ */ #include +#include #include namespace SDL2pp { @@ -48,4 +49,80 @@ Mixer& Mixer::operator=(Mixer&& other) noexcept { 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::Pause(int channel) { + Mix_Pause(channel); +} + +void Mixer::Resume(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_ExpireChannel(channel, ms); +} + +int Mixer::Playing(int channel) const { + return Mix_Playing(channel); +} + +int Mixer::Paused(int channel) const { + return Mix_Paused(channel); +} + +int Mixer::FadingChannel(int which) const { + return Mix_FadingChannel(which); +} + } diff --git a/SDL2pp/Mixer.hh b/SDL2pp/Mixer.hh index 6b52c08..af326cd 100644 --- a/SDL2pp/Mixer.hh +++ b/SDL2pp/Mixer.hh @@ -22,13 +22,11 @@ #ifndef SDL2PP_MIXER_HH #define SDL2PP_MIXER_HH -#include - #include namespace SDL2pp { -class RWops; +class Chunk; //////////////////////////////////////////////////////////// /// \brief SDL_mixer's audio mixer @@ -108,6 +106,249 @@ public: /// //////////////////////////////////////////////////////////// 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 Pause(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 Resume(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); + + //////////////////////////////////////////////////////////// + /// \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); + + // TODO: ChannelFinished + + //////////////////////////////////////////////////////////// + /// \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 Playing(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 Paused(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 + /// + //////////////////////////////////////////////////////////// + int FadingChannel(int which) const; + + // TODO: Groups + // TODO: Music + // TODO: Effects }; } diff --git a/examples/mixer.cc b/examples/mixer.cc index 43ab0ba..c903adf 100644 --- a/examples/mixer.cc +++ b/examples/mixer.cc @@ -36,9 +36,24 @@ int main() try { SDLMixer mixerlib(MIX_INIT_OGG); Mixer mixer(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, 2, 4096); - // currently fails as audio device hasn't been opened Chunk chunk(TESTDATA_DIR "/test.ogg"); - chunk.Volume(128); + + int chan; + + chan = mixer.PlayChannel(-1, chunk); + std::cerr << "Playing sound on channel " << chan << "\n"; + + SDL_Delay(500); + + chan = mixer.PlayChannel(-1, chunk); + std::cerr << "Playing sound on channel " << chan << "\n"; + + SDL_Delay(1000); + + chan = mixer.PlayChannel(-1, chunk); + std::cerr << "Playing sound on channel " << chan << "\n"; + + SDL_Delay(4500); return 0; } catch (std::exception& e) { From 30734317c8a60c2169412210f0cb5ff7dfc920c5 Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Fri, 28 Aug 2015 23:26:51 +0300 Subject: [PATCH 15/32] It's a good idea to halt playback before Chunk destruction --- SDL2pp/Mixer.hh | 2 +- examples/mixer.cc | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/SDL2pp/Mixer.hh b/SDL2pp/Mixer.hh index af326cd..23c4685 100644 --- a/SDL2pp/Mixer.hh +++ b/SDL2pp/Mixer.hh @@ -271,7 +271,7 @@ public: /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC34 /// //////////////////////////////////////////////////////////// - void HaltChannel(int channel); + void HaltChannel(int channel = -1); //////////////////////////////////////////////////////////// /// \brief Change the timed stoppage of a channel diff --git a/examples/mixer.cc b/examples/mixer.cc index c903adf..38cc8df 100644 --- a/examples/mixer.cc +++ b/examples/mixer.cc @@ -55,6 +55,9 @@ int main() try { SDL_Delay(4500); + // Make sure no chunks are being played before destroying Chunk + mixer.HaltChannel(); + return 0; } catch (std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; From ed12546cc5b36da120728b7481412eaf0a9c5d0e Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Fri, 28 Aug 2015 23:45:24 +0300 Subject: [PATCH 16/32] Fix copypasta --- SDL2pp/Mixer.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SDL2pp/Mixer.cc b/SDL2pp/Mixer.cc index af90352..05b0689 100644 --- a/SDL2pp/Mixer.cc +++ b/SDL2pp/Mixer.cc @@ -110,7 +110,7 @@ int Mixer::ExpireChannel(int channel, int ticks) { } int Mixer::FadeOutChannel(int channel, int ms) { - return Mix_ExpireChannel(channel, ms); + return Mix_FadeOutChannel(channel, ms); } int Mixer::Playing(int channel) const { From 1ba35e5575c647845c83139c22791e315978427b Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Fri, 28 Aug 2015 23:45:52 +0300 Subject: [PATCH 17/32] Implement ChannelFinished() --- SDL2pp/Mixer.cc | 4 ++++ SDL2pp/Mixer.hh | 18 +++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/SDL2pp/Mixer.cc b/SDL2pp/Mixer.cc index 05b0689..69e2005 100644 --- a/SDL2pp/Mixer.cc +++ b/SDL2pp/Mixer.cc @@ -113,6 +113,10 @@ int Mixer::FadeOutChannel(int channel, int ms) { return Mix_FadeOutChannel(channel, ms); } +void Mixer::ChannelFinished(ChannelFinishedHandler channel_finished) { + Mix_ChannelFinished(channel_finished); +} + int Mixer::Playing(int channel) const { return Mix_Playing(channel); } diff --git a/SDL2pp/Mixer.hh b/SDL2pp/Mixer.hh index 23c4685..090cd65 100644 --- a/SDL2pp/Mixer.hh +++ b/SDL2pp/Mixer.hh @@ -41,6 +41,9 @@ class Chunk; /// //////////////////////////////////////////////////////////// class Mixer { +public: + typedef void (*ChannelFinishedHandler)(int); + private: bool open_; @@ -300,7 +303,20 @@ public: //////////////////////////////////////////////////////////// int FadeOutChannel(int channel, int ms); - // TODO: ChannelFinished + //////////////////////////////////////////////////////////// + /// \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 ChannelFinished(ChannelFinishedHandler channel_finished); //////////////////////////////////////////////////////////// /// \brief Get the active playing status of a channel From 4be68384ff3ec150b3847cd03452ef73e23d3648 Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Fri, 28 Aug 2015 23:46:13 +0300 Subject: [PATCH 18/32] Extend example --- examples/mixer.cc | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/examples/mixer.cc b/examples/mixer.cc index 38cc8df..0d340a7 100644 --- a/examples/mixer.cc +++ b/examples/mixer.cc @@ -40,20 +40,38 @@ int main() try { int chan; + mixer.ChannelFinished([](int channel){ + std::cerr << "Channel " << channel << " finished playback" << std::endl; + }); + + // Fade in + chan = mixer.FadeInChannel(-1, chunk, 0, 1000); + std::cerr << "Fading sound in on channel " << chan << "\n"; + + SDL_Delay(2000); + + // Mix 3 sounds chan = mixer.PlayChannel(-1, chunk); std::cerr << "Playing sound on channel " << chan << "\n"; - SDL_Delay(500); + SDL_Delay(250); chan = mixer.PlayChannel(-1, chunk); std::cerr << "Playing sound on channel " << chan << "\n"; - SDL_Delay(1000); + SDL_Delay(250); chan = mixer.PlayChannel(-1, chunk); std::cerr << "Playing sound on channel " << chan << "\n"; - SDL_Delay(4500); + SDL_Delay(2000); + + // Fade out + chan = mixer.PlayChannel(-1, chunk); + std::cerr << "Fading out sound on channel " << chan << "\n"; + mixer.FadeOutChannel(chan, 2000); + + SDL_Delay(2000); // Make sure no chunks are being played before destroying Chunk mixer.HaltChannel(); From 1cc64cea4f02442138c29eaa386ebe1fa41f69bb Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Sat, 29 Aug 2015 00:27:22 +0300 Subject: [PATCH 19/32] Cosmetic fixes for example --- examples/mixer.cc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/mixer.cc b/examples/mixer.cc index 0d340a7..8a36492 100644 --- a/examples/mixer.cc +++ b/examples/mixer.cc @@ -34,46 +34,46 @@ using namespace SDL2pp; int main() try { SDL sdl(SDL_INIT_AUDIO); SDLMixer mixerlib(MIX_INIT_OGG); - Mixer mixer(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, 2, 4096); + Mixer mixer(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, MIX_DEFAULT_CHANNELS, 4096); - Chunk chunk(TESTDATA_DIR "/test.ogg"); - - int chan; + Chunk sound(TESTDATA_DIR "/test.ogg"); mixer.ChannelFinished([](int channel){ std::cerr << "Channel " << channel << " finished playback" << std::endl; }); + int chan; + // Fade in - chan = mixer.FadeInChannel(-1, chunk, 0, 1000); + 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, chunk); + chan = mixer.PlayChannel(-1, sound); std::cerr << "Playing sound on channel " << chan << "\n"; SDL_Delay(250); - chan = mixer.PlayChannel(-1, chunk); + chan = mixer.PlayChannel(-1, sound); std::cerr << "Playing sound on channel " << chan << "\n"; SDL_Delay(250); - chan = mixer.PlayChannel(-1, chunk); + chan = mixer.PlayChannel(-1, sound); std::cerr << "Playing sound on channel " << chan << "\n"; SDL_Delay(2000); // Fade out - chan = mixer.PlayChannel(-1, chunk); + chan = mixer.PlayChannel(-1, sound); std::cerr << "Fading out sound on channel " << chan << "\n"; mixer.FadeOutChannel(chan, 2000); SDL_Delay(2000); - // Make sure no chunks are being played before destroying Chunk + // Make sure no sounds are being played before destroying Chunk mixer.HaltChannel(); return 0; From e37d67725e68f26c3feb8c3032d11bb52d989fba Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Sat, 29 Aug 2015 02:34:16 +0300 Subject: [PATCH 20/32] It is actually safe to free chunks which are still playing Though documentation and even a comment at the start of Mix_FreeChunk() state that it's unsafe to free chunk which is still being played, the function actually contains the code to stop all playback of a chunk which is being freed. See SDL2_mixer 2.0.0, mixer.c:759: /* Guarantee that this chunk isn't playing */ SDL_LockAudio(); if ( mix_channel ) { for ( i=0; i Date: Sat, 29 Aug 2015 02:43:03 +0300 Subject: [PATCH 21/32] Document Mixer member typedef --- SDL2pp/Mixer.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SDL2pp/Mixer.hh b/SDL2pp/Mixer.hh index 090cd65..cb2b29b 100644 --- a/SDL2pp/Mixer.hh +++ b/SDL2pp/Mixer.hh @@ -42,7 +42,7 @@ class Chunk; //////////////////////////////////////////////////////////// class Mixer { public: - typedef void (*ChannelFinishedHandler)(int); + typedef void (*ChannelFinishedHandler)(int); ///< Function type for channel finished callback private: bool open_; From 6dd5817471e66b9b141cf8d22484e7ca672db1be Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Sun, 30 Aug 2015 16:06:54 +0300 Subject: [PATCH 22/32] Mention mixer support in readme --- README.md | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8302795..1de5e2f 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 variabled 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) ``` From 7916195c15bb516cea664c0c33941c2377251468 Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Sun, 30 Aug 2015 16:39:06 +0300 Subject: [PATCH 23/32] Use more meaningful function names --- SDL2pp/Mixer.cc | 10 +++++----- SDL2pp/Mixer.hh | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/SDL2pp/Mixer.cc b/SDL2pp/Mixer.cc index 69e2005..d9f4d1d 100644 --- a/SDL2pp/Mixer.cc +++ b/SDL2pp/Mixer.cc @@ -93,11 +93,11 @@ int Mixer::FadeInChannel(int channel, const Chunk& chunk, int loops, int ms, int return chan; } -void Mixer::Pause(int channel) { +void Mixer::PauseChannel(int channel) { Mix_Pause(channel); } -void Mixer::Resume(int channel) { +void Mixer::ResumeChannel(int channel) { Mix_Resume(channel); } @@ -117,15 +117,15 @@ void Mixer::ChannelFinished(ChannelFinishedHandler channel_finished) { Mix_ChannelFinished(channel_finished); } -int Mixer::Playing(int channel) const { +int Mixer::IsChannelPlaying(int channel) const { return Mix_Playing(channel); } -int Mixer::Paused(int channel) const { +int Mixer::IsChannelPaused(int channel) const { return Mix_Paused(channel); } -int Mixer::FadingChannel(int which) const { +int Mixer::IsChannelFading(int which) const { return Mix_FadingChannel(which); } diff --git a/SDL2pp/Mixer.hh b/SDL2pp/Mixer.hh index cb2b29b..8a70f51 100644 --- a/SDL2pp/Mixer.hh +++ b/SDL2pp/Mixer.hh @@ -254,7 +254,7 @@ public: /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC32 /// //////////////////////////////////////////////////////////// - void Pause(int channel = -1); + void PauseChannel(int channel = -1); //////////////////////////////////////////////////////////// /// \brief Resume a paused channel @@ -264,7 +264,7 @@ public: /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC33 /// //////////////////////////////////////////////////////////// - void Resume(int channel = -1); + void ResumeChannel(int channel = -1); //////////////////////////////////////////////////////////// /// \brief Stop playing on a channel @@ -332,7 +332,7 @@ public: /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC38 /// //////////////////////////////////////////////////////////// - int Playing(int channel) const; + int IsChannelPlaying(int channel) const; //////////////////////////////////////////////////////////// /// \brief Get the pause status of a channel @@ -348,7 +348,7 @@ public: /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC39 /// //////////////////////////////////////////////////////////// - int Paused(int channel) const; + int IsChannelPaused(int channel) const; //////////////////////////////////////////////////////////// /// \brief Get the fade status of a channel @@ -360,7 +360,7 @@ public: /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC40 /// //////////////////////////////////////////////////////////// - int FadingChannel(int which) const; + int IsChannelFading(int which) const; // TODO: Groups // TODO: Music From 88b7733d2ab238f9667ed93a6385990d341ca6cf Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Mon, 31 Aug 2015 18:07:57 +0300 Subject: [PATCH 24/32] Fix ChannelFading return type and name --- SDL2pp/Mixer.cc | 2 +- SDL2pp/Mixer.hh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/SDL2pp/Mixer.cc b/SDL2pp/Mixer.cc index d9f4d1d..534b113 100644 --- a/SDL2pp/Mixer.cc +++ b/SDL2pp/Mixer.cc @@ -125,7 +125,7 @@ int Mixer::IsChannelPaused(int channel) const { return Mix_Paused(channel); } -int Mixer::IsChannelFading(int which) const { +Mix_Fading Mixer::GetChannelFading(int which) const { return Mix_FadingChannel(which); } diff --git a/SDL2pp/Mixer.hh b/SDL2pp/Mixer.hh index 8a70f51..28bc17a 100644 --- a/SDL2pp/Mixer.hh +++ b/SDL2pp/Mixer.hh @@ -360,7 +360,7 @@ public: /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC40 /// //////////////////////////////////////////////////////////// - int IsChannelFading(int which) const; + Mix_Fading GetChannelFading(int which) const; // TODO: Groups // TODO: Music From 8155dc8158c99636a7f1379b9f417bde5968f148 Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Mon, 31 Aug 2015 18:15:45 +0300 Subject: [PATCH 25/32] Better method name --- SDL2pp/Mixer.cc | 2 +- SDL2pp/Mixer.hh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/SDL2pp/Mixer.cc b/SDL2pp/Mixer.cc index 534b113..b2d9605 100644 --- a/SDL2pp/Mixer.cc +++ b/SDL2pp/Mixer.cc @@ -113,7 +113,7 @@ int Mixer::FadeOutChannel(int channel, int ms) { return Mix_FadeOutChannel(channel, ms); } -void Mixer::ChannelFinished(ChannelFinishedHandler channel_finished) { +void Mixer::SetChannelFinishedHandler(ChannelFinishedHandler channel_finished) { Mix_ChannelFinished(channel_finished); } diff --git a/SDL2pp/Mixer.hh b/SDL2pp/Mixer.hh index 28bc17a..210ca3f 100644 --- a/SDL2pp/Mixer.hh +++ b/SDL2pp/Mixer.hh @@ -316,7 +316,7 @@ public: /// \see https://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer.html#SEC37 /// //////////////////////////////////////////////////////////// - void ChannelFinished(ChannelFinishedHandler channel_finished); + void SetChannelFinishedHandler(ChannelFinishedHandler channel_finished); //////////////////////////////////////////////////////////// /// \brief Get the active playing status of a channel From f3511a1a7189c38423657cfc798d8ae051f70af7 Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Mon, 31 Aug 2015 18:38:29 +0300 Subject: [PATCH 26/32] Implement mixer music --- CMakeLists.txt | 2 + SDL2pp/Mixer.cc | 77 +++++++++++++++- SDL2pp/Mixer.hh | 169 ++++++++++++++++++++++++++++++++++- SDL2pp/Music.cc | 62 +++++++++++++ SDL2pp/Music.hh | 125 ++++++++++++++++++++++++++ SDL2pp/SDL2pp.hh | 1 + examples/CMakeLists.txt | 6 ++ examples/mixer.cc | 2 +- examples/mixer_music.cc | 49 ++++++++++ examples/mixer_music_sine.cc | 54 +++++++++++ 10 files changed, 544 insertions(+), 3 deletions(-) create mode 100644 SDL2pp/Music.cc create mode 100644 SDL2pp/Music.hh create mode 100644 examples/mixer_music.cc create mode 100644 examples/mixer_music_sine.cc 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; +} From 7121b7854360f416864a24014c8e51de17f7e115 Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Mon, 31 Aug 2015 18:41:01 +0300 Subject: [PATCH 27/32] Fix comment --- examples/mixer_music_sine.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/mixer_music_sine.cc b/examples/mixer_music_sine.cc index 15159f8..f4d6618 100644 --- a/examples/mixer_music_sine.cc +++ b/examples/mixer_music_sine.cc @@ -30,13 +30,12 @@ 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 + // 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) From 6ebd677893f636821d75e32d930e389b7e68f1bf Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Mon, 31 Aug 2015 19:02:04 +0300 Subject: [PATCH 28/32] Implement mixer groups --- SDL2pp/Mixer.cc | 38 +++++++++++++++ SDL2pp/Mixer.hh | 122 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 159 insertions(+), 1 deletion(-) diff --git a/SDL2pp/Mixer.cc b/SDL2pp/Mixer.cc index 7f1a763..e762863 100644 --- a/SDL2pp/Mixer.cc +++ b/SDL2pp/Mixer.cc @@ -131,6 +131,44 @@ 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"); diff --git a/SDL2pp/Mixer.hh b/SDL2pp/Mixer.hh index 4ce6073..44883dc 100644 --- a/SDL2pp/Mixer.hh +++ b/SDL2pp/Mixer.hh @@ -371,7 +371,127 @@ public: //////////////////////////////////////////////////////////// Mix_Fading GetChannelFading(int which) const; - // TODO: Groups + //////////////////////////////////////////////////////////// + /// \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 From 0cc8e489f86bbc90f807ef25e1bea50c8e505437 Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Mon, 31 Aug 2015 19:06:34 +0300 Subject: [PATCH 29/32] Include for unique_ptr --- SDL2pp/Mixer.hh | 1 + 1 file changed, 1 insertion(+) diff --git a/SDL2pp/Mixer.hh b/SDL2pp/Mixer.hh index 44883dc..d6e7c71 100644 --- a/SDL2pp/Mixer.hh +++ b/SDL2pp/Mixer.hh @@ -23,6 +23,7 @@ #define SDL2PP_MIXER_HH #include +#include #include From 01a537218ae7914a7a271cecbda080cf8a8b515c Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Tue, 1 Sep 2015 00:32:03 +0300 Subject: [PATCH 30/32] Fix comments --- SDL2pp/Mixer.hh | 6 +++--- SDL2pp/Music.hh | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/SDL2pp/Mixer.hh b/SDL2pp/Mixer.hh index d6e7c71..65434f8 100644 --- a/SDL2pp/Mixer.hh +++ b/SDL2pp/Mixer.hh @@ -436,7 +436,7 @@ public: /// /// \param[in] tag A group number /// - /// \returns The channel found on success. -1 is returned when \ + /// \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 @@ -449,7 +449,7 @@ public: /// /// \param[in] tag A group number /// - /// \returns The channel found on success. -1 is returned when \ + /// \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 @@ -462,7 +462,7 @@ public: /// /// \param[in] tag A group number /// - /// \returns The channel found on success. -1 is returned when \ + /// \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 diff --git a/SDL2pp/Music.hh b/SDL2pp/Music.hh index c3258a4..d5695f9 100644 --- a/SDL2pp/Music.hh +++ b/SDL2pp/Music.hh @@ -44,7 +44,7 @@ public: //////////////////////////////////////////////////////////// /// \brief Construct from existing Mix_Music pointer /// - /// \param[in] chunk Existing Mix_Music to manage + /// \param[in] music Existing Mix_Music to manage /// //////////////////////////////////////////////////////////// Music(Mix_Music* music); @@ -104,9 +104,9 @@ public: Music& operator=(const Music& other) = delete; //////////////////////////////////////////////////////////// - /// \brief Get pointer to managed Mix_Music object + /// \brief Get pointer to managed Mix_Music /// - /// \returns Pointer to managed Mix_Music object + /// \returns Pointer to managed Mix_Music /// //////////////////////////////////////////////////////////// Mix_Music* Get() const; From 326378c59c6b6690ef3deb7bcd36b9d275e889b2 Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Fri, 4 Sep 2015 23:07:57 +0300 Subject: [PATCH 31/32] Add buildin effects support --- SDL2pp/Mixer.cc | 40 +++++++++++++ SDL2pp/Mixer.hh | 118 ++++++++++++++++++++++++++++++++++++- examples/CMakeLists.txt | 3 + examples/mixer_effects.cc | 120 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 280 insertions(+), 1 deletion(-) create mode 100644 examples/mixer_effects.cc diff --git a/SDL2pp/Mixer.cc b/SDL2pp/Mixer.cc index e762863..7d649fc 100644 --- a/SDL2pp/Mixer.cc +++ b/SDL2pp/Mixer.cc @@ -242,4 +242,44 @@ void Mixer::SetMusicHook(MusicHook&& hook) { }, 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 index 65434f8..53d032c 100644 --- a/SDL2pp/Mixer.hh +++ b/SDL2pp/Mixer.hh @@ -652,7 +652,123 @@ public: //////////////////////////////////////////////////////////// void SetMusicHook(MusicHook&& hook); - // TODO: Effects + // 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); }; } diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index c7e8d1f..12b1dfb 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -37,4 +37,7 @@ IF(SDL2PP_WITH_MIXER) 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_effects.cc b/examples/mixer_effects.cc new file mode 100644 index 0000000..191999d --- /dev/null +++ b/examples/mixer_effects.cc @@ -0,0 +1,120 @@ +/* + 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"); + + int chan; + + // Panning + std::cerr << "Panning: left" << std::endl; + mixer.SetPanning(MIX_CHANNEL_POST, 255, 0); + chan = mixer.PlayChannel(-1, sound); + SDL_Delay(2000); + mixer.HaltChannel(-1); + + std::cerr << "Panning: right" << std::endl; + mixer.SetPanning(MIX_CHANNEL_POST, 0, 255); + chan = 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); + chan = mixer.PlayChannel(-1, sound); + SDL_Delay(2000); + mixer.HaltChannel(-1); + + std::cerr << "Distance: further" << std::endl; + mixer.SetDistance(MIX_CHANNEL_POST, 192); + chan = mixer.PlayChannel(-1, sound); + SDL_Delay(2000); + mixer.HaltChannel(-1); + + std::cerr << "Distance: even further" << std::endl; + mixer.SetDistance(MIX_CHANNEL_POST, 224); + chan = 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); + chan = 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); + chan = mixer.PlayChannel(-1, sound); + SDL_Delay(2000); + mixer.HaltChannel(-1); + + std::cerr << "Position: further right" << std::endl; + mixer.SetPosition(MIX_CHANNEL_POST, 90, 192); + chan = 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); + chan = 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); + chan = 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; +} From 00e47a88339bd09873d4123ae8c1ca5204b3bf31 Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Sun, 6 Sep 2015 22:01:42 +0300 Subject: [PATCH 32/32] Remove unused variable --- examples/mixer_effects.cc | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/examples/mixer_effects.cc b/examples/mixer_effects.cc index 191999d..28286a9 100644 --- a/examples/mixer_effects.cc +++ b/examples/mixer_effects.cc @@ -38,18 +38,16 @@ int main() try { Chunk sound(TESTDATA_DIR "/test.ogg"); - int chan; - // Panning std::cerr << "Panning: left" << std::endl; mixer.SetPanning(MIX_CHANNEL_POST, 255, 0); - chan = mixer.PlayChannel(-1, sound); + mixer.PlayChannel(-1, sound); SDL_Delay(2000); mixer.HaltChannel(-1); std::cerr << "Panning: right" << std::endl; mixer.SetPanning(MIX_CHANNEL_POST, 0, 255); - chan = mixer.PlayChannel(-1, sound); + mixer.PlayChannel(-1, sound); SDL_Delay(2000); mixer.HaltChannel(-1); @@ -58,19 +56,19 @@ int main() try { // Distance std::cerr << "Distance: somewhat far" << std::endl; mixer.SetDistance(MIX_CHANNEL_POST, 128); - chan = mixer.PlayChannel(-1, sound); + mixer.PlayChannel(-1, sound); SDL_Delay(2000); mixer.HaltChannel(-1); std::cerr << "Distance: further" << std::endl; mixer.SetDistance(MIX_CHANNEL_POST, 192); - chan = mixer.PlayChannel(-1, sound); + mixer.PlayChannel(-1, sound); SDL_Delay(2000); mixer.HaltChannel(-1); std::cerr << "Distance: even further" << std::endl; mixer.SetDistance(MIX_CHANNEL_POST, 224); - chan = mixer.PlayChannel(-1, sound); + mixer.PlayChannel(-1, sound); SDL_Delay(2000); mixer.HaltChannel(-1); @@ -79,25 +77,25 @@ int main() try { // Position std::cerr << "Position: closest left" << std::endl; mixer.SetPosition(MIX_CHANNEL_POST, 270, 0); - chan = mixer.PlayChannel(-1, sound); + 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); - chan = mixer.PlayChannel(-1, sound); + mixer.PlayChannel(-1, sound); SDL_Delay(2000); mixer.HaltChannel(-1); std::cerr << "Position: further right" << std::endl; mixer.SetPosition(MIX_CHANNEL_POST, 90, 192); - chan = mixer.PlayChannel(-1, sound); + 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); - chan = mixer.PlayChannel(-1, sound); + mixer.PlayChannel(-1, sound); SDL_Delay(2000); mixer.HaltChannel(-1); @@ -107,7 +105,7 @@ int main() try { std::cerr << "Reverse stereo" << std::endl; mixer.SetReverseStereo(MIX_CHANNEL_POST); - chan = mixer.PlayChannel(-1, sound); + mixer.PlayChannel(-1, sound); SDL_Delay(2000); mixer.HaltChannel(-1);