From 4eeb066914fd2c0385294ae34b7ff4b98390f184 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Alexis=20Laferri=C3=A8re?= Date: Thu, 25 May 2017 12:56:08 -0400 Subject: [PATCH] gamnit: implement app::audio for desktop using SDL2 mixer MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Alexis Laferrière --- lib/gamnit/README.md | 4 +- lib/gamnit/display_linux.nit | 27 ++++++++++- lib/linux/audio.nit | 107 ++++++++++++++++++++++++++++++++++++++---- 3 files changed, 125 insertions(+), 13 deletions(-) diff --git a/lib/gamnit/README.md b/lib/gamnit/README.md index 5b12210..a289482 100644 --- a/lib/gamnit/README.md +++ b/lib/gamnit/README.md @@ -9,13 +9,13 @@ To compile the _gamnit_ apps packaged with the Nit repositoy on GNU/Linux you ne Under Debian 8.2, this command should install everything needed: ~~~ -apt-get install libgles2-mesa-dev libsdl2-dev libsdl2-image-dev inkscape mpg123 +apt-get install libgles2-mesa-dev libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev inkscape mpg123 ~~~ Under Windows 64 bits, using msys2, you can install the required packages with: ~~~ -pacman -S mingw-w64-x86_64-angleproject-git mingw-w64-x86_64-SDL2 mingw-w64-x86_64-SDL2_image +pacman -S mingw-w64-x86_64-angleproject-git mingw-w64-x86_64-SDL2 mingw-w64-x86_64-SDL2_image mingw-w64-x86_64-SDL2_mixer ~~~ # Services by submodules diff --git a/lib/gamnit/display_linux.nit b/lib/gamnit/display_linux.nit index 231ee17..634656b 100644 --- a/lib/gamnit/display_linux.nit +++ b/lib/gamnit/display_linux.nit @@ -16,6 +16,7 @@ module display_linux import sdl2::image +import sdl2::mixer import egl # local to gamnit intrude import display @@ -68,7 +69,7 @@ redef class GamnitDisplay # Setup the SDL display and lib fun setup_sdl(window_width, window_height: Int): SDLWindow do - assert sdl.initialize((new SDLInitFlags).video) else + assert sdl.initialize((new SDLInitFlags).video.audio) else print_error "Failed to initialize SDL2: {sdl.error}" end @@ -82,11 +83,33 @@ redef class GamnitDisplay print_error "Failed to create SDL2 window: {sdl.error}" end + # Audio support + var inited = mix.initialize(mix_init_flags) + assert inited != mix_init_flags else + print_error "Failed to load SDL2 mixer format supports: {mix.error}" + end + + var opened = mix.open_audio(44100, mix.default_format, 2, 1024) + assert opened else + print_error "Failed to initialize SDL2 mixer: {mix.error}" + end + return sdl_window end + # Initialization flags passed to SDL2 mixer + # + # Defaults to all available formats. + var mix_init_flags: MixInitFlags = mix.flac | mix.mod | mix.mp3 | mix.ogg is lazy, writable + # Close the SDL display - fun close_sdl do sdl_window.destroy + fun close_sdl + do + sdl_window.destroy + mix.close_audio + mix.quit + sdl.finalize + end end redef class TextureAsset diff --git a/lib/linux/audio.nit b/lib/linux/audio.nit index 85f572a..a3bfb4f 100644 --- a/lib/linux/audio.nit +++ b/lib/linux/audio.nit @@ -14,23 +14,112 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Linux audio implementation +# `app::audio` implementation for GNU/Linux using SDL2 mixer module audio import app::audio +import sdl2::mixer import linux redef class PlayableAudio + redef var error = null - redef fun play do - if path.has_suffix(".wav") then - sys.system "aplay -q {app.assets_dir}{path} &" - else if path.has_suffix(".mp3") then - sys.system "mpg123 -q {app.assets_dir}{path} &" + # Real file system path to this asset + private fun fs_path: String do return app.assets_dir / path + + # Does `fs_path` exist? + private fun fs_path_exists: Bool + do + if not fs_path.file_exists then + error = new Error("Failed to load audio '{path}': file not found") + return false + end + return true + end +end + +redef class Sound + + private var native: nullable MixChunk = null + + redef fun load + do + if not fs_path_exists then return + + # SDL2 mixer load + var native = mix.load_wav(fs_path.to_cstring) + if native.address_is_null then + error = new Error("Failed to load sound '{path}': {mix.error}") + return + end + + self.native = native + end + + redef fun play + do + var native = native + + if native == null and error == null then + # Lazy load + load + + # Auto print errors on lazy loading only + var error = error + if error != null then print_error error + end + + # If there's an error, silently skip + if error != null then return + native = self.native + assert native != null + + # Play on any available channel + mix.play_channel(-1, native, 0) + end +end + +redef class Music + + private var native: nullable MixMusic = null + + redef fun load + do + if not fs_path_exists then return + + # SDL2 mixer load + var native = mix.load_mus(fs_path.to_cstring) + if native.address_is_null then + error = new Error("Failed to load music '{path}': {mix.error}") + return + end + + self.native = native + end + + redef fun play + do + var native = native + + if native == null and error == null then + # Lazy load + load + + # Auto print errors on lazy loading only + var error = error + if error != null then print_error error end + + # If there's an error, silently skip + if error != null then return + native = self.native + assert native != null + + # Play looping + mix.play_music(native, -1) end - redef fun load do end - redef fun pause do end - redef fun resume do end + redef fun pause do mix.pause_music + + redef fun resume do mix.resume_music end -- 1.7.9.5