Kaynağa Gözat

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 8 yıl önce
ebeveyn
işleme
7619caef3e
5 değiştirilmiş dosya ile 181 ekleme ve 7 silme
  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 Dosyayı Görüntüle

@@ -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 Dosyayı Görüntüle

@@ -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 Dosyayı Görüntüle

@@ -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 Dosyayı Görüntüle

@@ -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 Dosyayı Görüntüle

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


Yükleniyor…
İptal
Kaydet