瀏覽代碼

audio: minimal streaming API

This will allow us to generate sounds on the fly on several dedicated
channels. Until now we could only play fully loaded samples.
legacy
Sam Hocevar 10 年之前
父節點
當前提交
7619caef3e
共有 5 個文件被更改,包括 181 次插入7 次删除
  1. +1
    -0
      .gitignore
  2. +114
    -0
      doc/tutorial/09_sound.cpp
  3. +5
    -1
      doc/tutorial/Makefile.am
  4. +57
    -5
      src/audio/audio.cpp
  5. +4
    -1
      src/lol/audio/audio.h

+ 1
- 0
.gitignore 查看文件

@@ -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


+ 114
- 0
doc/tutorial/09_sound.cpp 查看文件

@@ -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;
}


+ 5
- 1
doc/tutorial/Makefile.am 查看文件

@@ -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@


+ 57
- 5
src/audio/audio.cpp 查看文件

@@ -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
}



+ 4
- 1
src/lol/audio/audio.h 查看文件

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


Loading…
取消
儲存