Skip to content

Instantly share code, notes, and snippets.

@take-cheeze
Created June 21, 2014 06:49
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save take-cheeze/43f026d07547b9399738 to your computer and use it in GitHub Desktop.
Save take-cheeze/43f026d07547b9399738 to your computer and use it in GitHub Desktop.
Playing MIDI with fluidsynth on OpenAL.
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#include <boost/assert.hpp>
#include <cstdint>
#include <cstdlib>
#include <chrono>
#include <iostream>
#include <memory>
#include <thread>
#include <vector>
#include <fluidsynth.h>
#include <fluidsynth/midi.h>
#include <fluidsynth/seq.h>
#include <fluidsynth/seqbind.h>
namespace {
void load_to_buffer(fluid_synth_t *synth, ALuint buffer, unsigned sample_rate) {
std::vector<int16_t> buf_data(sample_rate * 2); // stereo
fluid_synth_write_s16(synth, sample_rate,
buf_data.data(), 0, 2,
buf_data.data(), 1, 2);
alBufferData(buffer, AL_FORMAT_STEREO16, buf_data.data(),
sizeof(int16_t) * buf_data.size(), sample_rate);
}
}
int main(int argc, char *argv[]) {
BOOST_ASSERT(argc == 3);
std::shared_ptr<ALCdevice> dev(alcOpenDevice(nullptr), &alcCloseDevice);
std::shared_ptr<ALCcontext> ctx(alcCreateContext(dev.get(), nullptr), &alcDestroyContext);
alcMakeContextCurrent(ctx.get());
std::shared_ptr<fluid_settings_t> settings(new_fluid_settings(), &delete_fluid_settings);
fluid_settings_setstr(settings.get(), "player.timing-source", "sample");
fluid_settings_setint(settings.get(), "synth.lock-memory", 0);
std::shared_ptr<fluid_synth_t> synth(new_fluid_synth(settings.get()), &delete_fluid_synth);
BOOST_VERIFY(fluid_synth_sfload(synth.get(), argv[1], 1) != -1);
std::shared_ptr<fluid_sequencer_t> seq(new_fluid_sequencer2(false), &delete_fluid_sequencer);
fluid_sequencer_register_fluidsynth(seq.get(), synth.get());
double sample_rate;
fluid_settings_getnum(settings.get(), "synth.sample-rate", &sample_rate);
enum { BUFFER_NUM = 3 };
std::cout << "synth.sample-rate: " << sample_rate << std::endl;
std::shared_ptr<fluid_player_t> player(new_fluid_player(synth.get()), &delete_fluid_player);
fluid_player_add(player.get(), argv[2]);
fluid_player_play(player.get());
ALuint buffers[BUFFER_NUM];
alGenBuffers(BUFFER_NUM, buffers);
for (int i = 0; i < BUFFER_NUM; ++i) {
// std::cout << "Tick: " << fluid_sequencer_get_tick(seq.get()) << std::endl;
load_to_buffer(synth.get(), buffers[i], sample_rate);
}
ALuint source;
alGenSources(1, &source);
alSourceQueueBuffers(source, 3, buffers);
alSourcePlay(source);
while (fluid_player_get_status(player.get()) == FLUID_PLAYER_PLAYING) {
BOOST_ASSERT(alGetError() == AL_NO_ERROR);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
ALint val;
alGetSourcei(source, AL_BUFFERS_PROCESSED, &val);
if (val == 0) { continue; }
ALuint unqueued[val];
alSourceUnqueueBuffers(source, val, unqueued);
for (int i = 0; i < val; ++i) {
// std::cout << "Tick: " << fluid_sequencer_get_tick(seq.get()) << std::endl;
load_to_buffer(synth.get(), unqueued[i], sample_rate);
}
alSourceQueueBuffers(source, val, unqueued);
ALint source_type;
alGetSourcei(source, AL_SOURCE_TYPE, &source_type);
BOOST_ASSERT(source_type == AL_STREAMING);
}
std::cout << "End of midi" << std::endl;
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment