|
From: rainbyte <rai...@us...> - 2012-07-22 00:13:42
|
Author: Alvaro Fernando Garcia <alv...@gm...>
Date: Sat Jul 21 21:06:42 2012 -0300
Midi Synth:
- Added basic fluidsynth support (optional via cmake).
- Soundfont download needed (named as TimGM6mb.sf2).
- Midi instruments can be tested on practice screen.
---
cmake/Modules/FindFluidSynth.cmake | 22 +++++++++
game/CMakeLists.txt | 14 ++++++
game/fluidsynth.cc | 91 ++++++++++++++++++++++++++++++++++++
game/fluidsynth.hh | 71 ++++++++++++++++++++++++++++
game/input_midi.cc | 13 ++++-
game/main.cc | 5 ++
6 files changed, 214 insertions(+), 2 deletions(-)
diff --git a/cmake/Modules/FindFluidSynth.cmake b/cmake/Modules/FindFluidSynth.cmake
new file mode 100644
index 0000000..e668c8f
--- /dev/null
+++ b/cmake/Modules/FindFluidSynth.cmake
@@ -0,0 +1,22 @@
+# - Find fluidsynth
+# Find the native fluidsynth includes and library
+#
+# FLUIDSYNTH_INCLUDE_DIR - where to find fluidsynth.h
+# FLUIDSYNTH_LIBRARIES - List of libraries when using fluidsynth.
+# FLUIDSYNTH_FOUND - True if fluidsynth found.
+
+
+IF (FLUIDSYNTH_INCLUDE_DIR AND FLUIDSYNTH_LIBRARIES)
+ # Already in cache, be silent
+ SET(FluidSynth_FIND_QUIETLY TRUE)
+ENDIF (FLUIDSYNTH_INCLUDE_DIR AND FLUIDSYNTH_LIBRARIES)
+
+FIND_PATH(FLUIDSYNTH_INCLUDE_DIR fluidsynth.h)
+
+FIND_LIBRARY(FLUIDSYNTH_LIBRARIES NAMES fluidsynth )
+MARK_AS_ADVANCED( FLUIDSYNTH_LIBRARIES FLUIDSYNTH_INCLUDE_DIR )
+
+# handle the QUIETLY and REQUIRED arguments and set FLUIDSYNTH_FOUND to TRUE if
+# all listed variables are TRUE
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(FluidSynth DEFAULT_MSG FLUIDSYNTH_LIBRARIES FLUIDSYNTH_INCLUDE_DIR)
diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt
index f92c742..6ed0b6b 100644
--- a/game/CMakeLists.txt
+++ b/game/CMakeLists.txt
@@ -102,6 +102,20 @@ else()
message(STATUS "MIDI I/O: Disabled (explicitly disabled)")
endif()
+if(NOT NO_FLUIDSYNTH)
+ find_package(FluidSynth)
+ if(FLUIDSYNTH_FOUND)
+ include_directories(${FLUIDSYNTH_INCLUDE_DIRS})
+ list(APPEND LIBS ${FLUIDSYNTH_LIBRARIES})
+ add_definitions("-DUSE_FLUIDSYNTH")
+ message(STATUS "FluidSynth support: Enabled")
+ else()
+ message(STATUS "FluidSynth support: Disabled (libfluidsynth not found)")
+ endif()
+else()
+ message(STATUS "FluidSynth support: Disabled (explicitly disabled)")
+endif()
+
if(NOT NO_WEBCAM)
find_package(OpenCV)
if(OpenCV_FOUND)
diff --git a/game/fluidsynth.cc b/game/fluidsynth.cc
new file mode 100644
index 0000000..5e3cb2d
--- /dev/null
+++ b/game/fluidsynth.cc
@@ -0,0 +1,91 @@
+#include "fluidsynth.hh"
+
+synth::FluidPtr synth::midiSynth;
+
+// TODO: add error handling
+
+#ifdef USE_FLUIDSYNTH
+
+synth::Fluid::Fluid() {
+ _settings = new_fluid_settings();
+ _synth = new_fluid_synth(_settings);
+
+ // TODO: remove this and implement audioOut function instead
+ // this config is here only for testing purposes
+ fluid_settings_setstr(_settings, "audio.driver", "pulseaudio");
+
+ _adriver = new_fluid_audio_driver(_settings, _synth);
+
+ // Set audio gain from 0.2 to 2.5 (affects output volume)
+ fluid_settings_setnum(_settings, "synth.gain", 2.0);
+
+
+ // TODO: improve soundfont loading (or add a default soundfont)
+ // TimGM6mb.sf2 (public domain soundfont) being used to test
+ fs::path sf_file = getDataDir() / "TimGM6mb.sf2";
+
+ if (!fs::exists(sf_file)) {
+ std::cout << "##" << std::endl;
+ std::cout << "## synth/info: Skipping " << sf_file << std::endl;
+ std::cout << "## TimGM6mb.sf2 soundfont not found, please download it to the data folder" << std::endl;
+ std::cout << "## Soundfont's webpage: http://musescore.org/en/handbook/soundfont/" << std::endl;
+ std::cout << "##" << std::endl;
+ } else sf_id = fontLoad(sf_file.string());
+}
+
+synth::Fluid::~Fluid() {
+
+}
+
+int synth::Fluid::fontLoad(std::string sf_filename) {
+ std::cout << sf_filename.c_str() << std::endl;
+ int id = fluid_synth_sfload(_synth, sf_filename.c_str(), true);
+ if(id) return id;
+ throw std::runtime_error("Could not load soundfont");
+}
+
+void synth::Fluid::fontUnload(int id) {
+ fluid_synth_sfunload(_synth, id, true);
+}
+
+void synth::Fluid::sendNoteTimed(int chan, int note, int vel, int msecs) {
+ fluid_synth_noteon(_synth, chan, note, vel);
+
+ boost::this_thread::sleep( boost::posix_time::milliseconds(msecs) );
+
+ fluid_synth_noteoff(_synth, chan, note);
+}
+
+void synth::Fluid::sendNoteOn(int chan, int note, int vel) {
+ fluid_synth_noteon(_synth, chan, note, vel);
+}
+
+void synth::Fluid::sendNoteOff(int chan, int note) {
+ fluid_synth_noteoff(_synth, chan, note);
+}
+
+void synth::Fluid::setGain(float gain) {
+ fluid_synth_set_gain(_synth, gain);
+}
+
+float synth::Fluid::getGain() {
+ return fluid_synth_get_gain(_synth);
+}
+
+// TODO: implement audio output inside the game
+/*
+void synth::Fluid::audioOut() {
+ //use fluid_synth_write_s16() here
+}
+*/
+
+#endif // USE_FLUIDSYNTH
+
+void synth::init() {
+ try {
+ synth::midiSynth.reset(new synth::Fluid());
+ }
+ catch (std::runtime_error& e) {
+ std::clog << "synth/info: " << e.what() << std::endl;
+ }
+}
diff --git a/game/fluidsynth.hh b/game/fluidsynth.hh
new file mode 100644
index 0000000..d120af3
--- /dev/null
+++ b/game/fluidsynth.hh
@@ -0,0 +1,71 @@
+#pragma once
+
+#include <stdexcept>
+#include <iostream>
+#include <boost/scoped_ptr.hpp>
+#include <boost/thread/thread.hpp>
+
+#include <fluidsynth.h>
+
+#include "fs.hh"
+
+namespace synth {
+
+#ifdef USE_FLUIDSYNTH
+ class Fluid {
+ /*
+ Common Parameters:
+ _synth FluidSynth instance
+ chan MIDI channel number (0 to MIDI channel count - 1)
+ note MIDI note number (0-127)
+ vel MIDI velocity (0-127, 0=noteoff)
+ sf_id identifier needed to unload the soundfont
+ */
+
+ public:
+ static bool enabled() { return true; }
+ Fluid();
+ ~Fluid();
+ int fontLoad(std::string sf_filename);
+ void fontUnload(int id);
+ void sendNoteTimed(int chan, int note, int vel, int msecs);
+ void sendNoteOn(int chan, int note, int vel);
+ void sendNoteOff(int chan, int note);
+ void setGain(float gain);
+ float getGain();
+ // TODO: implement audio output inside the game
+ // void audioOut();
+
+ private:
+ int sf_id;
+
+ // fluidsynth specific variables
+ fluid_settings_t* _settings;
+ fluid_synth_t* _synth;
+ fluid_audio_driver_t* _adriver;
+
+ };
+#else
+ class Fluid {
+ public:
+ static bool enabled() { return false; }
+ Fluid();
+ int fontLoad(std::string sf_filename);
+ void fontUnload(void) {};
+ void sendNoteTimed(void, void, void, void) {};
+ void sendNoteOn(void, void, void) {};
+ void sendNoteOff(void, void) {};
+ void setGain(void) {};
+ void getGain() {};
+ void audioOut();
+
+ private:
+ };
+
+#endif // USE_FLUIDSYNTH
+
+ typedef boost::scoped_ptr<Fluid> FluidPtr;
+ extern FluidPtr midiSynth;
+
+ void init();
+} // namespace fluid
diff --git a/game/input_midi.cc b/game/input_midi.cc
index 38b23c7..680e854 100644
--- a/game/input_midi.cc
+++ b/game/input_midi.cc
@@ -1,5 +1,9 @@
#include "input.hh"
+#ifdef USE_FLUIDSYNTH
+#include "fluidsynth.hh"
+#endif // USE_FLUIDSYNTH
+
input::midi::MidiDevicePtr input::midi::midiDevice;
#ifdef USE_PORTMIDI
@@ -59,9 +63,14 @@ void input::midi::MidiDevice::process() {
// code snippet left here for visibility
unsigned char chan = ev.message & 0x0F;
if (chan != 0x09) continue; // only accept channel 10 (percussion)
-#endif
+#endif // 0
if (evnt != 0x90) continue; // 0x90 = any channel note-on
if (vel == 0x00) continue; // velocity 0 is often used instead of note-off
+
+#ifdef USE_FLUIDSYNTH
+ synth::midiSynth->sendNoteTimed(0, (int)note, (int)vel, 0);
+#endif // USE_FLUIDSYNTH
+
Map::const_iterator it = map.find(note);
if (it == map.end()) {
std::cout << "Unassigned MIDI drum event: note " << note << std::endl;
@@ -75,7 +84,7 @@ void input::midi::MidiDevice::process() {
}
}
-#endif
+#endif // USE_PORTMIDI
void input::midi::init(input::Instruments& instruments) {
// TODO: Proper error handling...
diff --git a/game/main.cc b/game/main.cc
index 50b265e..773c4f8 100644
--- a/game/main.cc
+++ b/game/main.cc
@@ -2,6 +2,7 @@
#include "fs.hh"
#include "screen.hh"
#include "input.hh"
+#include "fluidsynth.hh"
#include "profiler.hh"
#include "songs.hh"
#include "backgrounds.hh"
@@ -126,6 +127,8 @@ void mainLoop(std::string const& songlist) {
Songs songs(database, songlist);
ScreenManager sm(window);
try {
+ // Initialize midi synthetizer
+ synth::init();
// Load audio samples
sm.loading(_("Loading audio samples..."), 0.5);
audio.loadSample("drum bass", getPath("sounds/drum_bass.ogg"));
@@ -378,5 +381,7 @@ void outputOptionalFeatureStatus() {
(input::midi::MidiDevice::enabled() ? "Enabled" : "Disabled")
<< std::endl << " Webcam support: " <<
(Webcam::enabled() ? "Enabled" : "Disabled")
+ << std::endl << " Fluidsynth support: " <<
+ (synth::Fluid::enabled() ? "Enabled" : "Disabled")
<< std::endl << std::endl;
}
|