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() {} | |||
}; | |||