mirror of
https://github.com/ZDoom/fluidsynth.git
synced 2024-11-10 23:11:41 +00:00
Event queue for thread boundary, refactor ringbuffer, move some files to unused subdir
This commit is contained in:
parent
d5ab7a8f74
commit
b79a3e1cbe
19 changed files with 1173 additions and 259 deletions
|
@ -122,7 +122,6 @@ set ( libfluidsynth_SOURCES
|
|||
sfloader/fluid_sfont.h
|
||||
fluid_event.c
|
||||
fluid_event_priv.h
|
||||
fluid_event_queue.c
|
||||
fluid_event_queue.h
|
||||
fluid_gen.c
|
||||
fluid_gen.h
|
||||
|
@ -130,13 +129,20 @@ set ( libfluidsynth_SOURCES
|
|||
datatypes/fluid_hash.h
|
||||
datatypes/fluid_list.c
|
||||
datatypes/fluid_list.h
|
||||
datatypes/fluid_ringbuffer.c
|
||||
datatypes/fluid_ringbuffer.h
|
||||
rvoice/fluid_iir_filter.c
|
||||
rvoice/fluid_iir_filter.h
|
||||
rvoice/fluid_lfo.h
|
||||
rvoice/fluid_adsr_env.c
|
||||
rvoice/fluid_adsr_env.h
|
||||
rvoice/fluid_rvoice.h
|
||||
rvoice/fluid_rvoice.c
|
||||
rvoice/fluid_rvoice_dsp.c
|
||||
rvoice/fluid_rvoice_event.h
|
||||
rvoice/fluid_rvoice_event.c
|
||||
rvoice/fluid_rvoice_handler.h
|
||||
rvoice/fluid_rvoice_handler.c
|
||||
rvoice/fluid_phase.h
|
||||
fluid_mdriver.c
|
||||
fluid_mdriver.h
|
||||
|
|
89
fluidsynth/src/datatypes/fluid_ringbuffer.c
Normal file
89
fluidsynth/src/datatypes/fluid_ringbuffer.c
Normal file
|
@ -0,0 +1,89 @@
|
|||
/* 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
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307, USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Josh Green <josh@resonance.org>
|
||||
* 2009-05-28
|
||||
*/
|
||||
|
||||
#include "fluid_ringbuffer.h"
|
||||
#include "fluidsynth_priv.h"
|
||||
|
||||
|
||||
/**
|
||||
* Create a lock free queue with a fixed maximum count and size of elements.
|
||||
* @param count Count of elements in queue (fixed max number of queued elements)
|
||||
* @return New lock free queue or NULL if out of memory (error message logged)
|
||||
*
|
||||
* Lockless FIFO queues don't use any locking mechanisms and can therefore be
|
||||
* advantageous in certain situations, such as passing data between a lower
|
||||
* priority thread and a higher "real time" thread, without potential lock
|
||||
* contention which could stall the high priority thread. Note that there may
|
||||
* only be one producer thread and one consumer thread.
|
||||
*/
|
||||
fluid_ringbuffer_t *
|
||||
new_fluid_ringbuffer (int count, int elementsize)
|
||||
{
|
||||
fluid_ringbuffer_t *queue;
|
||||
|
||||
fluid_return_val_if_fail (count > 0, NULL);
|
||||
|
||||
queue = FLUID_NEW (fluid_ringbuffer_t);
|
||||
|
||||
if (!queue)
|
||||
{
|
||||
FLUID_LOG (FLUID_ERR, "Out of memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
queue->array = FLUID_MALLOC (elementsize * count);
|
||||
|
||||
if (!queue->array)
|
||||
{
|
||||
FLUID_FREE (queue);
|
||||
FLUID_LOG (FLUID_ERR, "Out of memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Clear array, in case dynamic pointer reclaiming is being done */
|
||||
FLUID_MEMSET (queue->array, 0, elementsize * count);
|
||||
|
||||
queue->totalcount = count;
|
||||
queue->elementsize = elementsize;
|
||||
queue->count = 0;
|
||||
queue->in = 0;
|
||||
queue->out = 0;
|
||||
|
||||
return (queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Free an event queue.
|
||||
* @param queue Lockless queue instance
|
||||
*
|
||||
* Care must be taken when freeing a queue, to ensure that the consumer and
|
||||
* producer threads will no longer access it.
|
||||
*/
|
||||
void
|
||||
delete_fluid_ringbuffer (fluid_ringbuffer_t *queue)
|
||||
{
|
||||
FLUID_FREE (queue->array);
|
||||
FLUID_FREE (queue);
|
||||
}
|
115
fluidsynth/src/datatypes/fluid_ringbuffer.h
Normal file
115
fluidsynth/src/datatypes/fluid_ringbuffer.h
Normal file
|
@ -0,0 +1,115 @@
|
|||
/* 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
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307, USA
|
||||
*/
|
||||
|
||||
#ifndef _FLUID_RINGBUFFER_H
|
||||
#define _FLUID_RINGBUFFER_H
|
||||
|
||||
#include "fluid_sys.h"
|
||||
|
||||
/**
|
||||
* Lockless event queue instance.
|
||||
*/
|
||||
struct _fluid_ringbuffer_t
|
||||
{
|
||||
char *array; /**< Queue array of arbitrary size elements */
|
||||
int totalcount; /**< Total count of elements in array */
|
||||
int count; /**< Current count of elements */
|
||||
int in; /**< Index in queue to store next pushed element */
|
||||
int out; /**< Index in queue of next popped element */
|
||||
int elementsize; /**< Size of each element */
|
||||
void* userdata;
|
||||
};
|
||||
|
||||
typedef struct _fluid_ringbuffer_t fluid_ringbuffer_t;
|
||||
|
||||
|
||||
fluid_ringbuffer_t *new_fluid_ringbuffer (int count, int elementsize);
|
||||
void delete_fluid_ringbuffer (fluid_ringbuffer_t *queue);
|
||||
|
||||
/**
|
||||
* Get pointer to next input array element in queue.
|
||||
* @param queue Lockless queue instance
|
||||
* @param count Normally zero, or more if you need to push several items at once
|
||||
* @return Pointer to array element in queue to store data to or NULL if queue is full
|
||||
*
|
||||
* This function along with fluid_ringbuffer_next_inptr() form a queue "push"
|
||||
* operation and is split into 2 functions to avoid an element copy. Note that
|
||||
* the returned array element pointer may contain the data of a previous element
|
||||
* if the queue has wrapped around. This can be used to reclaim pointers to
|
||||
* allocated memory, etc.
|
||||
*/
|
||||
static FLUID_INLINE void*
|
||||
fluid_ringbuffer_get_inptr (fluid_ringbuffer_t *queue, int offset)
|
||||
{
|
||||
return fluid_atomic_int_get (&queue->count) + offset >= queue->totalcount ? NULL
|
||||
: queue->array + queue->elementsize * ((queue->in + offset) % queue->totalcount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Advance the input queue index to complete a "push" operation.
|
||||
* @param queue Lockless queue instance
|
||||
* @param count Normally one, or more if you need to push several items at once
|
||||
*
|
||||
* This function along with fluid_ringbuffer_get_inptr() form a queue "push"
|
||||
* operation and is split into 2 functions to avoid element copy.
|
||||
*/
|
||||
static FLUID_INLINE void
|
||||
fluid_ringbuffer_next_inptr (fluid_ringbuffer_t *queue, int count)
|
||||
{
|
||||
fluid_atomic_int_add (&queue->count, count);
|
||||
|
||||
queue->in += count;
|
||||
if (queue->in >= queue->totalcount)
|
||||
queue->in -= queue->totalcount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pointer to next output array element in queue.
|
||||
* @param queue Lockless queue instance
|
||||
* @return Pointer to array element data in the queue or NULL if empty, can only
|
||||
* be used up until fluid_ringbuffer_next_outptr() is called.
|
||||
*
|
||||
* This function along with fluid_ringbuffer_next_outptr() form a queue "pop"
|
||||
* operation and is split into 2 functions to avoid an element copy.
|
||||
*/
|
||||
static FLUID_INLINE void*
|
||||
fluid_ringbuffer_get_outptr (fluid_ringbuffer_t *queue)
|
||||
{
|
||||
return fluid_atomic_int_get (&queue->count) == 0 ? NULL
|
||||
: queue->array + queue->elementsize * queue->out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Advance the output queue index to complete a "pop" operation.
|
||||
* @param queue Lockless queue instance
|
||||
*
|
||||
* This function along with fluid_ringbuffer_get_outptr() form a queue "pop"
|
||||
* operation and is split into 2 functions to avoid an element copy.
|
||||
*/
|
||||
static FLUID_INLINE void
|
||||
fluid_ringbuffer_next_outptr (fluid_ringbuffer_t *queue)
|
||||
{
|
||||
fluid_atomic_int_add (&queue->count, -1);
|
||||
|
||||
if (++queue->out == queue->totalcount)
|
||||
queue->out = 0;
|
||||
}
|
||||
|
||||
#endif /* _FLUID_ringbuffer_H */
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include "fluid_sys.h"
|
||||
#include "fluid_midi.h"
|
||||
#include "fluid_ringbuffer.h"
|
||||
|
||||
/**
|
||||
* Type of queued event.
|
||||
|
@ -120,22 +121,18 @@ typedef struct
|
|||
};
|
||||
} fluid_event_queue_elem_t;
|
||||
|
||||
/**
|
||||
* Lockless event queue instance.
|
||||
*/
|
||||
typedef struct
|
||||
typedef struct _fluid_ringbuffer_t fluid_event_queue_t;
|
||||
|
||||
static FLUID_INLINE fluid_event_queue_t *
|
||||
fluid_event_queue_new (int count)
|
||||
{
|
||||
fluid_event_queue_elem_t *array; /**< Queue array of arbitrary size elements */
|
||||
int totalcount; /**< Total count of elements in array */
|
||||
int count; /**< Current count of elements */
|
||||
int in; /**< Index in queue to store next pushed element */
|
||||
int out; /**< Index in queue of next popped element */
|
||||
void *synth; /**< Owning fluid_synth_t instance */
|
||||
} fluid_event_queue_t;
|
||||
return (fluid_event_queue_t *) new_fluid_ringbuffer(count, sizeof(fluid_event_queue_elem_t));
|
||||
}
|
||||
|
||||
|
||||
fluid_event_queue_t *fluid_event_queue_new (int count);
|
||||
void fluid_event_queue_free (fluid_event_queue_t *queue);
|
||||
static FLUID_INLINE void fluid_event_queue_free (fluid_event_queue_t *queue)
|
||||
{
|
||||
delete_fluid_ringbuffer(queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pointer to next input array element in queue.
|
||||
|
@ -151,8 +148,7 @@ void fluid_event_queue_free (fluid_event_queue_t *queue);
|
|||
static FLUID_INLINE fluid_event_queue_elem_t *
|
||||
fluid_event_queue_get_inptr (fluid_event_queue_t *queue)
|
||||
{
|
||||
return fluid_atomic_int_get (&queue->count) == queue->totalcount ? NULL
|
||||
: queue->array + queue->in;
|
||||
return (fluid_event_queue_elem_t *) fluid_ringbuffer_get_inptr(queue, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -165,10 +161,7 @@ fluid_event_queue_get_inptr (fluid_event_queue_t *queue)
|
|||
static FLUID_INLINE void
|
||||
fluid_event_queue_next_inptr (fluid_event_queue_t *queue)
|
||||
{
|
||||
fluid_atomic_int_inc (&queue->count);
|
||||
|
||||
if (++queue->in == queue->totalcount)
|
||||
queue->in = 0;
|
||||
fluid_ringbuffer_next_inptr(queue, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -183,8 +176,7 @@ fluid_event_queue_next_inptr (fluid_event_queue_t *queue)
|
|||
static FLUID_INLINE fluid_event_queue_elem_t *
|
||||
fluid_event_queue_get_outptr (fluid_event_queue_t *queue)
|
||||
{
|
||||
return fluid_atomic_int_get (&queue->count) == 0 ? NULL
|
||||
: queue->array + queue->out;
|
||||
return (fluid_event_queue_elem_t *) fluid_ringbuffer_get_outptr(queue);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -197,10 +189,7 @@ fluid_event_queue_get_outptr (fluid_event_queue_t *queue)
|
|||
static FLUID_INLINE void
|
||||
fluid_event_queue_next_outptr (fluid_event_queue_t *queue)
|
||||
{
|
||||
fluid_atomic_int_add (&queue->count, -1);
|
||||
|
||||
if (++queue->out == queue->totalcount)
|
||||
queue->out = 0;
|
||||
fluid_ringbuffer_next_outptr(queue);
|
||||
}
|
||||
|
||||
#endif /* _FLUID_EVENT_QUEUE_H */
|
||||
|
|
|
@ -647,6 +647,12 @@ new_fluid_synth(fluid_settings_t *settings)
|
|||
}
|
||||
}
|
||||
|
||||
/* Allocate handler */
|
||||
synth->eventhandler = new_fluid_rvoice_eventhandler(1, synth->polyphony*8, synth->polyphony);
|
||||
if (synth->eventhandler == NULL)
|
||||
goto error_recovery;
|
||||
fluid_rvoice_handler_set_polyphony(synth->eventhandler->handler, synth->polyphony);
|
||||
|
||||
/* Allocate the sample buffers */
|
||||
synth->left_buf = NULL;
|
||||
synth->right_buf = NULL;
|
||||
|
@ -901,6 +907,9 @@ delete_fluid_synth(fluid_synth_t* synth)
|
|||
}
|
||||
}
|
||||
|
||||
if (synth->eventhandler)
|
||||
delete_fluid_rvoice_eventhandler(synth->eventhandler);
|
||||
|
||||
/* delete all the SoundFonts */
|
||||
for (list = synth->sfont_info; list; list = fluid_list_next (list)) {
|
||||
sfont_info = (fluid_sfont_info_t *)fluid_list_get (list);
|
||||
|
@ -1085,7 +1094,7 @@ fluid_synth_get_event_queue (fluid_synth_t* synth)
|
|||
queue = fluid_event_queue_new (FLUID_MAX_EVENTS_PER_BUFSIZE);
|
||||
if (!queue) return NULL; /* Error has already been logged */
|
||||
|
||||
queue->synth = synth;
|
||||
queue->userdata = synth;
|
||||
|
||||
/* Atomicly and in a lock free fashion, put queue pointer in queues[] array */
|
||||
for (i = 0; i < FLUID_MAX_EVENT_QUEUES; i++)
|
||||
|
@ -1223,7 +1232,7 @@ static void
|
|||
fluid_synth_thread_queue_destroy_notify (void *data)
|
||||
{
|
||||
fluid_event_queue_t *queue = data;
|
||||
fluid_synth_t *synth = queue->synth;
|
||||
fluid_synth_t *synth = queue->userdata;
|
||||
|
||||
/* Queues are not freed (can't be thread safe without locking in synth thread),
|
||||
* added to pool for potential future use */
|
||||
|
@ -2582,6 +2591,10 @@ fluid_synth_update_polyphony_LOCAL(fluid_synth_t* synth)
|
|||
if (_PLAYING (voice)) fluid_voice_off (voice);
|
||||
}
|
||||
|
||||
fluid_rvoice_eventhandler_push(synth->eventhandler,
|
||||
fluid_rvoice_handler_set_polyphony, synth->eventhandler->handler,
|
||||
synth->polyphony, 0.0f);
|
||||
|
||||
return FLUID_OK;
|
||||
}
|
||||
|
||||
|
@ -2968,22 +2981,38 @@ fluid_synth_dither_s16(int *dither_index, int len, float* lin, float* rin,
|
|||
fluid_profile(FLUID_PROF_WRITE_S16, prof_ref);
|
||||
}
|
||||
|
||||
static void
|
||||
fluid_synth_check_finished_voices(fluid_synth_t* synth)
|
||||
{
|
||||
int j;
|
||||
fluid_rvoice_t* fv;
|
||||
|
||||
while (NULL != (fv = fluid_rvoice_eventhandler_get_finished_voice(synth->eventhandler))) {
|
||||
for (j=0; j < synth->polyphony; j++)
|
||||
if (synth->voice[j]->rvoice == fv) {
|
||||
fluid_voice_unlock_rvoice(synth->voice[j]);
|
||||
fluid_voice_off(synth->voice[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Process a single block (FLUID_BUFSIZE) of audio.
|
||||
*/
|
||||
static int
|
||||
fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out)
|
||||
{
|
||||
fluid_real_t local_voice_buf[FLUID_BUFSIZE];
|
||||
int i, auchan;
|
||||
// int start_index, voice_index;
|
||||
fluid_voice_t* voice;
|
||||
fluid_real_t* left_buf;
|
||||
fluid_real_t* right_buf;
|
||||
// fluid_real_t local_voice_buf[FLUID_BUFSIZE];
|
||||
int i;
|
||||
// int start_index, voice_index, auchan;
|
||||
// fluid_voice_t* voice;
|
||||
// fluid_real_t* left_buf;
|
||||
// fluid_real_t* right_buf;
|
||||
fluid_real_t* reverb_buf;
|
||||
fluid_real_t* chorus_buf;
|
||||
int byte_size = FLUID_BUFSIZE * sizeof(fluid_real_t);
|
||||
int count;
|
||||
fluid_real_t* bufs[synth->audio_groups*2 + synth->effects_channels*2];
|
||||
// int byte_size = FLUID_BUFSIZE * sizeof(fluid_real_t);
|
||||
// int count;
|
||||
fluid_profile_ref_var (prof_ref);
|
||||
|
||||
/* Assign ID of synthesis thread */
|
||||
|
@ -3003,16 +3032,6 @@ fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out)
|
|||
else break; /* First NULL ends the array (values are never set to NULL) */
|
||||
}
|
||||
|
||||
/* clean the audio buffers */
|
||||
for (i = 0; i < synth->nbuf; i++) {
|
||||
FLUID_MEMSET(synth->left_buf[i], 0, byte_size);
|
||||
FLUID_MEMSET(synth->right_buf[i], 0, byte_size);
|
||||
}
|
||||
|
||||
for (i = 0; i < synth->effects_channels; i++) {
|
||||
FLUID_MEMSET(synth->fx_left_buf[i], 0, byte_size);
|
||||
FLUID_MEMSET(synth->fx_right_buf[i], 0, byte_size);
|
||||
}
|
||||
|
||||
/* Set up the reverb / chorus buffers only, when the effect is
|
||||
* enabled on synth level. Nonexisting buffers are detected in the
|
||||
|
@ -3020,104 +3039,8 @@ fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out)
|
|||
* in that case. */
|
||||
reverb_buf = synth->with_reverb ? synth->fx_left_buf[SYNTH_REVERB_CHANNEL] : NULL;
|
||||
chorus_buf = synth->with_chorus ? synth->fx_left_buf[SYNTH_CHORUS_CHANNEL] : NULL;
|
||||
|
||||
fluid_profile(FLUID_PROF_ONE_BLOCK_CLEAR, prof_ref);
|
||||
|
||||
#if 0
|
||||
if (synth->cores > 1)
|
||||
{
|
||||
/* Look for first active voice to process */
|
||||
for (voice_index = 0; voice_index < synth->polyphony; voice_index++)
|
||||
{
|
||||
voice = synth->voice[voice_index];
|
||||
if (_PLAYING (voice)) break;
|
||||
}
|
||||
|
||||
/* Was there a voice to process? */
|
||||
if (voice_index < synth->polyphony)
|
||||
{
|
||||
fluid_cond_mutex_lock (synth->core_mutex); /* ++ Lock core variables */
|
||||
|
||||
synth->core_voice_index = voice_index + 1; /* Set the next core_voice_index */
|
||||
|
||||
/* Tell the other core threads that there is work to do */
|
||||
synth->core_work = TRUE;
|
||||
synth->core_waiting_for_last = FALSE;
|
||||
fluid_cond_broadcast (synth->core_cond);
|
||||
|
||||
fluid_cond_mutex_unlock (synth->core_mutex); /* -- Unlock core variables */
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
got_voice: /* We got a voice to process */
|
||||
count = fluid_voice_write (voice, &synth->core_bufs[voice_index * FLUID_BUFSIZE]);
|
||||
|
||||
if (count > 0) synth->core_voice_processed[voice_index] = voice;
|
||||
|
||||
/* Look for next active voice to process (in a lock free manner) */
|
||||
do
|
||||
{
|
||||
voice_index = fluid_atomic_int_get (&synth->core_voice_index);
|
||||
|
||||
for (start_index = voice_index; voice_index < synth->polyphony; voice_index++)
|
||||
{
|
||||
voice = synth->voice[voice_index];
|
||||
|
||||
if (_PLAYING (voice))
|
||||
{
|
||||
if (fluid_atomic_int_compare_and_exchange (&synth->core_voice_index,
|
||||
start_index, voice_index + 1))
|
||||
goto got_voice;
|
||||
|
||||
break; /* compare and exchange failed (another thread grabbed the voice first) */
|
||||
}
|
||||
}
|
||||
}
|
||||
while (voice_index < synth->polyphony);
|
||||
|
||||
/* No more voices to process */
|
||||
fluid_cond_mutex_lock (synth->core_mutex); /* ++ Lock core variables */
|
||||
synth->core_work = FALSE;
|
||||
|
||||
/* If there are still other core threads in progress, wait for last one */
|
||||
if (synth->core_inprogress > 0)
|
||||
{
|
||||
synth->core_waiting_for_last = TRUE;
|
||||
|
||||
while (synth->core_inprogress > 0)
|
||||
fluid_cond_wait (synth->core_wait_last_cond, synth->core_mutex);
|
||||
}
|
||||
|
||||
fluid_cond_mutex_unlock (synth->core_mutex); /* -- Unlock core variables */
|
||||
break; /* We're done */
|
||||
} /* while (TRUE) - Process voices loop */
|
||||
|
||||
/* Mix all voices */
|
||||
for (i = 0; i < synth->polyphony; i++)
|
||||
{
|
||||
voice = synth->core_voice_processed[i];
|
||||
if (!voice) continue;
|
||||
|
||||
synth->core_voice_processed[i] = NULL;
|
||||
|
||||
auchan = fluid_channel_get_num (fluid_voice_get_channel (voice));
|
||||
auchan %= synth->audio_groups;
|
||||
left_buf = synth->left_buf[auchan];
|
||||
right_buf = synth->right_buf[auchan];
|
||||
|
||||
fluid_voice_mix (voice, left_buf, right_buf, reverb_buf, chorus_buf);
|
||||
} /* while (TRUE) - Mix processed voices loop */
|
||||
} /* if (i < synth->polyphony) - Are there any voices to process? */
|
||||
}
|
||||
else /* synth->cores < 1 - Not multi-core enabled */
|
||||
{
|
||||
#endif
|
||||
/* call all playing synthesis processes */
|
||||
for (i = 0; i < synth->polyphony; i++) {
|
||||
fluid_profile_ref_var (prof_ref_voice);
|
||||
|
||||
voice = synth->voice[i];
|
||||
if (!_PLAYING(voice)) continue;
|
||||
bufs[synth->audio_groups*2 + SYNTH_REVERB_CHANNEL] = reverb_buf;
|
||||
bufs[synth->audio_groups*2 + SYNTH_CHORUS_CHANNEL] = chorus_buf;
|
||||
|
||||
/* The output associated with a MIDI channel is wrapped around
|
||||
* using the number of audio groups as modulo divider. This is
|
||||
|
@ -3131,21 +3054,14 @@ got_voice: /* We got a voice to process */
|
|||
* channels 1, 4, 7, 10 etc go to output 1; 2, 5, 8, 11 etc to
|
||||
* output 2, 3, 6, 9, 12 etc to output 3.
|
||||
*/
|
||||
auchan = fluid_channel_get_num(fluid_voice_get_channel(voice));
|
||||
auchan %= synth->audio_groups;
|
||||
left_buf = synth->left_buf[auchan];
|
||||
right_buf = synth->right_buf[auchan];
|
||||
|
||||
count = fluid_voice_write (voice, local_voice_buf);
|
||||
if (count > 0)
|
||||
fluid_voice_mix (voice, count, local_voice_buf, left_buf, right_buf,
|
||||
reverb_buf, chorus_buf);
|
||||
for (i = 0; i < synth->audio_groups; i++) {
|
||||
bufs[i*2] = synth->left_buf[i];
|
||||
bufs[i*2+1] = synth->right_buf[i];
|
||||
}
|
||||
|
||||
fluid_profile (FLUID_PROF_ONE_BLOCK_VOICE, prof_ref_voice);
|
||||
}
|
||||
#if 0
|
||||
}
|
||||
#endif
|
||||
fluid_rvoice_eventhandler_dispatch_all(synth->eventhandler);
|
||||
fluid_rvoice_handler_render(synth->eventhandler->handler, 1, synth->audio_groups*2 + 2, bufs);
|
||||
|
||||
fluid_check_fpe("Synthesis processes");
|
||||
|
||||
|
@ -3208,6 +3124,8 @@ got_voice: /* We got a voice to process */
|
|||
|
||||
synth->ticks += FLUID_BUFSIZE;
|
||||
|
||||
fluid_synth_check_finished_voices(synth);
|
||||
|
||||
/* Testcase, that provokes a denormal floating point error */
|
||||
#if 0
|
||||
{float num=1;while (num != 0){num*=0.5;};};
|
||||
|
@ -3448,6 +3366,7 @@ fluid_synth_free_voice_by_kill_LOCAL(fluid_synth_t* synth)
|
|||
return voice;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allocate a synthesis voice.
|
||||
* @param synth FluidSynth instance
|
||||
|
@ -3476,6 +3395,8 @@ fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, int chan,
|
|||
fluid_return_val_if_fail (chan >= 0 && chan < synth->midi_channels, NULL);
|
||||
fluid_return_val_if_fail (fluid_synth_is_synth_thread (synth), NULL);
|
||||
|
||||
fluid_synth_check_finished_voices(synth);
|
||||
|
||||
/* check if there's an available synthesis process */
|
||||
for (i = 0; i < synth->polyphony; i++) {
|
||||
if (_AVAILABLE(synth->voice[i])) {
|
||||
|
@ -3486,6 +3407,7 @@ fluid_synth_alloc_voice(fluid_synth_t* synth, fluid_sample_t* sample, int chan,
|
|||
|
||||
/* No success yet? Then stop a running voice. */
|
||||
if (voice == NULL) {
|
||||
FLUID_LOG(FLUID_DBG, "Polyphony exceeded, trying to kill a voice");
|
||||
voice = fluid_synth_free_voice_by_kill_LOCAL(synth);
|
||||
}
|
||||
|
||||
|
@ -3589,6 +3511,11 @@ fluid_synth_start_voice(fluid_synth_t* synth, fluid_voice_t* voice)
|
|||
fluid_synth_kill_by_exclusive_class_LOCAL(synth, voice);
|
||||
|
||||
fluid_voice_start(voice); /* Start the new voice */
|
||||
if (synth->eventhandler->is_threadsafe)
|
||||
fluid_voice_lock_rvoice(voice);
|
||||
fluid_rvoice_eventhandler_add_rvoice(synth->eventhandler, voice->rvoice);
|
||||
/* TODO: Several voices in sync ? */
|
||||
fluid_rvoice_eventhandler_flush(synth->eventhandler);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -40,6 +40,8 @@
|
|||
#include "fluid_ladspa.h"
|
||||
#include "fluid_midi_router.h"
|
||||
#include "fluid_sys.h"
|
||||
#include "fluid_rvoice_handler.h"
|
||||
#include "fluid_rvoice_event.h"
|
||||
|
||||
/***************************************************************
|
||||
*
|
||||
|
@ -195,6 +197,7 @@ struct _fluid_synth_t
|
|||
unsigned int noteid; /**< the id is incremented for every new note. it's used for noteoff's */
|
||||
unsigned int storeid;
|
||||
int nbuf; /**< How many audio buffers are used? (depends on nr of audio channels / groups)*/
|
||||
fluid_rvoice_eventhandler_t* eventhandler;
|
||||
|
||||
fluid_real_t** left_buf;
|
||||
fluid_real_t** right_buf;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "fluid_synth.h"
|
||||
#include "fluid_sys.h"
|
||||
#include "fluid_sfont.h"
|
||||
#include "fluid_rvoice_event.h"
|
||||
|
||||
/* used for filter turn off optimization - if filter cutoff is above the
|
||||
specified value and filter q is below the other value, turn filter off */
|
||||
|
@ -41,13 +42,54 @@ static int calculate_hold_decay_buffers(fluid_voice_t* voice, int gen_base,
|
|||
static fluid_real_t
|
||||
fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t* voice);
|
||||
|
||||
#define UPDATE_RVOICE0(proc) proc(voice->rvoice)
|
||||
#define UPDATE_RVOICE1(proc, arg1) proc(voice->rvoice, arg1)
|
||||
#define UPDATE_RVOICE2(proc, arg1, arg2) proc(voice->rvoice, arg1, arg2)
|
||||
#define UPDATE_RVOICE_ENVLFO1(proc, envp, arg1) proc(&voice->rvoice->envlfo.envp, arg1)
|
||||
#define UPDATE_RVOICE0(proc) \
|
||||
do { \
|
||||
if (voice->can_access_rvoice) proc(voice->rvoice); \
|
||||
else fluid_rvoice_eventhandler_push(voice->channel->synth->eventhandler, \
|
||||
proc, voice->rvoice, 0, 0.0f); \
|
||||
} while (0)
|
||||
|
||||
#define UPDATE_RVOICE_GENERIC_R1(proc, obj, rarg) \
|
||||
do { \
|
||||
if (voice->can_access_rvoice) proc(obj, rarg); \
|
||||
else fluid_rvoice_eventhandler_push(voice->channel->synth->eventhandler, \
|
||||
proc, obj, 0, rarg); \
|
||||
} while (0)
|
||||
|
||||
#define UPDATE_RVOICE_GENERIC_I1(proc, obj, iarg) \
|
||||
do { \
|
||||
if (voice->can_access_rvoice) proc(obj, iarg); \
|
||||
else fluid_rvoice_eventhandler_push(voice->channel->synth->eventhandler, \
|
||||
proc, obj, iarg, 0.0f); \
|
||||
} while (0)
|
||||
|
||||
#define UPDATE_RVOICE_GENERIC_IR(proc, obj, iarg, rarg) \
|
||||
do { \
|
||||
if (voice->can_access_rvoice) proc(obj, iarg, rarg); \
|
||||
else fluid_rvoice_eventhandler_push(voice->channel->synth->eventhandler, \
|
||||
proc, obj, iarg, rarg); \
|
||||
} while (0)
|
||||
|
||||
#define UPDATE_RVOICE_GENERIC_ALL(proc, obj, iarg, r1, r2, r3, r4, r5) \
|
||||
do { \
|
||||
if (voice->can_access_rvoice) proc(obj, iarg, r1, r2, r3, r4, r5); \
|
||||
else fluid_rvoice_eventhandler_push5(voice->channel->synth->eventhandler, \
|
||||
proc, obj, iarg, r1, r2, r3, r4, r5); \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define UPDATE_RVOICE_ENVDATA(envp, section, arg1, arg2, arg3, arg4, arg5) \
|
||||
fluid_adsr_env_set_data(&voice->rvoice->envlfo.envp, section, arg1, arg2, arg3, arg4, arg5)
|
||||
#define UPDATE_RVOICE_FILTER1(proc, arg1) proc(&voice->rvoice->resonant_filter, arg1)
|
||||
UPDATE_RVOICE_GENERIC_ALL(fluid_adsr_env_set_data, &voice->rvoice->envlfo.envp, section, arg1, arg2, arg3, arg4, arg5)
|
||||
|
||||
#define UPDATE_RVOICE_R1(proc, arg1) UPDATE_RVOICE_GENERIC_R1(proc, voice->rvoice, arg1)
|
||||
#define UPDATE_RVOICE_I1(proc, arg1) UPDATE_RVOICE_GENERIC_I1(proc, voice->rvoice, arg1)
|
||||
#define UPDATE_RVOICE_FILTER1(proc, arg1) UPDATE_RVOICE_GENERIC_R1(proc, &voice->rvoice->resonant_filter, arg1)
|
||||
|
||||
#define UPDATE_RVOICE2(proc, iarg, rarg) UPDATE_RVOICE_GENERIC_IR(proc, voice->rvoice, iarg, rarg)
|
||||
#define UPDATE_RVOICE_BUFFERS2(proc, iarg, rarg) UPDATE_RVOICE_GENERIC_IR(proc, &voice->rvoice->buffers, iarg, rarg)
|
||||
#define UPDATE_RVOICE_ENVLFO_R1(proc, envp, rarg) UPDATE_RVOICE_GENERIC_R1(proc, &voice->rvoice->envlfo.envp, rarg)
|
||||
#define UPDATE_RVOICE_ENVLFO_I1(proc, envp, iarg) UPDATE_RVOICE_GENERIC_I1(proc, &voice->rvoice->envlfo.envp, iarg)
|
||||
|
||||
|
||||
/*
|
||||
* new_fluid_voice
|
||||
|
@ -77,7 +119,7 @@ new_fluid_voice(fluid_real_t output_rate)
|
|||
voice->channel = NULL;
|
||||
voice->sample = NULL;
|
||||
voice->output_rate = output_rate;
|
||||
UPDATE_RVOICE1(fluid_rvoice_set_output_rate, output_rate);
|
||||
UPDATE_RVOICE_R1(fluid_rvoice_set_output_rate, output_rate);
|
||||
|
||||
/* The 'sustain' and 'finished' segments of the volume / modulation
|
||||
* envelope are constant. They are never affected by any modulator
|
||||
|
@ -142,10 +184,10 @@ fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample,
|
|||
voice->debug = 0;
|
||||
voice->has_noteoff = 0;
|
||||
UPDATE_RVOICE0(fluid_rvoice_reset);
|
||||
UPDATE_RVOICE1(fluid_rvoice_set_sample, sample);
|
||||
fluid_rvoice_set_sample(voice->rvoice, sample);
|
||||
|
||||
i = fluid_channel_get_interp_method(voice->channel);
|
||||
UPDATE_RVOICE1(fluid_rvoice_set_interp_method, i);
|
||||
i = fluid_channel_get_interp_method(channel);
|
||||
UPDATE_RVOICE_I1(fluid_rvoice_set_interp_method, i);
|
||||
|
||||
/* Set all the generators to their default value, according to SF
|
||||
* 2.01 section 8.1.3 (page 48). The value of NRPN messages are
|
||||
|
@ -154,14 +196,22 @@ fluid_voice_init(fluid_voice_t* voice, fluid_sample_t* sample,
|
|||
* into voice parameters in
|
||||
* fluid_voice_calculate_runtime_synthesis_parameters. */
|
||||
fluid_gen_init(&voice->gen[0], channel);
|
||||
UPDATE_RVOICE1(fluid_rvoice_set_samplemode, voice->gen[GEN_SAMPLEMODE].val);
|
||||
UPDATE_RVOICE_I1(fluid_rvoice_set_samplemode, voice->gen[GEN_SAMPLEMODE].val);
|
||||
|
||||
voice->synth_gain = gain;
|
||||
/* avoid division by zero later*/
|
||||
if (voice->synth_gain < 0.0000001){
|
||||
voice->synth_gain = 0.0000001;
|
||||
}
|
||||
UPDATE_RVOICE1(fluid_rvoice_set_synth_gain, voice->synth_gain);
|
||||
UPDATE_RVOICE_R1(fluid_rvoice_set_synth_gain, voice->synth_gain);
|
||||
|
||||
/* Set up buffer mapping, should be done more flexible in the future. */
|
||||
i = channel->synth->audio_groups;
|
||||
UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_mapping, 2, i*2 + SYNTH_REVERB_CHANNEL);
|
||||
UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_mapping, 3, i*2 + SYNTH_CHORUS_CHANNEL);
|
||||
i = 2 * (voice->chan % i);
|
||||
UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_mapping, 0, i);
|
||||
UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_mapping, 1, i+1);
|
||||
|
||||
/* Increment the reference count of the sample to prevent the
|
||||
unloading of the soundfont while this voice is playing. */
|
||||
|
@ -182,7 +232,7 @@ fluid_voice_gen_set(fluid_voice_t* voice, int i, float val)
|
|||
voice->gen[i].val = val;
|
||||
voice->gen[i].flags = GEN_SET;
|
||||
if (i == GEN_SAMPLEMODE)
|
||||
UPDATE_RVOICE1(fluid_rvoice_set_samplemode, val);
|
||||
UPDATE_RVOICE_I1(fluid_rvoice_set_samplemode, val);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -269,19 +319,14 @@ fluid_voice_mix (fluid_voice_t *voice, int count, fluid_real_t* dsp_buf,
|
|||
fluid_real_t* reverb_buf, fluid_real_t* chorus_buf)
|
||||
{
|
||||
fluid_rvoice_buffers_t buffers;
|
||||
fluid_real_t* dest_buf[4] = {left_buf, right_buf, reverb_buf, chorus_buf};
|
||||
|
||||
buffers.count = 4;
|
||||
buffers.bufs[0].buf = left_buf;
|
||||
buffers.bufs[1].buf = right_buf;
|
||||
buffers.bufs[2].buf = reverb_buf;
|
||||
buffers.bufs[3].buf = chorus_buf;
|
||||
fluid_rvoice_buffers_set_amp(&buffers, 0, voice->amp_left);
|
||||
fluid_rvoice_buffers_set_amp(&buffers, 1, voice->amp_right);
|
||||
fluid_rvoice_buffers_set_amp(&buffers, 2, voice->amp_reverb);
|
||||
fluid_rvoice_buffers_set_amp(&buffers, 3, voice->amp_chorus);
|
||||
|
||||
buffers.bufs[0].amp = voice->amp_left;
|
||||
buffers.bufs[1].amp = voice->amp_right;
|
||||
buffers.bufs[2].amp = voice->amp_reverb;
|
||||
buffers.bufs[3].amp = voice->amp_chorus;
|
||||
|
||||
fluid_rvoice_buffers_mix(&buffers, dsp_buf, count);
|
||||
fluid_rvoice_buffers_mix(&buffers, dsp_buf, count, dest_buf, 4);
|
||||
|
||||
fluid_check_fpe ("voice_mix");
|
||||
}
|
||||
|
@ -447,7 +492,7 @@ fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t* voice)
|
|||
}
|
||||
|
||||
/* Make an estimate on how loud this voice can get at any time (attenuation). */
|
||||
UPDATE_RVOICE1(fluid_rvoice_set_min_attenuation_cB,
|
||||
UPDATE_RVOICE_R1(fluid_rvoice_set_min_attenuation_cB,
|
||||
fluid_voice_get_lower_boundary_for_attenuation(voice));
|
||||
return FLUID_OK;
|
||||
}
|
||||
|
@ -555,8 +600,8 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
|
|||
voice->pan = _GEN(voice, GEN_PAN);
|
||||
voice->amp_left = fluid_pan(voice->pan, 1) * voice->synth_gain / 32768.0f;
|
||||
voice->amp_right = fluid_pan(voice->pan, 0) * voice->synth_gain / 32768.0f;
|
||||
UPDATE_RVOICE2(fluid_rvoice_set_buf_amp, 0, voice->amp_left);
|
||||
UPDATE_RVOICE2(fluid_rvoice_set_buf_amp, 1, voice->amp_right);
|
||||
UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 0, voice->amp_left);
|
||||
UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 1, voice->amp_right);
|
||||
break;
|
||||
|
||||
case GEN_ATTENUATION:
|
||||
|
@ -567,7 +612,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
|
|||
* Motivation for range checking:
|
||||
* OHPiano.SF2 sets initial attenuation to a whooping -96 dB */
|
||||
fluid_clip(voice->attenuation, 0.0, 1440.0);
|
||||
UPDATE_RVOICE1(fluid_rvoice_set_attenuation, voice->attenuation);
|
||||
UPDATE_RVOICE_R1(fluid_rvoice_set_attenuation, voice->attenuation);
|
||||
break;
|
||||
|
||||
/* The pitch is calculated from three different generators.
|
||||
|
@ -580,7 +625,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
|
|||
voice->pitch = (_GEN(voice, GEN_PITCH)
|
||||
+ 100.0f * _GEN(voice, GEN_COARSETUNE)
|
||||
+ _GEN(voice, GEN_FINETUNE));
|
||||
UPDATE_RVOICE1(fluid_rvoice_set_pitch, voice->pitch);
|
||||
UPDATE_RVOICE_R1(fluid_rvoice_set_pitch, voice->pitch);
|
||||
break;
|
||||
|
||||
case GEN_REVERBSEND:
|
||||
|
@ -588,7 +633,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
|
|||
voice->reverb_send = _GEN(voice, GEN_REVERBSEND) / 1000.0f;
|
||||
fluid_clip(voice->reverb_send, 0.0, 1.0);
|
||||
voice->amp_reverb = voice->reverb_send * voice->synth_gain / 32768.0f;
|
||||
UPDATE_RVOICE2(fluid_rvoice_set_buf_amp, 2, voice->amp_reverb);
|
||||
UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 2, voice->amp_reverb);
|
||||
break;
|
||||
|
||||
case GEN_CHORUSSEND:
|
||||
|
@ -596,7 +641,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
|
|||
voice->chorus_send = _GEN(voice, GEN_CHORUSSEND) / 1000.0f;
|
||||
fluid_clip(voice->chorus_send, 0.0, 1.0);
|
||||
voice->amp_chorus = voice->chorus_send * voice->synth_gain / 32768.0f;
|
||||
UPDATE_RVOICE2(fluid_rvoice_set_buf_amp, 3, voice->amp_chorus);
|
||||
UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 3, voice->amp_chorus);
|
||||
break;
|
||||
|
||||
case GEN_OVERRIDEROOTKEY:
|
||||
|
@ -618,7 +663,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
|
|||
}
|
||||
/* voice->pitch depends on voice->root_pitch, so calculate voice->pitch now */
|
||||
fluid_voice_calculate_gen_pitch(voice);
|
||||
UPDATE_RVOICE1(fluid_rvoice_set_root_pitch_hz, x);
|
||||
UPDATE_RVOICE_R1(fluid_rvoice_set_root_pitch_hz, x);
|
||||
|
||||
break;
|
||||
|
||||
|
@ -663,26 +708,26 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
|
|||
case GEN_MODLFOTOPITCH:
|
||||
x = _GEN(voice, GEN_MODLFOTOPITCH);
|
||||
fluid_clip(x, -12000.0, 12000.0);
|
||||
UPDATE_RVOICE1(fluid_rvoice_set_modlfo_to_pitch, x);
|
||||
UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_pitch, x);
|
||||
break;
|
||||
|
||||
case GEN_MODLFOTOVOL:
|
||||
x = _GEN(voice, GEN_MODLFOTOVOL);
|
||||
fluid_clip(x, -960.0, 960.0);
|
||||
UPDATE_RVOICE1(fluid_rvoice_set_modlfo_to_vol, x);
|
||||
UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_vol, x);
|
||||
break;
|
||||
|
||||
case GEN_MODLFOTOFILTERFC:
|
||||
x = _GEN(voice, GEN_MODLFOTOFILTERFC);
|
||||
fluid_clip(x, -12000, 12000);
|
||||
UPDATE_RVOICE1(fluid_rvoice_set_modlfo_to_fc, x);
|
||||
UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_fc, x);
|
||||
break;
|
||||
|
||||
case GEN_MODLFODELAY:
|
||||
x = _GEN(voice, GEN_MODLFODELAY);
|
||||
fluid_clip(x, -12000.0f, 5000.0f);
|
||||
z = (unsigned int) (voice->output_rate * fluid_tc2sec_delay(x));
|
||||
UPDATE_RVOICE_ENVLFO1(fluid_lfo_set_delay, modlfo, z);
|
||||
UPDATE_RVOICE_ENVLFO_I1(fluid_lfo_set_delay, modlfo, z);
|
||||
break;
|
||||
|
||||
case GEN_MODLFOFREQ:
|
||||
|
@ -692,7 +737,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
|
|||
x = _GEN(voice, GEN_MODLFOFREQ);
|
||||
fluid_clip(x, -16000.0f, 4500.0f);
|
||||
x = (4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate);
|
||||
UPDATE_RVOICE_ENVLFO1(fluid_lfo_set_incr, modlfo, x);
|
||||
UPDATE_RVOICE_ENVLFO_R1(fluid_lfo_set_incr, modlfo, x);
|
||||
break;
|
||||
|
||||
case GEN_VIBLFOFREQ:
|
||||
|
@ -704,20 +749,20 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
|
|||
x = _GEN(voice, GEN_VIBLFOFREQ);
|
||||
fluid_clip(x, -16000.0f, 4500.0f);
|
||||
x = 4.0f * FLUID_BUFSIZE * fluid_act2hz(x) / voice->output_rate;
|
||||
UPDATE_RVOICE_ENVLFO1(fluid_lfo_set_incr, viblfo, x);
|
||||
UPDATE_RVOICE_ENVLFO_R1(fluid_lfo_set_incr, viblfo, x);
|
||||
break;
|
||||
|
||||
case GEN_VIBLFODELAY:
|
||||
x = _GEN(voice,GEN_VIBLFODELAY);
|
||||
fluid_clip(x, -12000.0f, 5000.0f);
|
||||
z = (unsigned int) (voice->output_rate * fluid_tc2sec_delay(x));
|
||||
UPDATE_RVOICE_ENVLFO1(fluid_lfo_set_delay, viblfo, z);
|
||||
UPDATE_RVOICE_ENVLFO_I1(fluid_lfo_set_delay, viblfo, z);
|
||||
break;
|
||||
|
||||
case GEN_VIBLFOTOPITCH:
|
||||
x = _GEN(voice, GEN_VIBLFOTOPITCH);
|
||||
fluid_clip(x, -12000.0, 12000.0);
|
||||
UPDATE_RVOICE1(fluid_rvoice_set_viblfo_to_pitch, x);
|
||||
UPDATE_RVOICE_R1(fluid_rvoice_set_viblfo_to_pitch, x);
|
||||
break;
|
||||
|
||||
case GEN_KEYNUM:
|
||||
|
@ -752,7 +797,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
|
|||
case GEN_MODENVTOPITCH:
|
||||
x = _GEN(voice, GEN_MODENVTOPITCH);
|
||||
fluid_clip(x, -12000.0, 12000.0);
|
||||
UPDATE_RVOICE1(fluid_rvoice_set_modenv_to_pitch, x);
|
||||
UPDATE_RVOICE_R1(fluid_rvoice_set_modenv_to_pitch, x);
|
||||
break;
|
||||
|
||||
case GEN_MODENVTOFILTERFC:
|
||||
|
@ -763,7 +808,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
|
|||
* Filter is reported to make funny noises now and then
|
||||
*/
|
||||
fluid_clip(x, -12000.0, 12000.0);
|
||||
UPDATE_RVOICE1(fluid_rvoice_set_modenv_to_fc, x);
|
||||
UPDATE_RVOICE_R1(fluid_rvoice_set_modenv_to_fc, x);
|
||||
break;
|
||||
|
||||
|
||||
|
@ -784,7 +829,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
|
|||
x = (voice->sample->start
|
||||
+ (int) _GEN(voice, GEN_STARTADDROFS)
|
||||
+ 32768 * (int) _GEN(voice, GEN_STARTADDRCOARSEOFS));
|
||||
UPDATE_RVOICE1(fluid_rvoice_set_start, x);
|
||||
UPDATE_RVOICE_I1(fluid_rvoice_set_start, x);
|
||||
}
|
||||
break;
|
||||
case GEN_ENDADDROFS: /* SF2.01 section 8.1.3 # 1 */
|
||||
|
@ -793,7 +838,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
|
|||
x = (voice->sample->end
|
||||
+ (int) _GEN(voice, GEN_ENDADDROFS)
|
||||
+ 32768 * (int) _GEN(voice, GEN_ENDADDRCOARSEOFS));
|
||||
UPDATE_RVOICE1(fluid_rvoice_set_end, x);
|
||||
UPDATE_RVOICE_I1(fluid_rvoice_set_end, x);
|
||||
}
|
||||
break;
|
||||
case GEN_STARTLOOPADDROFS: /* SF2.01 section 8.1.3 # 2 */
|
||||
|
@ -802,7 +847,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
|
|||
x = (voice->sample->loopstart
|
||||
+ (int) _GEN(voice, GEN_STARTLOOPADDROFS)
|
||||
+ 32768 * (int) _GEN(voice, GEN_STARTLOOPADDRCOARSEOFS));
|
||||
UPDATE_RVOICE1(fluid_rvoice_set_loopstart, x);
|
||||
UPDATE_RVOICE_I1(fluid_rvoice_set_loopstart, x);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -812,7 +857,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
|
|||
x = (voice->sample->loopend
|
||||
+ (int) _GEN(voice, GEN_ENDLOOPADDROFS)
|
||||
+ 32768 * (int) _GEN(voice, GEN_ENDLOOPADDRCOARSEOFS));
|
||||
UPDATE_RVOICE1(fluid_rvoice_set_loopend, x);
|
||||
UPDATE_RVOICE_I1(fluid_rvoice_set_loopend, x);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1036,7 +1081,7 @@ fluid_voice_noteoff(fluid_voice_t* voice)
|
|||
voice->status = FLUID_VOICE_SUSTAINED;
|
||||
} else {
|
||||
at_tick = fluid_channel_get_min_note_length_ticks (voice->channel);
|
||||
UPDATE_RVOICE1(fluid_rvoice_noteoff, at_tick);
|
||||
UPDATE_RVOICE_I1(fluid_rvoice_noteoff, at_tick);
|
||||
voice->has_noteoff = 1;
|
||||
}
|
||||
|
||||
|
@ -1079,7 +1124,7 @@ fluid_voice_kill_excl(fluid_voice_t* voice){
|
|||
fluid_voice_update_param(voice, GEN_MODENVRELEASE);
|
||||
|
||||
at_tick = fluid_channel_get_min_note_length_ticks (voice->channel);
|
||||
UPDATE_RVOICE1(fluid_rvoice_noteoff, at_tick);
|
||||
UPDATE_RVOICE_I1(fluid_rvoice_noteoff, at_tick);
|
||||
|
||||
|
||||
return FLUID_OK;
|
||||
|
@ -1295,11 +1340,11 @@ int fluid_voice_set_gain(fluid_voice_t* voice, fluid_real_t gain)
|
|||
voice->amp_reverb = voice->reverb_send * gain / 32768.0f;
|
||||
voice->amp_chorus = voice->chorus_send * gain / 32768.0f;
|
||||
|
||||
UPDATE_RVOICE1(fluid_rvoice_set_synth_gain, gain);
|
||||
UPDATE_RVOICE2(fluid_rvoice_set_buf_amp, 0, voice->amp_left);
|
||||
UPDATE_RVOICE2(fluid_rvoice_set_buf_amp, 1, voice->amp_right);
|
||||
UPDATE_RVOICE2(fluid_rvoice_set_buf_amp, 2, voice->amp_reverb);
|
||||
UPDATE_RVOICE2(fluid_rvoice_set_buf_amp, 3, voice->amp_chorus);
|
||||
UPDATE_RVOICE_R1(fluid_rvoice_set_synth_gain, gain);
|
||||
UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 0, voice->amp_left);
|
||||
UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 1, voice->amp_right);
|
||||
UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 2, voice->amp_reverb);
|
||||
UPDATE_RVOICE_BUFFERS2(fluid_rvoice_buffers_set_amp, 3, voice->amp_chorus);
|
||||
|
||||
return FLUID_OK;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "fluid_adsr_env.h"
|
||||
#include "fluid_lfo.h"
|
||||
#include "fluid_rvoice.h"
|
||||
#include "fluid_sys.h"
|
||||
|
||||
#define NO_CHANNEL 0xff
|
||||
|
||||
|
@ -133,6 +134,26 @@ void fluid_voice_mix (fluid_voice_t *voice, int count, fluid_real_t* dsp_buf,
|
|||
|
||||
int fluid_voice_kill_excl(fluid_voice_t* voice);
|
||||
|
||||
/**
|
||||
* Locks the rvoice for rendering, so it can't be modified directly
|
||||
*/
|
||||
static FLUID_INLINE fluid_rvoice_t*
|
||||
fluid_voice_lock_rvoice(fluid_voice_t* voice)
|
||||
{
|
||||
voice->can_access_rvoice = 0;
|
||||
return voice->rvoice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks the rvoice for rendering, so it can be modified directly
|
||||
*/
|
||||
static FLUID_INLINE void
|
||||
fluid_voice_unlock_rvoice(fluid_voice_t* voice)
|
||||
{
|
||||
voice->can_access_rvoice = 1;
|
||||
}
|
||||
|
||||
|
||||
#define fluid_voice_get_channel(voice) ((voice)->channel)
|
||||
|
||||
|
||||
|
@ -147,7 +168,8 @@ int fluid_voice_kill_excl(fluid_voice_t* voice);
|
|||
* section 5 (release). */
|
||||
#define _ON(voice) ((voice)->status == FLUID_VOICE_ON && !voice->has_noteoff)
|
||||
#define _SUSTAINED(voice) ((voice)->status == FLUID_VOICE_SUSTAINED)
|
||||
#define _AVAILABLE(voice) (((voice)->status == FLUID_VOICE_CLEAN) || ((voice)->status == FLUID_VOICE_OFF))
|
||||
#define _AVAILABLE(voice) ((voice)->can_access_rvoice && \
|
||||
(((voice)->status == FLUID_VOICE_CLEAN) || ((voice)->status == FLUID_VOICE_OFF)))
|
||||
#define _RELEASED(voice) ((voice)->chan == NO_CHANNEL)
|
||||
#define _SAMPLEMODE(voice) ((int)(voice)->gen[GEN_SAMPLEMODE].val)
|
||||
|
||||
|
|
38
fluidsynth/src/rvoice/fluid_adsr_env.c
Normal file
38
fluidsynth/src/rvoice/fluid_adsr_env.c
Normal file
|
@ -0,0 +1,38 @@
|
|||
/* 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
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include "fluid_adsr_env.h"
|
||||
|
||||
void
|
||||
fluid_adsr_env_set_data(fluid_adsr_env_t* env,
|
||||
fluid_adsr_env_section_t section,
|
||||
unsigned int count,
|
||||
fluid_real_t coeff,
|
||||
fluid_real_t incr,
|
||||
fluid_real_t min,
|
||||
fluid_real_t max)
|
||||
{
|
||||
env->data[section].count = count;
|
||||
env->data[section].coeff = coeff;
|
||||
env->data[section].incr = incr;
|
||||
env->data[section].min = min;
|
||||
env->data[section].max = max;
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@
|
|||
#define _FLUID_ADSR_ENVELOPE_H
|
||||
|
||||
#include "fluidsynth_priv.h"
|
||||
#include "fluid_sys.h"
|
||||
|
||||
/*
|
||||
* envelope data
|
||||
|
@ -59,7 +60,7 @@ struct _fluid_adsr_env_t {
|
|||
|
||||
/* For performance, all functions are inlined */
|
||||
|
||||
static inline void
|
||||
static FLUID_INLINE void
|
||||
fluid_adsr_env_calc(fluid_adsr_env_t* env, int is_volenv)
|
||||
{
|
||||
fluid_env_data_t* env_data;
|
||||
|
@ -99,21 +100,16 @@ fluid_adsr_env_calc(fluid_adsr_env_t* env, int is_volenv)
|
|||
env->count++;
|
||||
}
|
||||
|
||||
static inline void
|
||||
/* This one cannot be inlined since it is referenced in
|
||||
the event queue */
|
||||
void
|
||||
fluid_adsr_env_set_data(fluid_adsr_env_t* env,
|
||||
fluid_adsr_env_section_t section,
|
||||
unsigned int count,
|
||||
fluid_real_t coeff,
|
||||
fluid_real_t incr,
|
||||
fluid_real_t min,
|
||||
fluid_real_t max)
|
||||
{
|
||||
env->data[section].count = count;
|
||||
env->data[section].coeff = coeff;
|
||||
env->data[section].incr = incr;
|
||||
env->data[section].min = min;
|
||||
env->data[section].max = max;
|
||||
}
|
||||
fluid_real_t max);
|
||||
|
||||
static inline void
|
||||
fluid_adsr_env_reset(fluid_adsr_env_t* env)
|
||||
|
|
|
@ -273,6 +273,8 @@ fluid_rvoice_write (fluid_rvoice_t* voice, fluid_real_t *dsp_buf)
|
|||
|
||||
/******************* sample sanity check **********/
|
||||
|
||||
if (!voice->dsp.sample)
|
||||
return 0;
|
||||
if (voice->dsp.check_sample_sanity_flag)
|
||||
fluid_rvoice_check_sample_sanity(voice);
|
||||
|
||||
|
@ -369,32 +371,46 @@ fluid_rvoice_write (fluid_rvoice_t* voice, fluid_real_t *dsp_buf)
|
|||
}
|
||||
|
||||
|
||||
static inline fluid_real_t*
|
||||
get_dest_buf(fluid_rvoice_buffers_t* buffers, int index,
|
||||
fluid_real_t** dest_bufs, int dest_bufcount)
|
||||
{
|
||||
int j = buffers->bufs[index].mapping;
|
||||
if (j >= dest_bufcount || j < 0) return NULL;
|
||||
return dest_bufs[j];
|
||||
}
|
||||
|
||||
/**
|
||||
* Mix data down to buffers
|
||||
*
|
||||
* @param buffers Destination buffer(s)
|
||||
* @param dsp_buf Mono sample source
|
||||
* @param count Number of samples to process
|
||||
* @param samplecount Number of samples to process (no FLUID_BUFSIZE restriction)
|
||||
* @param dest_bufs Array of buffers to mixdown to
|
||||
* @param dest_bufcount Length of dest_bufs
|
||||
*/
|
||||
void
|
||||
fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t* buffers,
|
||||
fluid_real_t* dsp_buf, int count)
|
||||
fluid_real_t* dsp_buf, int samplecount,
|
||||
fluid_real_t** dest_bufs, int dest_bufcount)
|
||||
{
|
||||
int bufcount = buffers->count;
|
||||
int i, dsp_i;
|
||||
if (!samplecount || !bufcount || !dest_bufcount)
|
||||
return;
|
||||
|
||||
for (i=0; i < bufcount; i++) {
|
||||
fluid_real_t* buf = get_dest_buf(buffers, i, dest_bufs, dest_bufcount);
|
||||
fluid_real_t* next_buf;
|
||||
fluid_real_t amp = buffers->bufs[i].amp;
|
||||
fluid_real_t* buf = buffers->bufs[i].buf;
|
||||
if (buf == NULL || amp == 0.0f)
|
||||
continue;
|
||||
|
||||
/* Optimization for centered stereo samples - we can save one
|
||||
multiplication per sample */
|
||||
if (i < bufcount-1 && buffers->bufs[i+1].buf != NULL &&
|
||||
buffers->bufs[i+1].amp == amp) {
|
||||
fluid_real_t* next_buf = buffers->bufs[i+1].buf;
|
||||
for (dsp_i = 0; dsp_i < count; dsp_i++) {
|
||||
next_buf = (i+1 >= bufcount ? NULL : get_dest_buf(buffers, i+1, dest_bufs, dest_bufcount));
|
||||
if (next_buf && buffers->bufs[i+1].amp == amp) {
|
||||
for (dsp_i = 0; dsp_i < samplecount; dsp_i++) {
|
||||
fluid_real_t samp = amp * dsp_buf[dsp_i];
|
||||
buf[dsp_i] += samp;
|
||||
next_buf[dsp_i] += samp;
|
||||
|
@ -402,28 +418,50 @@ fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t* buffers,
|
|||
i++;
|
||||
}
|
||||
else {
|
||||
for (dsp_i = 0; dsp_i < count; dsp_i++)
|
||||
for (dsp_i = 0; dsp_i < samplecount; dsp_i++)
|
||||
buf[dsp_i] += amp * dsp_buf[dsp_i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Synthesize a voice to several buffers.
|
||||
*
|
||||
* @param voice rvoice to synthesize
|
||||
* @return Count of samples written to the buffers. (-1 means voice is currently
|
||||
* quiet, 0 .. #FLUID_BUFSIZE-1 means voice finished.)
|
||||
* Initialize buffers up to (and including) bufnum
|
||||
*/
|
||||
int
|
||||
fluid_rvoice_render(fluid_rvoice_t* voice)
|
||||
static int
|
||||
fluid_rvoice_buffers_check_bufnum(fluid_rvoice_buffers_t* buffers, unsigned int bufnum)
|
||||
{
|
||||
fluid_real_t dsp_buf[FLUID_BUFSIZE];
|
||||
int result = fluid_rvoice_write(voice, dsp_buf);
|
||||
if (result > 0)
|
||||
fluid_rvoice_buffers_mix(&voice->buffers, dsp_buf, result);
|
||||
return result;
|
||||
unsigned int i;
|
||||
|
||||
if (bufnum < buffers->count) return FLUID_OK;
|
||||
if (bufnum >= FLUID_RVOICE_MAX_BUFS) return FLUID_FAILED;
|
||||
|
||||
for (i = buffers->count; i <= bufnum; i++) {
|
||||
buffers->bufs[bufnum].amp = 0.0f;
|
||||
buffers->bufs[bufnum].mapping = i;
|
||||
}
|
||||
buffers->count = bufnum+1;
|
||||
return FLUID_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
fluid_rvoice_buffers_set_amp(fluid_rvoice_buffers_t* buffers,
|
||||
unsigned int bufnum, fluid_real_t value)
|
||||
{
|
||||
if (fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK)
|
||||
return;
|
||||
buffers->bufs[bufnum].amp = value;
|
||||
}
|
||||
|
||||
void
|
||||
fluid_rvoice_buffers_set_mapping(fluid_rvoice_buffers_t* buffers,
|
||||
unsigned int bufnum, int mapping)
|
||||
{
|
||||
if (fluid_rvoice_buffers_check_bufnum(buffers, bufnum) != FLUID_OK)
|
||||
return;
|
||||
buffers->bufs[bufnum].mapping = mapping;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
fluid_rvoice_reset(fluid_rvoice_t* voice)
|
||||
|
@ -524,15 +562,6 @@ fluid_rvoice_set_min_attenuation_cB(fluid_rvoice_t* voice, fluid_real_t value)
|
|||
voice->dsp.min_attenuation_cB = value;
|
||||
}
|
||||
|
||||
void
|
||||
fluid_rvoice_set_buf_amp(fluid_rvoice_t* voice, unsigned int bufnum, fluid_real_t value)
|
||||
{
|
||||
if (bufnum >= FLUID_RVOICE_MAX_BUFS) return; /* Safety net */
|
||||
if (voice->buffers.count <= bufnum)
|
||||
voice->buffers.count = bufnum+1;
|
||||
voice->buffers.bufs[bufnum].amp = value;
|
||||
}
|
||||
|
||||
void
|
||||
fluid_rvoice_set_viblfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value)
|
||||
{
|
||||
|
|
|
@ -134,10 +134,10 @@ struct _fluid_rvoice_dsp_t
|
|||
*/
|
||||
struct _fluid_rvoice_buffers_t
|
||||
{
|
||||
unsigned int count; /* Number of buffers */
|
||||
unsigned int count; /* Number of records in "bufs" */
|
||||
struct {
|
||||
fluid_real_t amp;
|
||||
fluid_real_t* buf;
|
||||
int mapping; /* Mapping to mixdown buffer index */
|
||||
} bufs[FLUID_RVOICE_MAX_BUFS];
|
||||
};
|
||||
|
||||
|
@ -155,9 +155,14 @@ struct _fluid_rvoice_t
|
|||
|
||||
|
||||
int fluid_rvoice_write(fluid_rvoice_t* voice, fluid_real_t *dsp_buf);
|
||||
|
||||
void fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t* buffers,
|
||||
fluid_real_t* dsp_buf, int count);
|
||||
int fluid_rvoice_render(fluid_rvoice_t* voice);
|
||||
fluid_real_t* dsp_buf, int samplecount,
|
||||
fluid_real_t** dest_bufs, int dest_bufcount);
|
||||
void fluid_rvoice_buffers_set_amp(fluid_rvoice_buffers_t* buffers,
|
||||
unsigned int bufnum, fluid_real_t value);
|
||||
void fluid_rvoice_buffers_set_mapping(fluid_rvoice_buffers_t* buffers,
|
||||
unsigned int bufnum, int mapping);
|
||||
|
||||
/* Dynamic update functions */
|
||||
|
||||
|
@ -171,7 +176,6 @@ void fluid_rvoice_set_pitch(fluid_rvoice_t* voice, fluid_real_t value);
|
|||
void fluid_rvoice_set_synth_gain(fluid_rvoice_t* voice, fluid_real_t value);
|
||||
void fluid_rvoice_set_attenuation(fluid_rvoice_t* voice, fluid_real_t value);
|
||||
void fluid_rvoice_set_min_attenuation_cB(fluid_rvoice_t* voice, fluid_real_t value);
|
||||
void fluid_rvoice_set_buf_amp(fluid_rvoice_t* voice, unsigned int bufnum, fluid_real_t value);
|
||||
void fluid_rvoice_set_viblfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value);
|
||||
void fluid_rvoice_set_modlfo_to_pitch(fluid_rvoice_t* voice, fluid_real_t value);
|
||||
void fluid_rvoice_set_modlfo_to_vol(fluid_rvoice_t* voice, fluid_real_t value);
|
||||
|
|
254
fluidsynth/src/rvoice/fluid_rvoice_event.c
Normal file
254
fluidsynth/src/rvoice/fluid_rvoice_event.c
Normal file
|
@ -0,0 +1,254 @@
|
|||
/* 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
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include "fluid_rvoice_event.h"
|
||||
#include "fluid_rvoice.h"
|
||||
#include "fluid_rvoice_handler.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) { \
|
||||
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) { \
|
||||
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; }
|
||||
|
||||
void
|
||||
fluid_rvoice_event_dispatch(fluid_rvoice_event_t* event)
|
||||
{
|
||||
EVENTFUNC_PTR(fluid_rvoice_handler_add_voice, fluid_rvoice_handler_t*, fluid_rvoice_t*);
|
||||
EVENTFUNC_I1(fluid_rvoice_noteoff, fluid_rvoice_t*);
|
||||
EVENTFUNC_0(fluid_rvoice_voiceoff, 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_R1(fluid_rvoice_handler_set_polyphony, fluid_rvoice_handler_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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
{
|
||||
fluid_rvoice_eventhandler_t* eventhandler = FLUID_NEW(fluid_rvoice_eventhandler_t);
|
||||
if (eventhandler == NULL) {
|
||||
FLUID_LOG(FLUID_ERR, "Out of memory");
|
||||
return NULL;
|
||||
}
|
||||
eventhandler->handler = 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->handler = new_fluid_rvoice_handler();
|
||||
if (eventhandler->handler == NULL)
|
||||
goto error_recovery;
|
||||
fluid_rvoice_handler_set_voice_callback(eventhandler->handler,
|
||||
finished_voice_callback, eventhandler);
|
||||
return eventhandler;
|
||||
|
||||
error_recovery:
|
||||
delete_fluid_rvoice_eventhandler(eventhandler);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Call fluid_rvoice_event_dispatch for all events in queue
|
||||
*/
|
||||
void
|
||||
fluid_rvoice_eventhandler_dispatch_all(fluid_rvoice_eventhandler_t* handler)
|
||||
{
|
||||
fluid_rvoice_event_t* event;
|
||||
while (NULL != (event = fluid_ringbuffer_get_outptr(handler->queue))) {
|
||||
fluid_rvoice_event_dispatch(event);
|
||||
fluid_ringbuffer_next_outptr(handler->queue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
delete_fluid_rvoice_eventhandler(fluid_rvoice_eventhandler_t* handler)
|
||||
{
|
||||
if (handler == NULL) return;
|
||||
delete_fluid_ringbuffer(handler->queue);
|
||||
delete_fluid_ringbuffer(handler->finished_voices);
|
||||
FLUID_FREE(handler);
|
||||
}
|
113
fluidsynth/src/rvoice/fluid_rvoice_event.h
Normal file
113
fluidsynth/src/rvoice/fluid_rvoice_event.h
Normal file
|
@ -0,0 +1,113 @@
|
|||
/* 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
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307, USA
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _FLUID_RVOICE_EVENT_H
|
||||
#define _FLUID_RVOICE_EVENT_H
|
||||
|
||||
#include "fluidsynth_priv.h"
|
||||
#include "fluid_rvoice_handler.h"
|
||||
#include "fluid_ringbuffer.h"
|
||||
|
||||
#define EVENT_REAL_PARAMS (5)
|
||||
|
||||
typedef struct _fluid_rvoice_event_t fluid_rvoice_event_t;
|
||||
typedef struct _fluid_rvoice_eventhandler_t fluid_rvoice_eventhandler_t;
|
||||
|
||||
struct _fluid_rvoice_event_t {
|
||||
void* method;
|
||||
void* object;
|
||||
void* ptr;
|
||||
int intparam;
|
||||
fluid_real_t realparams[EVENT_REAL_PARAMS];
|
||||
};
|
||||
|
||||
void fluid_rvoice_event_dispatch(fluid_rvoice_event_t* event);
|
||||
|
||||
|
||||
/**
|
||||
* Bridge between the renderer thread and the midi state thread.
|
||||
* If is_threadsafe is true, that means fluid_rvoice_eventhandler_fetch_all
|
||||
* can be called in parallell with fluid_rvoice_eventhandler_push/flush
|
||||
*/
|
||||
struct _fluid_rvoice_eventhandler_t {
|
||||
int is_threadsafe; /* False for optimal performance, true for atomic operations */
|
||||
fluid_ringbuffer_t* queue; /**< List of fluid_rvoice_event_t */
|
||||
int queue_stored; /**< Extras pushed but not flushed */
|
||||
fluid_ringbuffer_t* finished_voices; /**< return queue from handler, list of fluid_rvoice_t* */
|
||||
fluid_rvoice_handler_t* handler;
|
||||
};
|
||||
|
||||
fluid_rvoice_eventhandler_t* new_fluid_rvoice_eventhandler(
|
||||
int is_threadsafe, int queuesize, int finished_voices_size);
|
||||
|
||||
void delete_fluid_rvoice_eventhandler(fluid_rvoice_eventhandler_t*);
|
||||
|
||||
void fluid_rvoice_eventhandler_dispatch_all(fluid_rvoice_eventhandler_t*);
|
||||
|
||||
static FLUID_INLINE void
|
||||
fluid_rvoice_eventhandler_flush(fluid_rvoice_eventhandler_t* handler)
|
||||
{
|
||||
if (handler->queue_stored > 0) {
|
||||
fluid_ringbuffer_next_inptr(handler->queue, handler->queue_stored);
|
||||
handler->queue_stored = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return next finished voice, or NULL if nothing in queue
|
||||
*/
|
||||
static FLUID_INLINE fluid_rvoice_t*
|
||||
fluid_rvoice_eventhandler_get_finished_voice(fluid_rvoice_eventhandler_t* handler)
|
||||
{
|
||||
void* result = fluid_ringbuffer_get_outptr(handler->finished_voices);
|
||||
if (result == NULL) return NULL;
|
||||
result = * (fluid_rvoice_t**) result;
|
||||
fluid_ringbuffer_next_outptr(handler->finished_voices);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int fluid_rvoice_eventhandler_push(fluid_rvoice_eventhandler_t* handler,
|
||||
void* method, void* object, int intparam,
|
||||
fluid_real_t realparam);
|
||||
|
||||
int fluid_rvoice_eventhandler_push_ptr(fluid_rvoice_eventhandler_t* handler,
|
||||
void* method, void* object, void* ptr);
|
||||
|
||||
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);
|
||||
|
||||
static FLUID_INLINE void
|
||||
fluid_rvoice_eventhandler_add_rvoice(fluid_rvoice_eventhandler_t* handler,
|
||||
fluid_rvoice_t* rvoice)
|
||||
{
|
||||
if (handler->is_threadsafe)
|
||||
fluid_rvoice_eventhandler_push_ptr(handler, fluid_rvoice_handler_add_voice,
|
||||
handler->handler, rvoice);
|
||||
else
|
||||
fluid_rvoice_handler_add_voice(handler->handler, rvoice);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
204
fluidsynth/src/rvoice/fluid_rvoice_handler.c
Normal file
204
fluidsynth/src/rvoice/fluid_rvoice_handler.c
Normal file
|
@ -0,0 +1,204 @@
|
|||
/* 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
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307, USA
|
||||
*/
|
||||
|
||||
|
||||
#include "fluid_rvoice_handler.h"
|
||||
|
||||
|
||||
fluid_rvoice_handler_t* new_fluid_rvoice_handler(void)
|
||||
{
|
||||
fluid_rvoice_handler_t* handler;
|
||||
|
||||
handler = FLUID_NEW(fluid_rvoice_handler_t);
|
||||
if (handler == NULL) {
|
||||
FLUID_LOG(FLUID_ERR, "Out of memory");
|
||||
return NULL;
|
||||
}
|
||||
FLUID_MEMSET(handler, 0, sizeof(fluid_rvoice_handler_t));
|
||||
|
||||
return handler;
|
||||
}
|
||||
|
||||
void delete_fluid_rvoice_handler(fluid_rvoice_handler_t* handler)
|
||||
{
|
||||
if (handler == NULL)
|
||||
return;
|
||||
|
||||
#if 0
|
||||
FLUID_FREE(handler->finished_voices);
|
||||
#endif
|
||||
FLUID_FREE(handler->voices);
|
||||
FLUID_FREE(handler);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
fluid_rvoice_handler_add_voice(fluid_rvoice_handler_t* handler, fluid_rvoice_t* voice)
|
||||
{
|
||||
if (handler->active_voices >= handler->polyphony) {
|
||||
FLUID_LOG(FLUID_WARN, "Trying to exceed polyphony in fluid_rvoice_handler_add_voice");
|
||||
return FLUID_FAILED;
|
||||
}
|
||||
handler->voices[handler->active_voices++] = voice;
|
||||
return FLUID_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update polyphony - max number of voices (NOTE: not hard real-time capable)
|
||||
* @return FLUID_OK or FLUID_FAILED
|
||||
*/
|
||||
int
|
||||
fluid_rvoice_handler_set_polyphony(fluid_rvoice_handler_t* handler, int value)
|
||||
{
|
||||
void* newptr;
|
||||
if (handler->active_voices > value)
|
||||
return FLUID_FAILED;
|
||||
#if 0
|
||||
if (handler->finished_voice_count > value)
|
||||
return FLUID_FAILED;
|
||||
#endif
|
||||
|
||||
newptr = FLUID_REALLOC(handler->voices, value * sizeof(fluid_rvoice_t*));
|
||||
if (newptr == NULL)
|
||||
return FLUID_FAILED;
|
||||
handler->voices = newptr;
|
||||
#if 0
|
||||
newptr = FLUID_REALLOC(handler->finished_voices, value * sizeof(fluid_rvoice_t*));
|
||||
if (newptr == NULL)
|
||||
return FLUID_FAILED;
|
||||
handler->finished_voices = newptr;
|
||||
#endif
|
||||
|
||||
handler->polyphony = value;
|
||||
return FLUID_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
fluid_rvoice_handler_remove_voice(fluid_rvoice_handler_t* handler, int index)
|
||||
{
|
||||
#if 0
|
||||
if (handler->finished_voice_count < handler->polyphony)
|
||||
handler->finished_voices[handler->finished_voice_count++] = handler->voices[index];
|
||||
#endif
|
||||
|
||||
if (handler->remove_voice_callback != NULL)
|
||||
handler->remove_voice_callback(handler->remove_voice_callback_userdata,
|
||||
handler->voices[index]);
|
||||
|
||||
handler->active_voices--;
|
||||
if (index < handler->active_voices) /* Move the last voice into the "hole" */
|
||||
handler->voices[index] = handler->voices[handler->active_voices];
|
||||
}
|
||||
|
||||
/**
|
||||
* Synthesize one voice
|
||||
* @return Number of samples written
|
||||
*/
|
||||
#if 0
|
||||
static inline int
|
||||
fluid_rvoice_handler_write_one(fluid_rvoice_handler_t* handler, int index,
|
||||
fluid_real_t* buf, int blockcount)
|
||||
{
|
||||
int i, result = 0;
|
||||
fluid_rvoice_t* voice = handler->voices[index];
|
||||
for (i=0; i < blockcount; i++) {
|
||||
int s = fluid_rvoice_write(voice, buf);
|
||||
if (s == -1) {
|
||||
FLUID_MEMSET(buf, 0, FLUID_BUFSIZE*sizeof(fluid_real_t));
|
||||
s = FLUID_BUFSIZE;
|
||||
}
|
||||
buf += s;
|
||||
result += s;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Synthesize one voice and add to buffer.
|
||||
* NOTE: If return value is less than blockcount*FLUID_BUFSIZE, that means
|
||||
* voice has been finished, removed and possibly replaced with another voice.
|
||||
* @return Number of samples written
|
||||
*/
|
||||
static inline int
|
||||
fluid_rvoice_handler_mix_one(fluid_rvoice_handler_t* handler, int index,
|
||||
fluid_real_t** bufs, unsigned int blockcount, unsigned int bufcount)
|
||||
{
|
||||
unsigned int i, j=0, result = 0;
|
||||
fluid_rvoice_t* voice = handler->voices[index];
|
||||
|
||||
fluid_real_t local_buf[FLUID_BUFSIZE*blockcount];
|
||||
|
||||
for (i=0; i < blockcount; i++) {
|
||||
int s = fluid_rvoice_write(voice, &local_buf[FLUID_BUFSIZE*i]);
|
||||
if (s == -1) {
|
||||
s = FLUID_BUFSIZE; /* Voice is quiet, TODO: optimize away memset/mix */
|
||||
FLUID_MEMSET(&local_buf[FLUID_BUFSIZE*i], 0, FLUID_BUFSIZE*sizeof(fluid_real_t*));
|
||||
}
|
||||
result += s;
|
||||
if (s < FLUID_BUFSIZE) {
|
||||
j = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
fluid_rvoice_buffers_mix(&voice->buffers, local_buf, result, bufs, bufcount);
|
||||
|
||||
if (j)
|
||||
fluid_rvoice_handler_remove_voice(handler, index);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline void
|
||||
fluid_resetbufs(int blockcount, int bufcount, fluid_real_t** bufs)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i < bufcount; i++)
|
||||
FLUID_MEMSET(bufs[i], 0, blockcount * FLUID_BUFSIZE * sizeof(fluid_real_t));
|
||||
}
|
||||
|
||||
/**
|
||||
* Single-threaded scenario, no worker threads
|
||||
*/
|
||||
static inline void
|
||||
fluid_rvoice_handler_render_loop_simple(fluid_rvoice_handler_t* handler,
|
||||
int blockcount, int bufcount, fluid_real_t** bufs)
|
||||
{
|
||||
int i;
|
||||
int scount = blockcount * FLUID_BUFSIZE;
|
||||
for (i=0; i < handler->active_voices; i++) {
|
||||
int s = fluid_rvoice_handler_mix_one(handler, i, bufs, blockcount, bufcount);
|
||||
if (s < scount) i--; /* Need to render the moved voice as well */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param blockcount number of samples to render is blockcount*FLUID_BUFSIZE
|
||||
* @param bufcount number of buffers to render into
|
||||
* @param bufs array of bufcount buffers, each containing blockcount*FLUID_BUFSIZE samples
|
||||
*/
|
||||
void
|
||||
fluid_rvoice_handler_render(fluid_rvoice_handler_t* handler,
|
||||
int blockcount, int bufcount, fluid_real_t** bufs)
|
||||
{
|
||||
fluid_resetbufs(blockcount, bufcount, bufs);
|
||||
fluid_rvoice_handler_render_loop_simple(handler, blockcount, bufcount, bufs);
|
||||
}
|
80
fluidsynth/src/rvoice/fluid_rvoice_handler.h
Normal file
80
fluidsynth/src/rvoice/fluid_rvoice_handler.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
/* 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
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307, USA
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _FLUID_RVOICE_HANDLER_H
|
||||
#define _FLUID_RVOICE_HANDLER_H
|
||||
|
||||
#include "fluid_rvoice.h"
|
||||
#include "fluid_sys.h"
|
||||
|
||||
typedef struct _fluid_rvoice_handler_t fluid_rvoice_handler_t;
|
||||
|
||||
struct _fluid_rvoice_handler_t {
|
||||
fluid_rvoice_t** voices; /* Sorted so that all nulls are last */
|
||||
int polyphony; /* Length of voices array */
|
||||
int active_voices; /* Number of non-null voices */
|
||||
#if 0
|
||||
fluid_rvoice_t** finished_voices; /* List of voices who have finished */
|
||||
int finished_voice_count;
|
||||
#endif
|
||||
void (*remove_voice_callback)(void*, fluid_rvoice_t*); /**< Recieve this callback every time a voice is removed */
|
||||
void* remove_voice_callback_userdata;
|
||||
};
|
||||
|
||||
int fluid_rvoice_handler_add_voice(fluid_rvoice_handler_t* handler, fluid_rvoice_t* voice);
|
||||
int fluid_rvoice_handler_set_polyphony(fluid_rvoice_handler_t* handler, int value);
|
||||
|
||||
void fluid_rvoice_handler_render(fluid_rvoice_handler_t* handler,
|
||||
int blockcount, int bufcount,
|
||||
fluid_real_t** bufs);
|
||||
|
||||
|
||||
static FLUID_INLINE void
|
||||
fluid_rvoice_handler_set_voice_callback(
|
||||
fluid_rvoice_handler_t* handler,
|
||||
void (*func)(void*, fluid_rvoice_t*),
|
||||
void* userdata)
|
||||
{
|
||||
handler->remove_voice_callback_userdata = userdata;
|
||||
handler->remove_voice_callback = func;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static FLUID_INLINE fluid_rvoice_t**
|
||||
fluid_rvoice_handler_get_finished_voices(fluid_rvoice_handler_t* handler,
|
||||
int* count)
|
||||
{
|
||||
*count = handler->finished_voice_count;
|
||||
return handler->finished_voices;
|
||||
}
|
||||
|
||||
static inline void
|
||||
fluid_rvoice_handler_clear_finished_voices(fluid_rvoice_handler_t* handler)
|
||||
{
|
||||
handler->finished_voice_count = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
fluid_rvoice_handler_t* new_fluid_rvoice_handler(void);
|
||||
void delete_fluid_rvoice_handler(fluid_rvoice_handler_t* handler);
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in a new issue