Path: blob/master/libs/fluidsynth/src/rvoice/fluid_rvoice_mixer.c
4396 views
/* FluidSynth - A Software Synthesizer1*2* Copyright (C) 2003 Peter Hanappe and others.3*4* This library is free software; you can redistribute it and/or5* modify it under the terms of the GNU Lesser General Public License6* as published by the Free Software Foundation; either version 2.1 of7* the License, or (at your option) any later version.8*9* This library is distributed in the hope that it will be useful, but10* WITHOUT ANY WARRANTY; without even the implied warranty of11* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU12* Lesser General Public License for more details.13*14* You should have received a copy of the GNU Lesser General Public15* License along with this library; if not, write to the Free16* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA17* 02110-1301, USA18*/1920#include "fluid_rvoice_mixer.h"21#include "fluid_rvoice.h"22#include "fluid_sys.h"23#include "fluid_rev.h"24#include "fluid_chorus.h"25#include "fluid_ladspa.h"26#include "fluid_synth.h"272829// If less than x voices, the thread overhead is larger than the gain,30// so don't activate the thread(s).31#define VOICES_PER_THREAD 83233typedef struct _fluid_mixer_buffers_t fluid_mixer_buffers_t;3435struct _fluid_mixer_buffers_t36{37fluid_rvoice_mixer_t *mixer; /**< Owner of object */38#if ENABLE_MIXER_THREADS39fluid_thread_t *thread; /**< Thread object */40fluid_atomic_int_t ready; /**< Atomic: buffers are ready for mixing */41#endif4243fluid_rvoice_t **finished_voices; /* List of voices who have finished */44int finished_voice_count;4546fluid_real_t *local_buf;4748int buf_count;49int fx_buf_count;5051/** buffer to store the left part of a stereo channel to.52* Specifically a two dimensional array, containing \c buf_count sample buffers53* (i.e. for each synth.audio-groups), of which each contains54* FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT audio items (=samples)55* @note Each sample buffer is aligned to the FLUID_DEFAULT_ALIGNMENT56* boundary provided that this pointer points to an aligned buffer.57* So make sure to access the sample buffer by first aligning this58* pointer using fluid_align_ptr()59*/60fluid_real_t *left_buf;6162/** dito, but for right part of a stereo channel */63fluid_real_t *right_buf;6465/** buffer to store the left part of a stereo effects channel to.66* Specifically a two dimensional array, containing \c fx_buf_count buffers67* (i.e. for each synth.effects-channels), of which each buffer contains68* FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT audio items (=samples)69*/70fluid_real_t *fx_left_buf;71fluid_real_t *fx_right_buf;72};7374typedef struct _fluid_mixer_fx_t fluid_mixer_fx_t;7576struct _fluid_mixer_fx_t77{78fluid_revmodel_t *reverb; /**< Reverb unit */79/* reverb shadow parameters here will be returned if queried */80double reverb_param[FLUID_REVERB_PARAM_LAST];81int reverb_on; /* reverb on/off */8283fluid_chorus_t *chorus; /**< Chorus unit */84/* chorus shadow parameters here will be returned if queried */85double chorus_param[FLUID_CHORUS_PARAM_LAST];86int chorus_on; /* chorus on/off */87};8889struct _fluid_rvoice_mixer_t90{91fluid_mixer_fx_t *fx;9293fluid_mixer_buffers_t buffers; /**< Used by mixer only: own buffers */94fluid_rvoice_eventhandler_t *eventhandler;9596fluid_rvoice_t **rvoices; /**< Read-only: Voices array, sorted so that all nulls are last */97int polyphony; /**< Read-only: Length of voices array */98int active_voices; /**< Read-only: Number of non-null voices */99int current_blockcount; /**< Read-only: how many blocks to process this time */100int fx_units;101int with_reverb; /**< Should the synth use the built-in reverb unit? */102int with_chorus; /**< Should the synth use the built-in chorus unit? */103int mix_fx_to_out; /**< Should the effects be mixed in with the primary output? */104105#ifdef LADSPA106fluid_ladspa_fx_t *ladspa_fx; /**< Used by mixer only: Effects unit for LADSPA support. Never created or freed */107#endif108109#if ENABLE_MIXER_THREADS110// int sleeping_threads; /**< Atomic: number of threads currently asleep */111// int active_threads; /**< Atomic: number of threads in the thread loop */112fluid_atomic_int_t threads_should_terminate; /**< Atomic: Set to TRUE when threads should terminate */113fluid_atomic_int_t current_rvoice; /**< Atomic: for the threads to know next voice to */114fluid_cond_t *wakeup_threads; /**< Signalled when the threads should wake up */115fluid_cond_mutex_t *wakeup_threads_m; /**< wakeup_threads mutex companion */116fluid_cond_t *thread_ready; /**< Signalled from thread, when the thread has a buffer ready for mixing */117fluid_cond_mutex_t *thread_ready_m; /**< thread_ready mutex companion */118119int thread_count; /**< Number of extra mixer threads for multi-core rendering */120fluid_mixer_buffers_t *threads; /**< Array of mixer threads (thread_count in length) */121#endif122};123124#if ENABLE_MIXER_THREADS125static void delete_rvoice_mixer_threads(fluid_rvoice_mixer_t *mixer);126static int fluid_rvoice_mixer_set_threads(fluid_rvoice_mixer_t *mixer, int thread_count, int prio_level);127#endif128129static FLUID_INLINE void130fluid_rvoice_mixer_process_fx(fluid_rvoice_mixer_t *mixer, int current_blockcount)131{132// Making those variables const causes gcc to fail with "variable is predetermined ‘shared’ for ‘shared’".133// Not explicitly marking them shared makes it fail for clang and MSVC...134/*const*/ int fx_channels_per_unit = mixer->buffers.fx_buf_count / mixer->fx_units;135/*const*/ int dry_count = mixer->buffers.buf_count; /* dry buffers count */136/*const*/ int mix_fx_to_out = mixer->mix_fx_to_out; /* get mix_fx_to_out mode */137138void (*reverb_process_func)(fluid_revmodel_t *rev, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out);139void (*chorus_process_func)(fluid_chorus_t *chorus, const fluid_real_t *in, fluid_real_t *left_out, fluid_real_t *right_out);140141fluid_real_t *out_rev_l, *out_rev_r, *out_ch_l, *out_ch_r;142143// all dry unprocessed mono input is stored in the left channel144fluid_real_t *in_rev = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT);145fluid_real_t *in_ch = in_rev;146147fluid_profile_ref_var(prof_ref);148149#ifdef LADSPA150151/* Run the signal through the LADSPA Fx unit. The buffers have already been152* set up in fluid_rvoice_mixer_set_ladspa. */153if(mixer->ladspa_fx)154{155fluid_ladspa_run(mixer->ladspa_fx, current_blockcount, FLUID_BUFSIZE);156fluid_check_fpe("LADSPA");157}158159#endif160161if(mix_fx_to_out)162{163// mix effects to first stereo channel164out_ch_l = out_rev_l = fluid_align_ptr(mixer->buffers.left_buf, FLUID_DEFAULT_ALIGNMENT);165out_ch_r = out_rev_r = fluid_align_ptr(mixer->buffers.right_buf, FLUID_DEFAULT_ALIGNMENT);166167reverb_process_func = fluid_revmodel_processmix;168chorus_process_func = fluid_chorus_processmix;169}170else171{172// replace effects into respective stereo effects channel173out_ch_l = out_rev_l = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT);174out_ch_r = out_rev_r = fluid_align_ptr(mixer->buffers.fx_right_buf, FLUID_DEFAULT_ALIGNMENT);175176reverb_process_func = fluid_revmodel_processreplace;177chorus_process_func = fluid_chorus_processreplace;178}179180if(mixer->with_reverb || mixer->with_chorus)181{182#if ENABLE_MIXER_THREADS && !defined(WITH_PROFILING)183int fx_mixer_threads = mixer->fx_units;184fluid_clip(fx_mixer_threads, 1, mixer->thread_count + 1);185#pragma omp parallel default(none) shared(mixer, reverb_process_func, chorus_process_func, dry_count, current_blockcount, mix_fx_to_out, fx_channels_per_unit) firstprivate(in_rev, in_ch, out_rev_l, out_rev_r, out_ch_l, out_ch_r) num_threads(fx_mixer_threads)186#endif187{188int i, f;189int buf_idx; /* buffer index */190int samp_idx; /* sample index in buffer */191int dry_idx = 0; /* dry buffer index */192int sample_count; /* sample count to process */193if(mixer->with_reverb)194{195#if ENABLE_MIXER_THREADS && !defined(WITH_PROFILING)196#pragma omp for schedule(static)197#endif198for(f = 0; f < mixer->fx_units; f++)199{200if(!mixer->fx[f].reverb_on)201{202continue; /* this reverb unit is disabled */203}204205buf_idx = f * fx_channels_per_unit + SYNTH_REVERB_CHANNEL;206samp_idx = buf_idx * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE;207sample_count = current_blockcount * FLUID_BUFSIZE;208209/* in mix mode, map fx out_rev at index f to a dry buffer at index dry_idx */210if(mix_fx_to_out)211{212/* dry buffer mapping, should be done more flexible in the future */213dry_idx = (f % dry_count) * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE;214}215216for(i = 0; i < sample_count; i += FLUID_BUFSIZE, samp_idx += FLUID_BUFSIZE)217{218reverb_process_func(mixer->fx[f].reverb,219&in_rev[samp_idx],220mix_fx_to_out ? &out_rev_l[dry_idx + i] : &out_rev_l[samp_idx],221mix_fx_to_out ? &out_rev_r[dry_idx + i] : &out_rev_r[samp_idx]);222}223} // implicit omp barrier - required, because out_rev_l aliases with out_ch_l224225fluid_profile(FLUID_PROF_ONE_BLOCK_REVERB, prof_ref, 0,226current_blockcount * FLUID_BUFSIZE);227}228229if(mixer->with_chorus)230{231#if ENABLE_MIXER_THREADS && !defined(WITH_PROFILING)232#pragma omp for schedule(static)233#endif234for(f = 0; f < mixer->fx_units; f++)235{236if(!mixer->fx[f].chorus_on)237{238continue; /* this chorus unit is disabled */239}240241buf_idx = f * fx_channels_per_unit + SYNTH_CHORUS_CHANNEL;242samp_idx = buf_idx * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE;243sample_count = current_blockcount * FLUID_BUFSIZE;244245/* in mix mode, map fx out_ch at index f to a dry buffer at index dry_idx */246if(mix_fx_to_out)247{248/* dry buffer mapping, should be done more flexible in the future */249dry_idx = (f % dry_count) * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE;250}251252for(i = 0; i < sample_count; i += FLUID_BUFSIZE, samp_idx += FLUID_BUFSIZE)253{254chorus_process_func(mixer->fx[f].chorus,255&in_ch [samp_idx],256mix_fx_to_out ? &out_ch_l[dry_idx + i] : &out_ch_l[samp_idx],257mix_fx_to_out ? &out_ch_r[dry_idx + i] : &out_ch_r[samp_idx]);258}259}260261fluid_profile(FLUID_PROF_ONE_BLOCK_CHORUS, prof_ref, 0,262current_blockcount * FLUID_BUFSIZE);263}264}265}266}267268/**269* Glue to get fluid_rvoice_buffers_mix what it wants270* Note: Make sure outbufs has 2 * (buf_count + fx_buf_count) elements before calling271*/272static FLUID_INLINE int273fluid_mixer_buffers_prepare(fluid_mixer_buffers_t *buffers, fluid_real_t **outbufs)274{275fluid_real_t *base_ptr;276int i;277const int fx_channels_per_unit = buffers->fx_buf_count / buffers->mixer->fx_units;278const int offset = buffers->buf_count * 2;279int with_reverb = buffers->mixer->with_reverb;280int with_chorus = buffers->mixer->with_chorus;281282/* Set up the reverb and chorus buffers only when the effect is enabled or283* when LADSPA is active. Nonexisting buffers are detected in the DSP loop.284* Not sending the effect signals saves some time in that case. */285#ifdef LADSPA286int with_ladspa = (buffers->mixer->ladspa_fx != NULL);287with_reverb = (with_reverb | with_ladspa);288with_chorus = (with_chorus | with_ladspa);289#endif290291// all the dry, non-processed mono audio for effects is to be stored in the left buffers292base_ptr = fluid_align_ptr(buffers->fx_left_buf, FLUID_DEFAULT_ALIGNMENT);293294for(i = 0; i < buffers->mixer->fx_units; i++)295{296int fx_idx = i * fx_channels_per_unit;297298outbufs[offset + fx_idx + SYNTH_REVERB_CHANNEL] =299(with_reverb)300? &base_ptr[(fx_idx + SYNTH_REVERB_CHANNEL) * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT]301: NULL;302303outbufs[offset + fx_idx + SYNTH_CHORUS_CHANNEL] =304(with_chorus)305? &base_ptr[(fx_idx + SYNTH_CHORUS_CHANNEL) * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT]306: NULL;307}308309/* The output associated with a MIDI channel is wrapped around310* using the number of audio groups as modulo divider. This is311* typically the number of output channels on the 'sound card',312* as long as the LADSPA Fx unit is not used. In case of LADSPA313* unit, think of it as subgroups on a mixer.314*315* For example: Assume that the number of groups is set to 2.316* Then MIDI channel 1, 3, 5, 7 etc. go to output 1, channels 2,317* 4, 6, 8 etc to output 2. Or assume 3 groups: Then MIDI318* channels 1, 4, 7, 10 etc go to output 1; 2, 5, 8, 11 etc to319* output 2, 3, 6, 9, 12 etc to output 3.320*/321base_ptr = fluid_align_ptr(buffers->left_buf, FLUID_DEFAULT_ALIGNMENT);322323for(i = 0; i < buffers->buf_count; i++)324{325outbufs[i * 2] = &base_ptr[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT];326}327328base_ptr = fluid_align_ptr(buffers->right_buf, FLUID_DEFAULT_ALIGNMENT);329330for(i = 0; i < buffers->buf_count; i++)331{332outbufs[i * 2 + 1] = &base_ptr[i * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT];333}334335return offset + buffers->fx_buf_count;336}337338339static FLUID_INLINE void340fluid_finish_rvoice(fluid_mixer_buffers_t *buffers, fluid_rvoice_t *rvoice)341{342if(buffers->finished_voice_count < buffers->mixer->polyphony)343{344buffers->finished_voices[buffers->finished_voice_count++] = rvoice;345}346else347{348FLUID_LOG(FLUID_ERR, "Exceeded finished voices array, try increasing polyphony");349}350}351352static void353fluid_mixer_buffer_process_finished_voices(fluid_mixer_buffers_t *buffers)354{355int i, j;356357for(i = 0; i < buffers->finished_voice_count; i++)358{359fluid_rvoice_t *v = buffers->finished_voices[i];360int av = buffers->mixer->active_voices;361362for(j = 0; j < av; j++)363{364if(v == buffers->mixer->rvoices[j])365{366av--;367368/* Pack the array */369if(j < av)370{371buffers->mixer->rvoices[j] = buffers->mixer->rvoices[av];372}373}374}375376buffers->mixer->active_voices = av;377378fluid_rvoice_eventhandler_finished_voice_callback(buffers->mixer->eventhandler, v);379}380381buffers->finished_voice_count = 0;382}383384static FLUID_INLINE void fluid_rvoice_mixer_process_finished_voices(fluid_rvoice_mixer_t *mixer)385{386#if ENABLE_MIXER_THREADS387int i;388389for(i = 0; i < mixer->thread_count; i++)390{391fluid_mixer_buffer_process_finished_voices(&mixer->threads[i]);392}393394#endif395fluid_mixer_buffer_process_finished_voices(&mixer->buffers);396}397398399static FLUID_INLINE fluid_real_t *400get_dest_buf(fluid_rvoice_buffers_t *buffers, int index,401fluid_real_t **dest_bufs, int dest_bufcount)402{403int j = buffers->bufs[index].mapping;404405if(j >= dest_bufcount || j < 0)406{407return NULL;408}409410return dest_bufs[j];411}412413/**414* Mix samples down from internal dsp_buf to output buffers415*416* @param buffers Destination buffer(s)417* @param dsp_buf Mono sample source418* @param start_block starting sample in dsp_buf419* @param sample_count number of samples to mix following \c start_block420* @param dest_bufs Array of buffers to mixdown to421* @param dest_bufcount Length of dest_bufs (i.e count of buffers)422*/423static void424fluid_rvoice_buffers_mix(fluid_rvoice_buffers_t *buffers,425const fluid_real_t *FLUID_RESTRICT dsp_buf,426int start_block, int sample_count,427fluid_real_t **dest_bufs, int dest_bufcount)428{429/* buffers count to mixdown to */430int bufcount = buffers->count;431int i, dsp_i;432433/* if there is nothing to mix, return immediately */434if(sample_count <= 0 || dest_bufcount <= 0)435{436return;437}438439FLUID_ASSERT((uintptr_t)dsp_buf % FLUID_DEFAULT_ALIGNMENT == 0);440FLUID_ASSERT((uintptr_t)(&dsp_buf[start_block * FLUID_BUFSIZE]) % FLUID_DEFAULT_ALIGNMENT == 0);441442/* mixdown for each buffer */443for(i = 0; i < bufcount; i++)444{445fluid_real_t *FLUID_RESTRICT buf = get_dest_buf(buffers, i, dest_bufs, dest_bufcount);446fluid_real_t target_amp = buffers->bufs[i].target_amp;447fluid_real_t current_amp = buffers->bufs[i].current_amp;448fluid_real_t amp_incr;449450if(buf == NULL || (current_amp == 0.0f && target_amp == 0.0f))451{452continue;453}454455amp_incr = (target_amp - current_amp) / FLUID_BUFSIZE;456457FLUID_ASSERT((uintptr_t)buf % FLUID_DEFAULT_ALIGNMENT == 0);458459/* Mixdown sample_count samples in the current buffer buf460*461* For the first FLUID_BUFSIZE samples, we linearly interpolate the buffers amplitude to462* avoid clicks/pops when rapidly changing the channels panning (issue 768).463*464* We could have squashed this into one single loop by using an if clause within the loop body.465* But it seems like having two separate loops is easier for compilers to understand, and therefore466* auto-vectorizing the loops.467*/468if(sample_count < FLUID_BUFSIZE)469{470// scalar loop variant, the voice will have finished afterwards471for(dsp_i = 0; dsp_i < sample_count; dsp_i++)472{473buf[start_block * FLUID_BUFSIZE + dsp_i] += current_amp * dsp_buf[start_block * FLUID_BUFSIZE + dsp_i];474current_amp += amp_incr;475}476}477else478{479// here goes the vectorizable loop480#pragma omp simd aligned(dsp_buf,buf:FLUID_DEFAULT_ALIGNMENT)481for(dsp_i = 0; dsp_i < FLUID_BUFSIZE; dsp_i++)482{483// We cannot simply increment current_amp by amp_incr during every iteration, as this would create a dependency and prevent vectorization.484buf[start_block * FLUID_BUFSIZE + dsp_i] += (current_amp + amp_incr * dsp_i) * dsp_buf[start_block * FLUID_BUFSIZE + dsp_i];485}486487// we have reached the target_amp488if(target_amp > 0)489{490/* Note, that this loop could be unrolled by FLUID_BUFSIZE elements */491#pragma omp simd aligned(dsp_buf,buf:FLUID_DEFAULT_ALIGNMENT)492for(dsp_i = FLUID_BUFSIZE; dsp_i < sample_count; dsp_i++)493{494// Index by blocks (not by samples) to let the compiler know that we always start accessing495// buf and dsp_buf at the FLUID_BUFSIZE*sizeof(fluid_real_t) byte boundary and never somewhere496// in between.497// A good compiler should understand: Aha, so I don't need to add a peel loop when vectorizing498// this loop. Great.499buf[start_block * FLUID_BUFSIZE + dsp_i] += target_amp * dsp_buf[start_block * FLUID_BUFSIZE + dsp_i];500}501}502}503504buffers->bufs[i].current_amp = target_amp;505}506}507508/**509* Synthesize one voice and add to buffer.510* NOTE: If return value is less than blockcount*FLUID_BUFSIZE, that means511* voice has been finished, removed and possibly replaced with another voice.512*/513static FLUID_INLINE void514fluid_mixer_buffers_render_one(fluid_mixer_buffers_t *buffers,515fluid_rvoice_t *rvoice, fluid_real_t **dest_bufs,516unsigned int dest_bufcount, fluid_real_t *src_buf, int blockcount)517{518int i, total_samples = 0, last_block_mixed = 0;519520for(i = 0; i < blockcount; i++)521{522/* render one block in src_buf */523int s = fluid_rvoice_write(rvoice, &src_buf[FLUID_BUFSIZE * i]);524525if(s == -1)526{527/* the voice is silent, mix back all the previously rendered sound */528fluid_rvoice_buffers_mix(&rvoice->buffers, src_buf, last_block_mixed,529total_samples - (last_block_mixed * FLUID_BUFSIZE),530dest_bufs, dest_bufcount);531532last_block_mixed = i + 1; /* future block start index to mix from */533total_samples += FLUID_BUFSIZE; /* accumulate samples count rendered */534}535else536{537/* the voice wasn't quiet. Some samples have been rendered [0..FLUID_BUFSIZE] */538total_samples += s;539540if(s < FLUID_BUFSIZE)541{542/* voice has finished */543break;544}545}546}547548/* Now mix the remaining blocks from last_block_mixed to total_sample */549fluid_rvoice_buffers_mix(&rvoice->buffers, src_buf, last_block_mixed,550total_samples - (last_block_mixed * FLUID_BUFSIZE),551dest_bufs, dest_bufcount);552553if(total_samples < blockcount * FLUID_BUFSIZE)554{555/* voice has finished */556fluid_finish_rvoice(buffers, rvoice);557}558}559560DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_add_voice)561{562int i;563fluid_rvoice_mixer_t *mixer = obj;564fluid_rvoice_t *voice = param[0].ptr;565566if(mixer->active_voices < mixer->polyphony)567{568mixer->rvoices[mixer->active_voices++] = voice;569return; // success570}571572/* See if any voices just finished, if so, take its place.573This can happen in voice overflow conditions. */574for(i = 0; i < mixer->active_voices; i++)575{576if(mixer->rvoices[i] == voice)577{578FLUID_LOG(FLUID_ERR, "Internal error: Trying to replace an existing rvoice in fluid_rvoice_mixer_add_voice?!");579return;580}581582if(mixer->rvoices[i]->envlfo.volenv.section == FLUID_VOICE_ENVFINISHED)583{584fluid_finish_rvoice(&mixer->buffers, mixer->rvoices[i]);585mixer->rvoices[i] = voice;586return; // success587}588}589590/* This should never happen */591FLUID_LOG(FLUID_ERR, "Trying to exceed polyphony in fluid_rvoice_mixer_add_voice");592}593594static int595fluid_mixer_buffers_update_polyphony(fluid_mixer_buffers_t *buffers, int value)596{597void *newptr;598599if(buffers->finished_voice_count > value)600{601return FLUID_FAILED;602}603604newptr = FLUID_REALLOC(buffers->finished_voices, value * sizeof(fluid_rvoice_t *));605606if(newptr == NULL && value > 0)607{608return FLUID_FAILED;609}610611buffers->finished_voices = newptr;612return FLUID_OK;613}614615/**616* Update polyphony - max number of voices (NOTE: not hard real-time capable)617* @return FLUID_OK or FLUID_FAILED618*/619DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_polyphony)620{621void *newptr;622fluid_rvoice_mixer_t *handler = obj;623int value = param[0].i;624625if(handler->active_voices > value)626{627return /*FLUID_FAILED*/;628}629630newptr = FLUID_REALLOC(handler->rvoices, value * sizeof(fluid_rvoice_t *));631632if(newptr == NULL)633{634return /*FLUID_FAILED*/;635}636637handler->rvoices = newptr;638639if(fluid_mixer_buffers_update_polyphony(&handler->buffers, value)640== FLUID_FAILED)641{642return /*FLUID_FAILED*/;643}644645#if ENABLE_MIXER_THREADS646{647int i;648649for(i = 0; i < handler->thread_count; i++)650{651if(fluid_mixer_buffers_update_polyphony(&handler->threads[i], value)652== FLUID_FAILED)653{654return /*FLUID_FAILED*/;655}656}657}658#endif659660handler->polyphony = value;661/*return FLUID_OK*/;662}663664665static void666fluid_render_loop_singlethread(fluid_rvoice_mixer_t *mixer, int blockcount)667{668int i;669FLUID_DECLARE_VLA(fluid_real_t *, bufs,670mixer->buffers.buf_count * 2 + mixer->buffers.fx_buf_count * 2);671int bufcount = fluid_mixer_buffers_prepare(&mixer->buffers, bufs);672673fluid_real_t *local_buf = fluid_align_ptr(mixer->buffers.local_buf, FLUID_DEFAULT_ALIGNMENT);674675fluid_profile_ref_var(prof_ref);676677for(i = 0; i < mixer->active_voices; i++)678{679fluid_mixer_buffers_render_one(&mixer->buffers, mixer->rvoices[i], bufs,680bufcount, local_buf, blockcount);681fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref, 1,682blockcount * FLUID_BUFSIZE);683}684}685686static FLUID_INLINE void687fluid_mixer_buffers_zero(fluid_mixer_buffers_t *buffers, int current_blockcount)688{689int i, size = current_blockcount * FLUID_BUFSIZE * sizeof(fluid_real_t);690691/* TODO: Optimize by only zero out the buffers we actually use later on. */692int buf_count = buffers->buf_count, fx_buf_count = buffers->fx_buf_count;693694fluid_real_t *FLUID_RESTRICT buf_l = fluid_align_ptr(buffers->left_buf, FLUID_DEFAULT_ALIGNMENT);695fluid_real_t *FLUID_RESTRICT buf_r = fluid_align_ptr(buffers->right_buf, FLUID_DEFAULT_ALIGNMENT);696697for(i = 0; i < buf_count; i++)698{699FLUID_MEMSET(&buf_l[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size);700FLUID_MEMSET(&buf_r[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size);701}702703buf_l = fluid_align_ptr(buffers->fx_left_buf, FLUID_DEFAULT_ALIGNMENT);704buf_r = fluid_align_ptr(buffers->fx_right_buf, FLUID_DEFAULT_ALIGNMENT);705706for(i = 0; i < fx_buf_count; i++)707{708FLUID_MEMSET(&buf_l[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size);709FLUID_MEMSET(&buf_r[i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE], 0, size);710}711}712713static int714fluid_mixer_buffers_init(fluid_mixer_buffers_t *buffers, fluid_rvoice_mixer_t *mixer)715{716static const int samplecount = FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT;717718buffers->mixer = mixer;719buffers->buf_count = mixer->buffers.buf_count;720buffers->fx_buf_count = mixer->buffers.fx_buf_count;721722/* Local mono voice buf */723buffers->local_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, samplecount, FLUID_DEFAULT_ALIGNMENT);724725/* Left and right audio buffers */726727buffers->left_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT);728buffers->right_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT);729730if((buffers->local_buf == NULL) || (buffers->left_buf == NULL) || (buffers->right_buf == NULL))731{732FLUID_LOG(FLUID_ERR, "Out of memory");733return 0;734}735736/* Effects audio buffers */737738buffers->fx_left_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->fx_buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT);739buffers->fx_right_buf = FLUID_ARRAY_ALIGNED(fluid_real_t, buffers->fx_buf_count * samplecount, FLUID_DEFAULT_ALIGNMENT);740741if((buffers->fx_left_buf == NULL) || (buffers->fx_right_buf == NULL))742{743FLUID_LOG(FLUID_ERR, "Out of memory");744return 0;745}746747buffers->finished_voices = NULL;748749if(fluid_mixer_buffers_update_polyphony(buffers, mixer->polyphony)750== FLUID_FAILED)751{752FLUID_LOG(FLUID_ERR, "Out of memory");753return 0;754}755756return 1;757}758759/**760* Note: Not hard real-time capable (calls malloc)761*/762DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_samplerate)763{764fluid_rvoice_mixer_t *mixer = obj;765fluid_real_t samplerate = param[1].real; // because fluid_synth_update_mixer() puts real into arg2766767int i;768769for(i = 0; i < mixer->fx_units; i++)770{771if(mixer->fx[i].chorus)772{773fluid_chorus_samplerate_change(mixer->fx[i].chorus, samplerate);774}775776if(mixer->fx[i].reverb)777{778fluid_revmodel_samplerate_change(mixer->fx[i].reverb, samplerate);779780/*781fluid_revmodel_samplerate_change() shouldn't fail if the reverb was created782with sample_rate_max set to the maximum sample rate indicated in the settings.783If this condition isn't respected, the reverb will continue to work but with784lost of quality.785*/786}787}788789#if LADSPA790791if(mixer->ladspa_fx != NULL)792{793fluid_ladspa_set_sample_rate(mixer->ladspa_fx, samplerate);794}795796#endif797}798799800/**801* @param buf_count number of primary stereo buffers802* @param fx_buf_count number of stereo effect buffers803*/804fluid_rvoice_mixer_t *805new_fluid_rvoice_mixer(int buf_count, int fx_buf_count, int fx_units,806fluid_real_t sample_rate_max,807fluid_real_t sample_rate,808fluid_rvoice_eventhandler_t *evthandler,809int extra_threads, int prio)810{811int i;812fluid_rvoice_mixer_t *mixer = FLUID_NEW(fluid_rvoice_mixer_t);813814if(mixer == NULL)815{816FLUID_LOG(FLUID_ERR, "Out of memory");817return NULL;818}819820FLUID_MEMSET(mixer, 0, sizeof(fluid_rvoice_mixer_t));821mixer->eventhandler = evthandler;822mixer->fx_units = fx_units;823mixer->buffers.buf_count = buf_count;824mixer->buffers.fx_buf_count = fx_buf_count * fx_units;825826/* allocate the reverb module */827mixer->fx = FLUID_ARRAY(fluid_mixer_fx_t, fx_units);828829if(mixer->fx == NULL)830{831FLUID_LOG(FLUID_ERR, "Out of memory");832goto error_recovery;833}834835FLUID_MEMSET(mixer->fx, 0, fx_units * sizeof(*mixer->fx));836837for(i = 0; i < fx_units; i++)838{839/* create reverb and chorus units */840mixer->fx[i].reverb = new_fluid_revmodel(sample_rate_max, sample_rate);841mixer->fx[i].chorus = new_fluid_chorus(sample_rate);842843if(mixer->fx[i].reverb == NULL || mixer->fx[i].chorus == NULL)844{845FLUID_LOG(FLUID_ERR, "Out of memory");846goto error_recovery;847}848}849850if(!fluid_mixer_buffers_init(&mixer->buffers, mixer))851{852goto error_recovery;853}854855#if ENABLE_MIXER_THREADS856mixer->thread_ready = new_fluid_cond();857mixer->wakeup_threads = new_fluid_cond();858mixer->thread_ready_m = new_fluid_cond_mutex();859mixer->wakeup_threads_m = new_fluid_cond_mutex();860861if(!mixer->thread_ready || !mixer->wakeup_threads ||862!mixer->thread_ready_m || !mixer->wakeup_threads_m)863{864goto error_recovery;865}866867if(fluid_rvoice_mixer_set_threads(mixer, extra_threads, prio) != FLUID_OK)868{869goto error_recovery;870}871872#endif873874return mixer;875876error_recovery:877delete_fluid_rvoice_mixer(mixer);878return NULL;879}880881static void882fluid_mixer_buffers_free(fluid_mixer_buffers_t *buffers)883{884FLUID_FREE(buffers->finished_voices);885886/* free all the sample buffers */887FLUID_FREE(buffers->local_buf);888FLUID_FREE(buffers->left_buf);889FLUID_FREE(buffers->right_buf);890FLUID_FREE(buffers->fx_left_buf);891FLUID_FREE(buffers->fx_right_buf);892}893894void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t *mixer)895{896int i;897898fluid_return_if_fail(mixer != NULL);899900#if ENABLE_MIXER_THREADS901delete_rvoice_mixer_threads(mixer);902903if(mixer->thread_ready)904{905delete_fluid_cond(mixer->thread_ready);906}907908if(mixer->wakeup_threads)909{910delete_fluid_cond(mixer->wakeup_threads);911}912913if(mixer->thread_ready_m)914{915delete_fluid_cond_mutex(mixer->thread_ready_m);916}917918if(mixer->wakeup_threads_m)919{920delete_fluid_cond_mutex(mixer->wakeup_threads_m);921}922923#endif924fluid_mixer_buffers_free(&mixer->buffers);925926927for(i = 0; i < mixer->fx_units; i++)928{929if(mixer->fx[i].reverb)930{931delete_fluid_revmodel(mixer->fx[i].reverb);932}933934if(mixer->fx[i].chorus)935{936delete_fluid_chorus(mixer->fx[i].chorus);937}938}939940FLUID_FREE(mixer->fx);941FLUID_FREE(mixer->rvoices);942FLUID_FREE(mixer);943}944945#ifdef LADSPA946/**947* Set a LADSPS fx instance to be used by the mixer and assign the mixer buffers948* as LADSPA host buffers with sensible names */949void fluid_rvoice_mixer_set_ladspa(fluid_rvoice_mixer_t *mixer,950fluid_ladspa_fx_t *ladspa_fx, int audio_groups)951{952mixer->ladspa_fx = ladspa_fx;953954if(ladspa_fx == NULL)955{956return;957}958else959{960fluid_real_t *main_l = fluid_align_ptr(mixer->buffers.left_buf, FLUID_DEFAULT_ALIGNMENT);961fluid_real_t *main_r = fluid_align_ptr(mixer->buffers.right_buf, FLUID_DEFAULT_ALIGNMENT);962963fluid_real_t *rev = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT);964fluid_real_t *chor = rev;965966rev = &rev[SYNTH_REVERB_CHANNEL * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT];967chor = &chor[SYNTH_CHORUS_CHANNEL * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT];968969fluid_ladspa_add_host_ports(ladspa_fx, "Main:L", audio_groups,970main_l,971FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT);972973fluid_ladspa_add_host_ports(ladspa_fx, "Main:R", audio_groups,974main_r,975FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT);976977fluid_ladspa_add_host_ports(ladspa_fx, "Reverb:Send", 1,978rev,979FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT);980981fluid_ladspa_add_host_ports(ladspa_fx, "Chorus:Send", 1,982chor,983FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT);984}985}986#endif987988/**989* set one or more reverb shadow parameters for one fx group.990* These parameters will be returned if queried.991* (see fluid_rvoice_mixer_reverb_get_param())992*993* @param mixer that contains all fx units.994* @param fx_group index of the fx group to which parameters must be set.995* must be in the range [-1..mixer->fx_units[. If -1 the changes are applied to996* all fx units.997* @param set Flags indicating which parameters should be set (#fluid_revmodel_set_t)998* @param values table of parameters values.999*/1000void1001fluid_rvoice_mixer_set_reverb_full(const fluid_rvoice_mixer_t *mixer,1002int fx_group, int set, const double values[])1003{1004fluid_mixer_fx_t *fx = mixer->fx;1005int nr_units = mixer->fx_units;10061007if(fx_group >= 0) /* apply parameters to this fx group only */1008{1009nr_units = fx_group + 1;1010}1011else /* apply parameters to all fx groups */1012{1013fx_group = 0;1014}10151016for(; fx_group < nr_units; fx_group++)1017{1018int param;10191020for(param = 0; param < FLUID_REVERB_PARAM_LAST; param++)1021{1022if(set & FLUID_REVPARAM_TO_SETFLAG(param))1023{1024fx[fx_group].reverb_param[param] = values[param];1025}1026}1027}1028}10291030/**1031* get one reverb shadow parameter for one fx group.1032* (see fluid_rvoice_mixer_set_reverb_full())1033*1034* @param mixer that contains all fx group units.1035* @param fx_group index of the fx group to get parameter from.1036* must be in the range [0..mixer->fx_units[.1037* @param enum indicating the parameter to get.1038* FLUID_REVERB_ROOMSIZE, reverb room size value.1039* FLUID_REVERB_DAMP, reverb damping value.1040* FLUID_REVERB_WIDTH, reverb width value.1041* FLUID_REVERB_LEVEL, reverb level value.1042* @return value.1043*/1044double1045fluid_rvoice_mixer_reverb_get_param(const fluid_rvoice_mixer_t *mixer,1046int fx_group, int param)1047{1048return mixer->fx[fx_group].reverb_param[param];1049}10501051/**1052* set one or more chorus shadow parameters for one fx group.1053* These parameters will be returned if queried.1054* (see fluid_rvoice_mixer_chorus_get_param())1055*1056* @param mixer that contains all fx units.1057* @param fx_group index of the fx group to which parameters must be set.1058* must be in the range [-1..mixer->fx_units[. If -1 the changes are applied1059* to all fx group.1060* Keep in mind, that the needed CPU time is proportional to 'nr'.1061* @param set Flags indicating which parameters to set (#fluid_chorus_set_t)1062* @param values table of pararameters.1063*/1064void1065fluid_rvoice_mixer_set_chorus_full(const fluid_rvoice_mixer_t *mixer,1066int fx_group, int set, const double values[])1067{1068fluid_mixer_fx_t *fx = mixer->fx;1069int nr_units = mixer->fx_units;10701071if(fx_group >= 0) /* apply parameters to this group fx only */1072{1073nr_units = fx_group + 1;1074}1075else /* apply parameters to all fx units*/1076{1077fx_group = 0;1078}10791080for(; fx_group < nr_units; fx_group++)1081{1082int param;10831084for(param = 0; param < FLUID_CHORUS_PARAM_LAST; param++)1085{1086if(set & FLUID_CHORPARAM_TO_SETFLAG(param))1087{1088fx[fx_group].chorus_param[param] = values[param];1089}1090}1091}1092}10931094/**1095* get one chorus shadow parameter for one fx group.1096* (see fluid_rvoice_mixer_set_chorus_full())1097*1098* @param mixer that contains all fx groups units.1099* @param fx_group index of the fx group to get parameter from.1100* must be in the range [0..mixer->fx_units[.1101* @param get Flags indicating which parameter to get (#fluid_chorus_set_t)1102* @return the parameter value (0.0 is returned if error)1103*/1104double1105fluid_rvoice_mixer_chorus_get_param(const fluid_rvoice_mixer_t *mixer,1106int fx_group, int param)1107{1108return mixer->fx[fx_group].chorus_param[param];1109}11101111/* @deprecated: use fluid_rvoice_mixer_reverb_enable instead */1112DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_enabled)1113{1114fluid_rvoice_mixer_t *mixer = obj;1115int on = param[0].i;11161117mixer->with_reverb = on;1118}11191120DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reverb_enable)1121{1122fluid_rvoice_mixer_t *mixer = obj;1123int fx_group = param[0].i; /* reverb fx group index */1124int on = param[1].i; /* on/off */11251126int nr_units = mixer->fx_units;11271128/* does on/off must be applied only to fx group at index fx_group ? */1129if(fx_group >= 0)1130{1131mixer->fx[fx_group].reverb_on = on;1132}1133/* on/off must be applied to all fx groups */1134else1135{1136for(fx_group = 0; fx_group < nr_units; fx_group++)1137{1138mixer->fx[fx_group].reverb_on = on;1139}1140}11411142/* set with_reverb if at least one reverb unit is on */1143for(fx_group = 0; fx_group < nr_units; fx_group++)1144{1145on = mixer->fx[fx_group].reverb_on;11461147if(on)1148{1149break;1150}1151}11521153mixer->with_reverb = on;1154}11551156/* @deprecated: use fluid_rvoice_mixer_chorus_enable instead */1157DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_enabled)1158{1159fluid_rvoice_mixer_t *mixer = obj;1160int on = param[0].i;1161mixer->with_chorus = on;1162}11631164DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_chorus_enable)1165{1166fluid_rvoice_mixer_t *mixer = obj;1167int fx_group = param[0].i; /* chorus fx group index */1168int on = param[1].i; /* on/off */11691170int nr_units = mixer->fx_units;11711172/* does on/off must be applied only to fx group at index fx_group ? */1173if(fx_group >= 0)1174{1175mixer->fx[fx_group].chorus_on = on;1176}1177/* on/off must be applied to all fx groups */1178else1179{1180for(fx_group = 0; fx_group < nr_units; fx_group++)1181{1182mixer->fx[fx_group].chorus_on = on;1183}1184}11851186/* set with_chorus if at least one chorus unit is on */1187for(fx_group = 0; fx_group < nr_units; fx_group++)1188{1189on = mixer->fx[fx_group].chorus_on;11901191if(on)1192{1193break;1194}1195}11961197mixer->with_chorus = on;1198}11991200void fluid_rvoice_mixer_set_mix_fx(fluid_rvoice_mixer_t *mixer, int on)1201{1202mixer->mix_fx_to_out = on;1203}12041205DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_chorus_params)1206{1207fluid_rvoice_mixer_t *mixer = obj;1208int i = param[0].i;1209int set = param[1].i;1210int nr = param[2].i;1211fluid_real_t level = param[3].real;1212fluid_real_t speed = param[4].real;1213fluid_real_t depth_ms = param[5].real;1214int type = param[6].i;12151216int nr_units = mixer->fx_units;12171218/* does parameters must be applied only to fx group i ? */1219if(i >= 0)1220{1221nr_units = i + 1;1222}1223else1224{1225i = 0; /* parameters must be applied to all fx groups */1226}12271228while(i < nr_units)1229{1230fluid_chorus_set(mixer->fx[i++].chorus, set, nr, level, speed, depth_ms, type);1231}1232}12331234DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_set_reverb_params)1235{1236fluid_rvoice_mixer_t *mixer = obj;1237int i = param[0].i; /* fx group index */1238int set = param[1].i;1239fluid_real_t roomsize = param[2].real;1240fluid_real_t damping = param[3].real;1241fluid_real_t width = param[4].real;1242fluid_real_t level = param[5].real;12431244int nr_units = mixer->fx_units;12451246/* does parameters change should be applied only to fx group i ? */1247if(i >= 0)1248{1249nr_units = i + 1; /* parameters change must be applied to fx groups i */1250}1251else1252{1253i = 0; /* parameters change must be applied to all fx groups */1254}12551256while(i < nr_units)1257{1258fluid_revmodel_set(mixer->fx[i++].reverb, set, roomsize, damping, width, level);1259}1260}12611262DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reset_reverb)1263{1264fluid_rvoice_mixer_t *mixer = obj;1265int i;12661267for(i = 0; i < mixer->fx_units; i++)1268{1269fluid_revmodel_reset(mixer->fx[i].reverb);1270}1271}12721273DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_mixer_reset_chorus)1274{1275fluid_rvoice_mixer_t *mixer = obj;1276int i;12771278for(i = 0; i < mixer->fx_units; i++)1279{1280fluid_chorus_reset(mixer->fx[i].chorus);1281}1282}12831284int fluid_rvoice_mixer_get_bufs(fluid_rvoice_mixer_t *mixer,1285fluid_real_t **left, fluid_real_t **right)1286{1287*left = fluid_align_ptr(mixer->buffers.left_buf, FLUID_DEFAULT_ALIGNMENT);1288*right = fluid_align_ptr(mixer->buffers.right_buf, FLUID_DEFAULT_ALIGNMENT);1289return mixer->buffers.buf_count;1290}12911292int fluid_rvoice_mixer_get_fx_bufs(fluid_rvoice_mixer_t *mixer,1293fluid_real_t **fx_left, fluid_real_t **fx_right)1294{1295*fx_left = fluid_align_ptr(mixer->buffers.fx_left_buf, FLUID_DEFAULT_ALIGNMENT);1296*fx_right = fluid_align_ptr(mixer->buffers.fx_right_buf, FLUID_DEFAULT_ALIGNMENT);1297return mixer->buffers.fx_buf_count;1298}12991300int fluid_rvoice_mixer_get_bufcount(fluid_rvoice_mixer_t *mixer)1301{1302return FLUID_MIXER_MAX_BUFFERS_DEFAULT;1303}13041305#if WITH_PROFILING1306int fluid_rvoice_mixer_get_active_voices(fluid_rvoice_mixer_t *mixer)1307{1308return mixer->active_voices;1309}1310#endif13111312#if ENABLE_MIXER_THREADS13131314static FLUID_INLINE fluid_rvoice_t *1315fluid_mixer_get_mt_rvoice(fluid_rvoice_mixer_t *mixer)1316{1317int i = fluid_atomic_int_exchange_and_add(&mixer->current_rvoice, 1);13181319if(i >= mixer->active_voices)1320{1321return NULL;1322}13231324return mixer->rvoices[i];1325}13261327#define THREAD_BUF_PROCESSING 01328#define THREAD_BUF_VALID 11329#define THREAD_BUF_NODATA 21330#define THREAD_BUF_TERMINATE 313311332/* Core thread function (processes voices in parallel to primary synthesis thread) */1333static fluid_thread_return_t1334fluid_mixer_thread_func(void *data)1335{1336fluid_mixer_buffers_t *buffers = data;1337fluid_rvoice_mixer_t *mixer = buffers->mixer;1338int hasValidData = 0;1339FLUID_DECLARE_VLA(fluid_real_t *, bufs, buffers->buf_count * 2 + buffers->fx_buf_count * 2);1340int bufcount = 0;1341int current_blockcount = 0;1342fluid_real_t *local_buf = fluid_align_ptr(buffers->local_buf, FLUID_DEFAULT_ALIGNMENT);13431344while(!fluid_atomic_int_get(&mixer->threads_should_terminate))1345{1346fluid_rvoice_t *rvoice = fluid_mixer_get_mt_rvoice(mixer);13471348if(rvoice == NULL)1349{1350// if no voices: signal rendered buffers, sleep1351fluid_atomic_int_set(&buffers->ready, hasValidData ? THREAD_BUF_VALID : THREAD_BUF_NODATA);1352fluid_cond_mutex_lock(mixer->thread_ready_m);1353fluid_cond_signal(mixer->thread_ready);1354fluid_cond_mutex_unlock(mixer->thread_ready_m);13551356fluid_cond_mutex_lock(mixer->wakeup_threads_m);13571358while(1)1359{1360int j = fluid_atomic_int_get(&buffers->ready);13611362if(j == THREAD_BUF_PROCESSING || j == THREAD_BUF_TERMINATE)1363{1364break;1365}13661367fluid_cond_wait(mixer->wakeup_threads, mixer->wakeup_threads_m);1368}13691370fluid_cond_mutex_unlock(mixer->wakeup_threads_m);13711372hasValidData = 0;1373}1374else1375{1376// else: if buffer is not zeroed, zero buffers1377if(!hasValidData)1378{1379// blockcount may have changed, since thread was put to sleep1380current_blockcount = mixer->current_blockcount;1381fluid_mixer_buffers_zero(buffers, current_blockcount);1382bufcount = fluid_mixer_buffers_prepare(buffers, bufs);1383hasValidData = 1;1384}13851386// then render voice to buffers1387fluid_mixer_buffers_render_one(buffers, rvoice, bufs, bufcount, local_buf, current_blockcount);1388}1389}13901391return FLUID_THREAD_RETURN_VALUE;1392}13931394static void1395fluid_mixer_buffers_mix(fluid_mixer_buffers_t *dst, fluid_mixer_buffers_t *src, int current_blockcount)1396{1397int i, j;1398int scount = current_blockcount * FLUID_BUFSIZE;1399int minbuf;1400fluid_real_t *FLUID_RESTRICT base_src;1401fluid_real_t *FLUID_RESTRICT base_dst;14021403minbuf = dst->buf_count;14041405if(minbuf > src->buf_count)1406{1407minbuf = src->buf_count;1408}14091410base_src = fluid_align_ptr(src->left_buf, FLUID_DEFAULT_ALIGNMENT);1411base_dst = fluid_align_ptr(dst->left_buf, FLUID_DEFAULT_ALIGNMENT);14121413for(i = 0; i < minbuf; i++)1414{1415#pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT)14161417for(j = 0; j < scount; j++)1418{1419int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j;1420base_dst[dsp_i] += base_src[dsp_i];1421}1422}14231424base_src = fluid_align_ptr(src->right_buf, FLUID_DEFAULT_ALIGNMENT);1425base_dst = fluid_align_ptr(dst->right_buf, FLUID_DEFAULT_ALIGNMENT);14261427for(i = 0; i < minbuf; i++)1428{1429#pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT)14301431for(j = 0; j < scount; j++)1432{1433int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j;1434base_dst[dsp_i] += base_src[dsp_i];1435}1436}14371438minbuf = dst->fx_buf_count;14391440if(minbuf > src->fx_buf_count)1441{1442minbuf = src->fx_buf_count;1443}14441445base_src = fluid_align_ptr(src->fx_left_buf, FLUID_DEFAULT_ALIGNMENT);1446base_dst = fluid_align_ptr(dst->fx_left_buf, FLUID_DEFAULT_ALIGNMENT);14471448for(i = 0; i < minbuf; i++)1449{1450#pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT)14511452for(j = 0; j < scount; j++)1453{1454int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j;1455base_dst[dsp_i] += base_src[dsp_i];1456}1457}14581459base_src = fluid_align_ptr(src->fx_right_buf, FLUID_DEFAULT_ALIGNMENT);1460base_dst = fluid_align_ptr(dst->fx_right_buf, FLUID_DEFAULT_ALIGNMENT);14611462for(i = 0; i < minbuf; i++)1463{1464#pragma omp simd aligned(base_dst,base_src:FLUID_DEFAULT_ALIGNMENT)14651466for(j = 0; j < scount; j++)1467{1468int dsp_i = i * FLUID_MIXER_MAX_BUFFERS_DEFAULT * FLUID_BUFSIZE + j;1469base_dst[dsp_i] += base_src[dsp_i];1470}1471}1472}147314741475/**1476* Go through all threads and see if someone is finished for mixing1477*/1478static int1479fluid_mixer_mix_in(fluid_rvoice_mixer_t *mixer, int extra_threads, int current_blockcount)1480{1481int i, result, hasmixed;14821483do1484{1485hasmixed = 0;1486result = 0;14871488for(i = 0; i < extra_threads; i++)1489{1490int j = fluid_atomic_int_get(&mixer->threads[i].ready);14911492switch(j)1493{1494case THREAD_BUF_PROCESSING:1495result = 1;1496break;14971498case THREAD_BUF_VALID:1499fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_NODATA);1500fluid_mixer_buffers_mix(&mixer->buffers, &mixer->threads[i], current_blockcount);1501hasmixed = 1;1502break;1503}1504}1505}1506while(hasmixed);15071508return result;1509}15101511static void1512fluid_render_loop_multithread(fluid_rvoice_mixer_t *mixer, int current_blockcount)1513{1514int i, bufcount;1515fluid_real_t *local_buf = fluid_align_ptr(mixer->buffers.local_buf, FLUID_DEFAULT_ALIGNMENT);15161517FLUID_DECLARE_VLA(fluid_real_t *, bufs,1518mixer->buffers.buf_count * 2 + mixer->buffers.fx_buf_count * 2);1519// How many threads should we start this time?1520int extra_threads = mixer->active_voices / VOICES_PER_THREAD;15211522if(extra_threads > mixer->thread_count)1523{1524extra_threads = mixer->thread_count;1525}15261527if(extra_threads == 0)1528{1529// No extra threads? No thread overhead!1530fluid_render_loop_singlethread(mixer, current_blockcount);1531return;1532}15331534bufcount = fluid_mixer_buffers_prepare(&mixer->buffers, bufs);15351536// Prepare voice list1537fluid_cond_mutex_lock(mixer->wakeup_threads_m);1538fluid_atomic_int_set(&mixer->current_rvoice, 0);15391540for(i = 0; i < extra_threads; i++)1541{1542fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_PROCESSING);1543}15441545// Signal threads to wake up1546fluid_cond_broadcast(mixer->wakeup_threads);1547fluid_cond_mutex_unlock(mixer->wakeup_threads_m);15481549// If thread is finished, mix it in1550while(fluid_mixer_mix_in(mixer, extra_threads, current_blockcount))1551{1552// Otherwise get a voice and render it1553fluid_rvoice_t *rvoice = fluid_mixer_get_mt_rvoice(mixer);15541555if(rvoice != NULL)1556{1557fluid_profile_ref_var(prof_ref);1558fluid_mixer_buffers_render_one(&mixer->buffers, rvoice, bufs, bufcount, local_buf, current_blockcount);1559fluid_profile(FLUID_PROF_ONE_BLOCK_VOICE, prof_ref, 1,1560current_blockcount * FLUID_BUFSIZE);1561//test++;1562}1563else1564{1565// If no voices, wait for mixes. Make sure one is still processing to avoid deadlock1566int is_processing = 0;1567//waits++;1568fluid_cond_mutex_lock(mixer->thread_ready_m);15691570for(i = 0; i < extra_threads; i++)1571{1572if(fluid_atomic_int_get(&mixer->threads[i].ready) ==1573THREAD_BUF_PROCESSING)1574{1575is_processing = 1;1576}1577}15781579if(is_processing)1580{1581fluid_cond_wait(mixer->thread_ready, mixer->thread_ready_m);1582}15831584fluid_cond_mutex_unlock(mixer->thread_ready_m);1585}1586}15871588//FLUID_LOG(FLUID_DBG, "Blockcount: %d, mixed %d of %d voices myself, waits = %d",1589// current_blockcount, test, mixer->active_voices, waits);1590}15911592static void delete_rvoice_mixer_threads(fluid_rvoice_mixer_t *mixer)1593{1594int i;15951596// if no threads have been created yet (e.g. because a previous error prevented creation of threads1597// mutexes and condition variables), skip terminating threads1598if(mixer->thread_count != 0)1599{1600fluid_atomic_int_set(&mixer->threads_should_terminate, 1);1601// Signal threads to wake up1602fluid_cond_mutex_lock(mixer->wakeup_threads_m);16031604for(i = 0; i < mixer->thread_count; i++)1605{1606fluid_atomic_int_set(&mixer->threads[i].ready, THREAD_BUF_TERMINATE);1607}16081609fluid_cond_broadcast(mixer->wakeup_threads);1610fluid_cond_mutex_unlock(mixer->wakeup_threads_m);16111612for(i = 0; i < mixer->thread_count; i++)1613{1614if(mixer->threads[i].thread)1615{1616fluid_thread_join(mixer->threads[i].thread);1617delete_fluid_thread(mixer->threads[i].thread);1618}16191620fluid_mixer_buffers_free(&mixer->threads[i]);1621}1622}16231624FLUID_FREE(mixer->threads);1625mixer->thread_count = 0;1626mixer->threads = NULL;1627}16281629/**1630* Update amount of extra mixer threads.1631* @param thread_count Number of extra mixer threads for multi-core rendering1632* @param prio_level real-time prio level for the extra mixer threads1633*/1634static int fluid_rvoice_mixer_set_threads(fluid_rvoice_mixer_t *mixer, int thread_count, int prio_level)1635{1636char name[16];1637int i;16381639// Kill all existing threads first1640if(mixer->thread_count)1641{1642delete_rvoice_mixer_threads(mixer);1643}16441645if(thread_count == 0)1646{1647return FLUID_OK;1648}16491650// Now prepare the new threads1651fluid_atomic_int_set(&mixer->threads_should_terminate, 0);1652mixer->threads = FLUID_ARRAY(fluid_mixer_buffers_t, thread_count);16531654if(mixer->threads == NULL)1655{1656FLUID_LOG(FLUID_ERR, "Out of memory");1657return FLUID_FAILED;1658}16591660FLUID_MEMSET(mixer->threads, 0, thread_count * sizeof(fluid_mixer_buffers_t));1661mixer->thread_count = thread_count;16621663for(i = 0; i < thread_count; i++)1664{1665fluid_mixer_buffers_t *b = &mixer->threads[i];16661667if(!fluid_mixer_buffers_init(b, mixer))1668{1669return FLUID_FAILED;1670}16711672fluid_atomic_int_set(&b->ready, THREAD_BUF_NODATA);1673FLUID_SNPRINTF(name, sizeof(name), "mixer%d", i);1674b->thread = new_fluid_thread(name, fluid_mixer_thread_func, b, prio_level, 0);16751676if(!b->thread)1677{1678return FLUID_FAILED;1679}1680}16811682return FLUID_OK;1683}1684#endif16851686/**1687* Synthesize audio into buffers1688* @param blockcount number of blocks to render, each having FLUID_BUFSIZE samples1689* @return number of blocks rendered1690*/1691int1692fluid_rvoice_mixer_render(fluid_rvoice_mixer_t *mixer, int blockcount)1693{1694fluid_profile_ref_var(prof_ref);16951696mixer->current_blockcount = blockcount;16971698// Zero buffers1699fluid_mixer_buffers_zero(&mixer->buffers, blockcount);1700fluid_profile(FLUID_PROF_ONE_BLOCK_CLEAR, prof_ref, mixer->active_voices,1701blockcount * FLUID_BUFSIZE);17021703#if ENABLE_MIXER_THREADS17041705if(mixer->thread_count > 0)1706{1707fluid_render_loop_multithread(mixer, blockcount);1708}1709else1710#endif1711{1712fluid_render_loop_singlethread(mixer, blockcount);1713}17141715fluid_profile(FLUID_PROF_ONE_BLOCK_VOICES, prof_ref, mixer->active_voices,1716blockcount * FLUID_BUFSIZE);171717181719// Process reverb & chorus1720fluid_rvoice_mixer_process_fx(mixer, blockcount);17211722// Call the callback and pack active voice array1723fluid_rvoice_mixer_process_finished_voices(mixer);17241725return blockcount;1726}172717281729