Add an option for FLuidSynth to handle MIDI
This commit is contained in:
@@ -53,6 +53,8 @@ OPTION(ALSOFT_BACKEND_COREAUDIO "Check for CoreAudio backend" ON)
|
||||
OPTION(ALSOFT_BACKEND_OPENSL "Check for OpenSL backend" ON)
|
||||
OPTION(ALSOFT_BACKEND_WAVE "Enable Wave Writer backend" ON)
|
||||
|
||||
OPTION(ALSOFT_MIDI_FLUIDSYNTH "Check for FluidSynth MIDI" ON)
|
||||
|
||||
OPTION(ALSOFT_REQUIRE_ALSA "Require ALSA backend" OFF)
|
||||
OPTION(ALSOFT_REQUIRE_OSS "Require OSS backend" OFF)
|
||||
OPTION(ALSOFT_REQUIRE_SOLARIS "Require Solaris backend" OFF)
|
||||
@@ -66,6 +68,8 @@ OPTION(ALSOFT_REQUIRE_PULSEAUDIO "Require PulseAudio backend" OFF)
|
||||
OPTION(ALSOFT_REQUIRE_COREAUDIO "Require CoreAudio backend" OFF)
|
||||
OPTION(ALSOFT_REQUIRE_OPENSL "Require OpenSL backend" OFF)
|
||||
|
||||
OPTION(ALSOFT_REQUIRE_FLUIDSYNTH "Require FluidSynth MIDI" OFF)
|
||||
|
||||
OPTION(ALSOFT_DLOPEN "Check for the dlopen API for loading optional libs" ON)
|
||||
|
||||
OPTION(ALSOFT_WERROR "Treat compile warnings as errors" OFF)
|
||||
@@ -553,6 +557,23 @@ IF(ALSOFT_REQUIRE_NEON AND NOT HAVE_NEON)
|
||||
ENDIF()
|
||||
|
||||
|
||||
SET(HAVE_MIDI_SYNTH 0)
|
||||
# Check for FluidSynth support
|
||||
IF(ALSOFT_MIDI_FLUIDSYNTH)
|
||||
FIND_PACKAGE(FluidSynth)
|
||||
IF(FLUIDSYNTH_FOUND)
|
||||
SET(HAVE_FLUIDSYNTH 1)
|
||||
IF(CMAKE_VERSION VERSION_LESS "2.8.8")
|
||||
INCLUDE_DIRECTORIES(${FLUIDSYNTH_INCLUDE_DIR})
|
||||
ENDIF()
|
||||
SET(EXTRA_LIBS ${FLUIDSYNTH_LIBRARIES} ${EXTRA_LIBS})
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
IF(ALSOFT_REQUIRE_FLUIDSYNTH AND NOT HAVE_FLUIDSYNTH)
|
||||
MESSAGE(FATAL_ERROR "Failed to enabled required FluidSynth support")
|
||||
ENDIF()
|
||||
|
||||
|
||||
SET(ALC_OBJS ${ALC_OBJS}
|
||||
Alc/backends/base.c
|
||||
# Default backends, always available
|
||||
@@ -847,6 +868,9 @@ IF(WIN32 AND ALSOFT_NO_UID_DEFS)
|
||||
SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY COMPILE_DEFINITIONS AL_NO_UID_DEFS)
|
||||
ENDIF()
|
||||
SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES "${OpenAL_SOURCE_DIR}/OpenAL32/Include" "${OpenAL_SOURCE_DIR}/Alc")
|
||||
IF(FLUIDSYNTH_FOUND)
|
||||
SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${FLUIDSYNTH_INCLUDE_DIR})
|
||||
ENDIF()
|
||||
SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES VERSION ${LIB_VERSION}
|
||||
SOVERSION ${LIB_MAJOR_VERSION})
|
||||
IF(WIN32 AND NOT LIBTYPE STREQUAL "STATIC")
|
||||
@@ -880,6 +904,10 @@ MESSAGE(STATUS "")
|
||||
MESSAGE(STATUS "Building with support for CPU extensions:")
|
||||
MESSAGE(STATUS " ${CPU_EXTS}")
|
||||
MESSAGE(STATUS "")
|
||||
IF(HAVE_FLUIDSYNTH)
|
||||
MESSAGE(STATUS "FluidSynth support for ALC_SOFT_midi_interface enabled")
|
||||
MESSAGE(STATUS "")
|
||||
ENDIF()
|
||||
|
||||
IF(WIN32)
|
||||
IF(NOT HAVE_DSOUND)
|
||||
|
||||
+240
-8
@@ -100,6 +100,222 @@ static ALenum MidiSynth_insertEvent(MidiSynth *self, ALuint64 time, ALuint event
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_FLUIDSYNTH
|
||||
|
||||
#include <fluidsynth.h>
|
||||
|
||||
typedef struct FSynth {
|
||||
DERIVE_FROM_TYPE(MidiSynth);
|
||||
|
||||
/* NOTE: This rwlock is for setting the soundfont. The EventQueue and
|
||||
* related must use the device lock as they're used in the mixer thread.
|
||||
*/
|
||||
RWLock Lock;
|
||||
|
||||
fluid_settings_t *Settings;
|
||||
fluid_synth_t *Synth;
|
||||
int FontID;
|
||||
} FSynth;
|
||||
|
||||
static void FSynth_Construct(FSynth *self, ALCdevice *device);
|
||||
static void FSynth_Destruct(FSynth *self);
|
||||
static ALboolean FSynth_init(FSynth *self, ALCdevice *device);
|
||||
static void FSynth_setState(FSynth *self, ALenum state);
|
||||
static void FSynth_update(FSynth *self, ALCdevice *device);
|
||||
static void FSynth_process(FSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]);
|
||||
static void FSynth_Delete(FSynth *self);
|
||||
DEFINE_MIDISYNTH_VTABLE(FSynth);
|
||||
|
||||
|
||||
static void FSynth_Construct(FSynth *self, ALCdevice *device)
|
||||
{
|
||||
MidiSynth_Construct(STATIC_CAST(MidiSynth, self), device);
|
||||
SET_VTABLE2(FSynth, MidiSynth, self);
|
||||
|
||||
RWLockInit(&self->Lock);
|
||||
|
||||
self->Settings = NULL;
|
||||
self->Synth = NULL;
|
||||
self->FontID = FLUID_FAILED;
|
||||
}
|
||||
|
||||
static void FSynth_Destruct(FSynth *self)
|
||||
{
|
||||
if(self->FontID != FLUID_FAILED)
|
||||
fluid_synth_sfunload(self->Synth, self->FontID, 0);
|
||||
self->FontID = FLUID_FAILED;
|
||||
|
||||
if(self->Synth != NULL)
|
||||
delete_fluid_synth(self->Synth);
|
||||
self->Synth = NULL;
|
||||
|
||||
if(self->Settings != NULL)
|
||||
delete_fluid_settings(self->Settings);
|
||||
self->Settings = NULL;
|
||||
|
||||
MidiSynth_Destruct(STATIC_CAST(MidiSynth, self));
|
||||
}
|
||||
|
||||
static ALboolean FSynth_init(FSynth *self, ALCdevice *device)
|
||||
{
|
||||
self->Settings = new_fluid_settings();
|
||||
if(!self->Settings)
|
||||
{
|
||||
ERR("Failed to create FluidSettings\n");
|
||||
return AL_FALSE;
|
||||
}
|
||||
|
||||
fluid_settings_setint(self->Settings, "synth.reverb.active", 1);
|
||||
fluid_settings_setint(self->Settings, "synth.chorus.active", 1);
|
||||
fluid_settings_setint(self->Settings, "synth.polyphony", 256);
|
||||
fluid_settings_setstr(self->Settings, "synth.midi-bank-select", "mma");
|
||||
fluid_settings_setnum(self->Settings, "synth.sample-rate", device->Frequency);
|
||||
|
||||
self->Synth = new_fluid_synth(self->Settings);
|
||||
if(!self->Synth)
|
||||
{
|
||||
ERR("Failed to create FluidSynth\n");
|
||||
return AL_FALSE;
|
||||
}
|
||||
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static void FSynth_setState(FSynth *self, ALenum state)
|
||||
{
|
||||
WriteLock(&self->Lock);
|
||||
if(state == AL_PLAYING)
|
||||
{
|
||||
if(self->FontID == FLUID_FAILED)
|
||||
{
|
||||
int fontid = FLUID_FAILED;
|
||||
const char *fname = getenv("FLUID_SOUNDFONT");
|
||||
if(fname && fname[0])
|
||||
fontid = fluid_synth_sfload(self->Synth, fname, 1);
|
||||
if(fontid != FLUID_FAILED)
|
||||
self->FontID = fontid;
|
||||
else
|
||||
ERR("Failed to load soundfont '%s'\n", fname?fname:"(nil)");
|
||||
}
|
||||
}
|
||||
MidiSynth_setState(STATIC_CAST(MidiSynth, self), state);
|
||||
WriteUnlock(&self->Lock);
|
||||
}
|
||||
|
||||
static void FSynth_update(FSynth *self, ALCdevice *device)
|
||||
{
|
||||
fluid_settings_setnum(self->Settings, "synth.sample-rate", device->Frequency);
|
||||
fluid_synth_set_sample_rate(self->Synth, device->Frequency);
|
||||
MidiSynth_update(STATIC_CAST(MidiSynth, self), device);
|
||||
}
|
||||
|
||||
|
||||
static void FSynth_processQueue(FSynth *self, ALuint64 time)
|
||||
{
|
||||
EvtQueue *queue = &STATIC_CAST(MidiSynth, self)->EventQueue;
|
||||
|
||||
while(queue->pos < queue->size && queue->events[queue->pos].time <= time)
|
||||
{
|
||||
const MidiEvent *evt = &queue->events[queue->pos];
|
||||
|
||||
switch((evt->event&0xF0))
|
||||
{
|
||||
case AL_NOTEOFF_SOFT:
|
||||
fluid_synth_noteoff(self->Synth, (evt->event&0x0F), evt->param[0]);
|
||||
break;
|
||||
case AL_NOTEON_SOFT:
|
||||
fluid_synth_noteon(self->Synth, (evt->event&0x0F), evt->param[0], evt->param[1]);
|
||||
break;
|
||||
case AL_AFTERTOUCH_SOFT:
|
||||
break;
|
||||
|
||||
case AL_CONTROLLERCHANGE_SOFT:
|
||||
fluid_synth_cc(self->Synth, (evt->event&0x0F), evt->param[0], evt->param[1]);
|
||||
break;
|
||||
case AL_PROGRAMCHANGE_SOFT:
|
||||
fluid_synth_program_change(self->Synth, (evt->event&0x0F), evt->param[0]);
|
||||
break;
|
||||
|
||||
case AL_CHANNELPRESSURE_SOFT:
|
||||
fluid_synth_channel_pressure(self->Synth, (evt->event&0x0F), evt->param[0]);
|
||||
break;
|
||||
|
||||
case AL_PITCHBEND_SOFT:
|
||||
fluid_synth_pitch_bend(self->Synth, (evt->event&0x0F), (evt->param[0]&0x7F) |
|
||||
((evt->param[1]&0x7F)<<7));
|
||||
break;
|
||||
}
|
||||
|
||||
queue->pos++;
|
||||
}
|
||||
|
||||
if(queue->pos == queue->size)
|
||||
{
|
||||
queue->pos = 0;
|
||||
queue->size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void FSynth_process(FSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE])
|
||||
{
|
||||
MidiSynth *synth = STATIC_CAST(MidiSynth, self);
|
||||
ALuint total = 0;
|
||||
|
||||
if(synth->State != AL_PLAYING)
|
||||
{
|
||||
if(synth->State == AL_PAUSED)
|
||||
fluid_synth_write_float(self->Synth, SamplesToDo, DryBuffer[FrontLeft], 0, 1,
|
||||
DryBuffer[FrontRight], 0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
while(total < SamplesToDo)
|
||||
{
|
||||
if(synth->SamplesToNext >= 1.0)
|
||||
{
|
||||
ALuint todo = minu(SamplesToDo - total, fastf2u(synth->SamplesToNext));
|
||||
|
||||
fluid_synth_write_float(self->Synth, todo,
|
||||
&DryBuffer[FrontLeft][total], 0, 1,
|
||||
&DryBuffer[FrontRight][total], 0, 1);
|
||||
total += todo;
|
||||
synth->SamplesSinceLast += todo;
|
||||
synth->SamplesToNext -= todo;
|
||||
}
|
||||
else
|
||||
{
|
||||
ALuint64 time = synth->NextEvtTime;
|
||||
if(time == UINT64_MAX)
|
||||
{
|
||||
synth->SamplesSinceLast += SamplesToDo-total;
|
||||
fluid_synth_write_float(self->Synth, SamplesToDo-total,
|
||||
&DryBuffer[FrontLeft][total], 0, 1,
|
||||
&DryBuffer[FrontRight][total], 0, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
synth->SamplesSinceLast -= (time - synth->LastEvtTime) * synth->SamplesPerTick;
|
||||
synth->SamplesSinceLast = maxd(synth->SamplesSinceLast, 0.0);
|
||||
synth->LastEvtTime = time;
|
||||
FSynth_processQueue(self, time);
|
||||
|
||||
synth->NextEvtTime = MidiSynth_getNextEvtTime(synth);
|
||||
if(synth->NextEvtTime != UINT64_MAX)
|
||||
synth->SamplesToNext += (synth->NextEvtTime - synth->LastEvtTime) * synth->SamplesPerTick;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void FSynth_Delete(FSynth *self)
|
||||
{
|
||||
free(self);
|
||||
}
|
||||
|
||||
|
||||
#endif /* HAVE_FLUIDSYNTH */
|
||||
|
||||
|
||||
typedef struct DSynth {
|
||||
DERIVE_FROM_TYPE(MidiSynth);
|
||||
} DSynth;
|
||||
@@ -184,15 +400,31 @@ static void DSynth_Delete(DSynth *self)
|
||||
|
||||
MidiSynth *SynthCreate(ALCdevice *device)
|
||||
{
|
||||
DSynth *synth = calloc(1, sizeof(*synth));
|
||||
if(!synth)
|
||||
{
|
||||
ERR("Failed to allocate DSynth\n");
|
||||
return NULL;
|
||||
}
|
||||
DSynth_Construct(synth, device);
|
||||
FSynth *fsynth;
|
||||
DSynth *dsynth;
|
||||
|
||||
return STATIC_CAST(MidiSynth, synth);
|
||||
fsynth = calloc(1, sizeof(*fsynth));
|
||||
if(!fsynth)
|
||||
ERR("Failed to allocate FSynth\n");
|
||||
else
|
||||
{
|
||||
FSynth_Construct(fsynth, device);
|
||||
if(FSynth_init(fsynth, device))
|
||||
return STATIC_CAST(MidiSynth, fsynth);
|
||||
DELETE_OBJ(STATIC_CAST(MidiSynth, fsynth));
|
||||
fsynth = NULL;
|
||||
}
|
||||
|
||||
dsynth = calloc(1, sizeof(*dsynth));
|
||||
if(!dsynth)
|
||||
ERR("Failed to allocate DSynth\n");
|
||||
else
|
||||
{
|
||||
DSynth_Construct(dsynth, device);
|
||||
return STATIC_CAST(MidiSynth, dsynth);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# - 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)
|
||||
|
||||
@@ -29,6 +29,9 @@
|
||||
/* Define if we have ARM Neon CPU extensions */
|
||||
#cmakedefine HAVE_NEON
|
||||
|
||||
/* Define if we have FluidSynth support */
|
||||
#cmakedefine HAVE_FLUIDSYNTH
|
||||
|
||||
/* Define if we have the ALSA backend */
|
||||
#cmakedefine HAVE_ALSA
|
||||
|
||||
|
||||
Reference in New Issue
Block a user