294 lines
10 KiB
C
294 lines
10 KiB
C
|
/* FluidSynth - A Software Synthesizer
|
||
|
*
|
||
|
* Copyright (C) 2003 Peter Hanappe and others.
|
||
|
*
|
||
|
* This library is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU Library General Public License
|
||
|
* as published by the Free Software Foundation; either version 2 of
|
||
|
* the License, or (at your option) any later version.
|
||
|
*
|
||
|
* This library is distributed in the hope that it will be useful, but
|
||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
* Library General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Library General Public
|
||
|
* License along with this library; if not, write to the Free
|
||
|
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||
|
* 02110-1301, USA
|
||
|
*/
|
||
|
|
||
|
#include "fluid_rvoice_event.h"
|
||
|
#include "fluid_rvoice.h"
|
||
|
#include "fluid_rvoice_mixer.h"
|
||
|
#include "fluid_iir_filter.h"
|
||
|
#include "fluid_lfo.h"
|
||
|
#include "fluid_adsr_env.h"
|
||
|
|
||
|
#define EVENTFUNC_0(proc, type) \
|
||
|
if (event->method == proc) { \
|
||
|
proc((type) event->object); \
|
||
|
return; }
|
||
|
|
||
|
#define EVENTFUNC_R1(proc, type) \
|
||
|
if (event->method == proc) { \
|
||
|
if(event->intparam != 0) { FLUID_LOG(FLUID_DBG, "IR-mismatch"); } \
|
||
|
proc((type) event->object, event->realparams[0]); \
|
||
|
return; }
|
||
|
|
||
|
#define EVENTFUNC_PTR(proc, type, type2) \
|
||
|
if (event->method == proc) { \
|
||
|
proc((type) event->object, (type2) event->ptr); \
|
||
|
return; }
|
||
|
|
||
|
#define EVENTFUNC_I1(proc, type) \
|
||
|
if (event->method == proc) { \
|
||
|
if(event->realparams[0] != 0.0f) { FLUID_LOG(FLUID_DBG, "IR-mismatch"); } \
|
||
|
proc((type) event->object, event->intparam); \
|
||
|
return; }
|
||
|
|
||
|
#define EVENTFUNC_IR(proc, type) \
|
||
|
if (event->method == proc) { \
|
||
|
proc((type) event->object, event->intparam, event->realparams[0]); \
|
||
|
return; }
|
||
|
|
||
|
#define EVENTFUNC_ALL(proc, type) \
|
||
|
if (event->method == proc) { \
|
||
|
proc((type) event->object, event->intparam, event->realparams[0], \
|
||
|
event->realparams[1], event->realparams[2], event->realparams[3], \
|
||
|
event->realparams[4]); \
|
||
|
return; }
|
||
|
|
||
|
#define EVENTFUNC_R4(proc, type) \
|
||
|
if (event->method == proc) { \
|
||
|
proc((type) event->object, event->intparam, event->realparams[0], \
|
||
|
event->realparams[1], event->realparams[2], event->realparams[3]); \
|
||
|
return; }
|
||
|
|
||
|
void
|
||
|
fluid_rvoice_event_dispatch(fluid_rvoice_event_t* event)
|
||
|
{
|
||
|
EVENTFUNC_PTR(fluid_rvoice_mixer_add_voice, fluid_rvoice_mixer_t*, fluid_rvoice_t*);
|
||
|
EVENTFUNC_I1(fluid_rvoice_noteoff, fluid_rvoice_t*);
|
||
|
EVENTFUNC_0(fluid_rvoice_voiceoff, fluid_rvoice_t*);
|
||
|
EVENTFUNC_0(fluid_rvoice_reset, fluid_rvoice_t*);
|
||
|
|
||
|
EVENTFUNC_ALL(fluid_adsr_env_set_data, fluid_adsr_env_t*);
|
||
|
|
||
|
EVENTFUNC_I1(fluid_lfo_set_delay, fluid_lfo_t*);
|
||
|
EVENTFUNC_R1(fluid_lfo_set_incr, fluid_lfo_t*);
|
||
|
|
||
|
EVENTFUNC_R1(fluid_iir_filter_set_fres, fluid_iir_filter_t*);
|
||
|
EVENTFUNC_R1(fluid_iir_filter_set_q_dB, fluid_iir_filter_t*);
|
||
|
|
||
|
EVENTFUNC_IR(fluid_rvoice_buffers_set_mapping, fluid_rvoice_buffers_t*);
|
||
|
EVENTFUNC_IR(fluid_rvoice_buffers_set_amp, fluid_rvoice_buffers_t*);
|
||
|
|
||
|
EVENTFUNC_R1(fluid_rvoice_set_modenv_to_pitch, fluid_rvoice_t*);
|
||
|
EVENTFUNC_R1(fluid_rvoice_set_output_rate, fluid_rvoice_t*);
|
||
|
EVENTFUNC_R1(fluid_rvoice_set_root_pitch_hz, fluid_rvoice_t*);
|
||
|
EVENTFUNC_R1(fluid_rvoice_set_synth_gain, fluid_rvoice_t*);
|
||
|
EVENTFUNC_R1(fluid_rvoice_set_pitch, fluid_rvoice_t*);
|
||
|
EVENTFUNC_R1(fluid_rvoice_set_attenuation, fluid_rvoice_t*);
|
||
|
EVENTFUNC_R1(fluid_rvoice_set_min_attenuation_cB, fluid_rvoice_t*);
|
||
|
EVENTFUNC_R1(fluid_rvoice_set_viblfo_to_pitch, fluid_rvoice_t*);
|
||
|
EVENTFUNC_R1(fluid_rvoice_set_modlfo_to_pitch, fluid_rvoice_t*);
|
||
|
EVENTFUNC_R1(fluid_rvoice_set_modlfo_to_vol, fluid_rvoice_t*);
|
||
|
EVENTFUNC_R1(fluid_rvoice_set_modlfo_to_fc, fluid_rvoice_t*);
|
||
|
EVENTFUNC_R1(fluid_rvoice_set_modenv_to_fc, fluid_rvoice_t*);
|
||
|
EVENTFUNC_R1(fluid_rvoice_set_modenv_to_pitch, fluid_rvoice_t*);
|
||
|
EVENTFUNC_I1(fluid_rvoice_set_interp_method, fluid_rvoice_t*);
|
||
|
EVENTFUNC_I1(fluid_rvoice_set_start, fluid_rvoice_t*);
|
||
|
EVENTFUNC_I1(fluid_rvoice_set_end, fluid_rvoice_t*);
|
||
|
EVENTFUNC_I1(fluid_rvoice_set_loopstart, fluid_rvoice_t*);
|
||
|
EVENTFUNC_I1(fluid_rvoice_set_loopend, fluid_rvoice_t*);
|
||
|
EVENTFUNC_I1(fluid_rvoice_set_samplemode, fluid_rvoice_t*);
|
||
|
EVENTFUNC_PTR(fluid_rvoice_set_sample, fluid_rvoice_t*, fluid_sample_t*);
|
||
|
|
||
|
EVENTFUNC_R1(fluid_rvoice_mixer_set_samplerate, fluid_rvoice_mixer_t*);
|
||
|
EVENTFUNC_I1(fluid_rvoice_mixer_set_polyphony, fluid_rvoice_mixer_t*);
|
||
|
EVENTFUNC_I1(fluid_rvoice_mixer_set_reverb_enabled, fluid_rvoice_mixer_t*);
|
||
|
EVENTFUNC_I1(fluid_rvoice_mixer_set_chorus_enabled, fluid_rvoice_mixer_t*);
|
||
|
EVENTFUNC_I1(fluid_rvoice_mixer_set_mix_fx, fluid_rvoice_mixer_t*);
|
||
|
EVENTFUNC_0(fluid_rvoice_mixer_reset_fx, fluid_rvoice_mixer_t*);
|
||
|
EVENTFUNC_0(fluid_rvoice_mixer_reset_reverb, fluid_rvoice_mixer_t*);
|
||
|
EVENTFUNC_0(fluid_rvoice_mixer_reset_chorus, fluid_rvoice_mixer_t*);
|
||
|
EVENTFUNC_IR(fluid_rvoice_mixer_set_threads, fluid_rvoice_mixer_t*);
|
||
|
|
||
|
EVENTFUNC_ALL(fluid_rvoice_mixer_set_chorus_params, fluid_rvoice_mixer_t*);
|
||
|
EVENTFUNC_R4(fluid_rvoice_mixer_set_reverb_params, fluid_rvoice_mixer_t*);
|
||
|
|
||
|
FLUID_LOG(FLUID_ERR, "fluid_rvoice_event_dispatch: Unknown method %p to dispatch!", event->method);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* In order to be able to push more than one event atomically,
|
||
|
* use push for all events, then use flush to commit them to the
|
||
|
* queue. If threadsafe is false, all events are processed immediately. */
|
||
|
int
|
||
|
fluid_rvoice_eventhandler_push(fluid_rvoice_eventhandler_t* handler,
|
||
|
void* method, void* object, int intparam,
|
||
|
fluid_real_t realparam)
|
||
|
{
|
||
|
fluid_rvoice_event_t* event;
|
||
|
fluid_rvoice_event_t local_event;
|
||
|
event = handler->is_threadsafe ?
|
||
|
fluid_ringbuffer_get_inptr(handler->queue, handler->queue_stored) : &local_event;
|
||
|
|
||
|
if (event == NULL) {
|
||
|
FLUID_LOG(FLUID_WARN, "Ringbuffer full, try increasing polyphony!");
|
||
|
return FLUID_FAILED; // Buffer full...
|
||
|
}
|
||
|
|
||
|
event->method = method;
|
||
|
event->object = object;
|
||
|
event->intparam = intparam;
|
||
|
event->realparams[0] = realparam;
|
||
|
if (handler->is_threadsafe)
|
||
|
handler->queue_stored++;
|
||
|
else
|
||
|
fluid_rvoice_event_dispatch(event);
|
||
|
return FLUID_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
int
|
||
|
fluid_rvoice_eventhandler_push_ptr(fluid_rvoice_eventhandler_t* handler,
|
||
|
void* method, void* object, void* ptr)
|
||
|
{
|
||
|
fluid_rvoice_event_t* event;
|
||
|
fluid_rvoice_event_t local_event;
|
||
|
event = handler->is_threadsafe ?
|
||
|
fluid_ringbuffer_get_inptr(handler->queue, handler->queue_stored) : &local_event;
|
||
|
|
||
|
if (event == NULL) {
|
||
|
FLUID_LOG(FLUID_WARN, "Ringbuffer full, try increasing polyphony!");
|
||
|
return FLUID_FAILED; // Buffer full...
|
||
|
}
|
||
|
|
||
|
event->method = method;
|
||
|
event->object = object;
|
||
|
event->ptr = ptr;
|
||
|
if (handler->is_threadsafe)
|
||
|
handler->queue_stored++;
|
||
|
else
|
||
|
fluid_rvoice_event_dispatch(event);
|
||
|
return FLUID_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
int
|
||
|
fluid_rvoice_eventhandler_push5(fluid_rvoice_eventhandler_t* handler,
|
||
|
void* method, void* object, int intparam,
|
||
|
fluid_real_t r1, fluid_real_t r2,
|
||
|
fluid_real_t r3, fluid_real_t r4, fluid_real_t r5)
|
||
|
{
|
||
|
fluid_rvoice_event_t* event;
|
||
|
fluid_rvoice_event_t local_event;
|
||
|
event = handler->is_threadsafe ?
|
||
|
fluid_ringbuffer_get_inptr(handler->queue, handler->queue_stored) : &local_event;
|
||
|
|
||
|
if (event == NULL) {
|
||
|
FLUID_LOG(FLUID_WARN, "Ringbuffer full, try increasing polyphony!");
|
||
|
return FLUID_FAILED; // Buffer full...
|
||
|
}
|
||
|
|
||
|
event->method = method;
|
||
|
event->object = object;
|
||
|
event->intparam = intparam;
|
||
|
event->realparams[0] = r1;
|
||
|
event->realparams[1] = r2;
|
||
|
event->realparams[2] = r3;
|
||
|
event->realparams[3] = r4;
|
||
|
event->realparams[4] = r5;
|
||
|
if (handler->is_threadsafe)
|
||
|
handler->queue_stored++;
|
||
|
else
|
||
|
fluid_rvoice_event_dispatch(event);
|
||
|
return FLUID_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void
|
||
|
finished_voice_callback(void* userdata, fluid_rvoice_t* rvoice)
|
||
|
{
|
||
|
fluid_rvoice_eventhandler_t* eventhandler = userdata;
|
||
|
fluid_rvoice_t** vptr = fluid_ringbuffer_get_inptr(eventhandler->finished_voices, 0);
|
||
|
if (vptr == NULL)
|
||
|
return; // Buffer full
|
||
|
*vptr = rvoice;
|
||
|
fluid_ringbuffer_next_inptr(eventhandler->finished_voices, 1);
|
||
|
}
|
||
|
|
||
|
fluid_rvoice_eventhandler_t*
|
||
|
new_fluid_rvoice_eventhandler(int is_threadsafe, int queuesize,
|
||
|
int finished_voices_size, int bufs, int fx_bufs, fluid_real_t sample_rate)
|
||
|
{
|
||
|
fluid_rvoice_eventhandler_t* eventhandler = FLUID_NEW(fluid_rvoice_eventhandler_t);
|
||
|
if (eventhandler == NULL) {
|
||
|
FLUID_LOG(FLUID_ERR, "Out of memory");
|
||
|
return NULL;
|
||
|
}
|
||
|
eventhandler->mixer = NULL;
|
||
|
eventhandler->queue = NULL;
|
||
|
eventhandler->finished_voices = NULL;
|
||
|
eventhandler->is_threadsafe = is_threadsafe;
|
||
|
eventhandler->queue_stored = 0;
|
||
|
|
||
|
eventhandler->finished_voices = new_fluid_ringbuffer(finished_voices_size,
|
||
|
sizeof(fluid_rvoice_t*));
|
||
|
if (eventhandler->finished_voices == NULL)
|
||
|
goto error_recovery;
|
||
|
|
||
|
eventhandler->queue = new_fluid_ringbuffer(queuesize, sizeof(fluid_rvoice_event_t));
|
||
|
if (eventhandler->queue == NULL)
|
||
|
goto error_recovery;
|
||
|
|
||
|
eventhandler->mixer = new_fluid_rvoice_mixer(bufs, fx_bufs, sample_rate);
|
||
|
if (eventhandler->mixer == NULL)
|
||
|
goto error_recovery;
|
||
|
fluid_rvoice_mixer_set_finished_voices_callback(eventhandler->mixer,
|
||
|
finished_voice_callback, eventhandler);
|
||
|
return eventhandler;
|
||
|
|
||
|
error_recovery:
|
||
|
delete_fluid_rvoice_eventhandler(eventhandler);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
fluid_rvoice_eventhandler_dispatch_count(fluid_rvoice_eventhandler_t* handler)
|
||
|
{
|
||
|
return fluid_ringbuffer_get_count(handler->queue);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Call fluid_rvoice_event_dispatch for all events in queue
|
||
|
* @return number of events dispatched
|
||
|
*/
|
||
|
int
|
||
|
fluid_rvoice_eventhandler_dispatch_all(fluid_rvoice_eventhandler_t* handler)
|
||
|
{
|
||
|
fluid_rvoice_event_t* event;
|
||
|
int result = 0;
|
||
|
while (NULL != (event = fluid_ringbuffer_get_outptr(handler->queue))) {
|
||
|
fluid_rvoice_event_dispatch(event);
|
||
|
result++;
|
||
|
fluid_ringbuffer_next_outptr(handler->queue);
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
delete_fluid_rvoice_eventhandler(fluid_rvoice_eventhandler_t* handler)
|
||
|
{
|
||
|
if (handler == NULL) return;
|
||
|
delete_fluid_rvoice_mixer(handler->mixer);
|
||
|
delete_fluid_ringbuffer(handler->queue);
|
||
|
delete_fluid_ringbuffer(handler->finished_voices);
|
||
|
FLUID_FREE(handler);
|
||
|
}
|