This will allow us to generate sounds on the fly on several dedicated channels. Until now we could only play fully loaded samples.legacy
| @@ -95,6 +95,7 @@ doc/tutorial/05_easymesh | |||
| doc/tutorial/06_sprite | |||
| doc/tutorial/07_input | |||
| doc/tutorial/08_fbo | |||
| doc/tutorial/09_sound | |||
| doc/tutorial/11_fractal | |||
| doc/tutorial/12_voronoi | |||
| doc/tutorial/13_shader_builder | |||
| @@ -0,0 +1,114 @@ | |||
| // | |||
| // Lol Engine — Sound tutorial | |||
| // | |||
| // Copyright © 2011—2016 Sam Hocevar <sam@hocevar.net> | |||
| // | |||
| // Lol Engine is free software. It comes without any warranty, to | |||
| // the extent permitted by applicable law. You can redistribute it | |||
| // and/or modify it under the terms of the Do What the Fuck You Want | |||
| // to Public License, Version 2, as published by the WTFPL Task Force. | |||
| // See http://www.wtfpl.net/ for more details. | |||
| // | |||
| #if HAVE_CONFIG_H | |||
| # include "config.h" | |||
| #endif | |||
| #include <lol/engine.h> | |||
| #include <functional> | |||
| using namespace lol; | |||
| class sound_demo : public WorldEntity | |||
| { | |||
| public: | |||
| sound_demo() | |||
| { | |||
| for (auto &val : m_streams) | |||
| val = -1; | |||
| m_controller = new Controller("Default"); | |||
| m_profile << InputProfile::Keyboard(0, "Space") | |||
| << InputProfile::MouseKey(1, "Left"); | |||
| m_controller->Init(m_profile); | |||
| m_mouse = InputDevice::GetMouse(); | |||
| m_text = new Text("SPACE for sine wave, Left Click for white noise", | |||
| "data/font/ascii.png"); | |||
| m_text->SetPos(vec3(5, 5, 1)); | |||
| Ticker::Ref(m_text); | |||
| } | |||
| ~sound_demo() | |||
| { | |||
| Ticker::Unref(m_text); | |||
| } | |||
| void synth(int mode, void *buf, int bytes) | |||
| { | |||
| uint16_t *stream = (uint16_t *)buf; | |||
| for (int i = 0; i < bytes / 2; ++i) | |||
| { | |||
| switch (mode) | |||
| { | |||
| case 0: // sine wave | |||
| stream[i] = 400 * lol::sin(12 * i * F_TAU / bytes); | |||
| break; | |||
| case 1: // white noise | |||
| stream[i] = lol::rand(-120, 120); | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| virtual void TickGame(float seconds) | |||
| { | |||
| WorldEntity::TickGame(seconds); | |||
| for (int i = 0; i < 2; ++i) | |||
| { | |||
| if (!m_controller->WasKeyPressedThisFrame(i)) | |||
| continue; | |||
| if (m_streams[i] < 0) | |||
| { | |||
| auto f = std::bind(&sound_demo::synth, this, i, | |||
| std::placeholders::_1, | |||
| std::placeholders::_2); | |||
| m_streams[i] = audio::start_streaming(f); | |||
| } | |||
| else | |||
| { | |||
| audio::stop_streaming(m_streams[i]); | |||
| m_streams[i] = -1; | |||
| } | |||
| } | |||
| } | |||
| virtual void TickDraw(float seconds, Scene &scene) | |||
| { | |||
| WorldEntity::TickDraw(seconds, scene); | |||
| } | |||
| private: | |||
| int m_streams[2]; | |||
| InputDevice *m_mouse; | |||
| Controller *m_controller; | |||
| InputProfile m_profile; | |||
| Text *m_text; | |||
| }; | |||
| int main(int argc, char **argv) | |||
| { | |||
| sys::init(argc, argv); | |||
| Application app("Tutorial 9: Sound", ivec2(640, 480), 60.0f); | |||
| new sound_demo(); | |||
| app.Run(); | |||
| return EXIT_SUCCESS; | |||
| } | |||
| @@ -3,7 +3,7 @@ include $(top_srcdir)/build/autotools/common.am | |||
| if BUILD_TUTORIAL | |||
| noinst_PROGRAMS = 01_triangle 02_cube 03_noise 04_texture 05_easymesh \ | |||
| 06_sprite 07_input 08_fbo 11_fractal \ | |||
| 06_sprite 07_input 08_fbo 09_sound 11_fractal \ | |||
| 12_voronoi 13_shader_builder 14_lol_lua | |||
| endif | |||
| @@ -43,6 +43,10 @@ endif | |||
| 08_fbo_CPPFLAGS = $(AM_CPPFLAGS) | |||
| 08_fbo_DEPENDENCIES = @LOL_DEPS@ | |||
| 09_sound_SOURCES = 09_sound.cpp | |||
| 09_sound_CPPFLAGS = $(AM_CPPFLAGS) | |||
| 09_sound_DEPENDENCIES = @LOL_DEPS@ | |||
| 11_fractal_SOURCES = 11_fractal.cpp 11_fractal.lolfx | |||
| 11_fractal_CPPFLAGS = $(AM_CPPFLAGS) | |||
| 11_fractal_DEPENDENCIES = @LOL_DEPS@ | |||
| @@ -28,14 +28,26 @@ | |||
| namespace lol | |||
| { | |||
| /* | |||
| struct audio_streamer | |||
| { | |||
| int m_channel; | |||
| std::function<void(void *, int)> m_callback; | |||
| #if defined LOL_USE_SDL_MIXER | |||
| Mix_Chunk *m_chunk; | |||
| #endif | |||
| }; | |||
| array<audio_streamer *> g_streamers; | |||
| /* | |||
| * Public audio class | |||
| */ | |||
| void audio::init() | |||
| { | |||
| #if defined LOL_USE_SDL_MIXER | |||
| Mix_OpenAudio(22050, AUDIO_S16, 8, 1024); | |||
| Mix_OpenAudio(22050, AUDIO_S16, 2, 1024); | |||
| set_channels(8); | |||
| #endif | |||
| } | |||
| @@ -61,8 +73,6 @@ void audio::mute_all() | |||
| { | |||
| #if defined LOL_USE_SDL_MIXER | |||
| Mix_Volume(-1,0); | |||
| #else | |||
| UNUSED(false); | |||
| #endif | |||
| } | |||
| @@ -70,8 +80,50 @@ void audio::unmute_all() | |||
| { | |||
| #if defined LOL_USE_SDL_MIXER | |||
| Mix_Volume(-1,MIX_MAX_VOLUME); | |||
| #endif | |||
| } | |||
| int audio::start_streaming(std::function<void(void *, int)> const &f) | |||
| { | |||
| #if defined LOL_USE_SDL_MIXER | |||
| static auto trampoline = [](int, void *stream, int bytes, void *udata) | |||
| { | |||
| auto s = (audio_streamer *)udata; | |||
| s->m_callback(stream, bytes); | |||
| }; | |||
| audio_streamer *s = new audio_streamer(); | |||
| g_streamers.push(s); | |||
| array<uint8_t> empty; | |||
| empty.resize(1024); | |||
| s->m_chunk = Mix_QuickLoad_RAW(empty.data(), empty.bytes()); | |||
| s->m_channel = Mix_PlayChannel(-1, s->m_chunk, -1); | |||
| s->m_callback = f; | |||
| Mix_RegisterEffect(s->m_channel, trampoline, nullptr, s); | |||
| return s->m_channel; | |||
| #else | |||
| UNUSED(f); | |||
| return -1; | |||
| #endif | |||
| } | |||
| void audio::stop_streaming(int channel) | |||
| { | |||
| #if defined LOL_USE_SDL_MIXER | |||
| for (int i = 0; i < g_streamers.count(); ++i) | |||
| { | |||
| if (g_streamers[i]->m_channel == channel) | |||
| { | |||
| Mix_HaltChannel(channel); | |||
| delete g_streamers[i]; | |||
| g_streamers.remove(i); | |||
| return; | |||
| } | |||
| } | |||
| #else | |||
| UNUSED(false); | |||
| UNUSED(channel); | |||
| #endif | |||
| } | |||
| @@ -18,7 +18,7 @@ | |||
| // Helper functions to set up the audio device. | |||
| // | |||
| #include <stdint.h> | |||
| #include <functional> | |||
| namespace lol | |||
| { | |||
| @@ -33,6 +33,9 @@ public: | |||
| static void mute_all(); | |||
| static void unmute_all(); | |||
| static int start_streaming(std::function<void(void *, int)> const &f); | |||
| static void stop_streaming(int channel); | |||
| private: | |||
| audio() {} | |||
| }; | |||