+fluid_synth_tuning_iteration_next(fluid_synth_t *synth, int *bank, int *prog)
+{
+ void *pval;
+ int b = 0, p = 0;
+
+ fluid_return_val_if_fail(synth != NULL, 0);
+ fluid_return_val_if_fail(bank != NULL, 0);
+ fluid_return_val_if_fail(prog != NULL, 0);
+ fluid_synth_api_enter(synth);
+
+ /* Current tuning iteration stored as: bank << 8 | program */
+ pval = fluid_private_get(synth->tuning_iter);
+ p = FLUID_POINTER_TO_INT(pval);
+ b = (p >> 8) & 0xFF;
+ p &= 0xFF;
+
+ if(!synth->tuning)
+ {
+ FLUID_API_RETURN(0);
+ }
+
+ for(; b < 128; b++, p = 0)
+ {
+ if(synth->tuning[b] == NULL)
+ {
+ continue;
+ }
+
+ for(; p < 128; p++)
+ {
+ if(synth->tuning[b][p] == NULL)
+ {
+ continue;
+ }
+
+ *bank = b;
+ *prog = p;
+
+ if(p < 127)
+ {
+ fluid_private_set(synth->tuning_iter,
+ FLUID_INT_TO_POINTER(b << 8 | (p + 1)));
+ }
+ else
+ {
+ fluid_private_set(synth->tuning_iter, FLUID_INT_TO_POINTER((b + 1) << 8));
+ }
+
+ FLUID_API_RETURN(1);
+ }
+ }
+
+ FLUID_API_RETURN(0);
+}
+
+/**
+ * Get the entire note tuning for a given MIDI bank and program.
+ * @param synth FluidSynth instance
+ * @param bank MIDI bank number of tuning
+ * @param prog MIDI program number of tuning
+ * @param name Location to store tuning name or NULL to ignore
+ * @param len Maximum number of chars to store to 'name' (including NULL byte)
+ * @param pitch Array to store tuning scale to or NULL to ignore (len of 128)
+ * @return #FLUID_OK if matching tuning was found, #FLUID_FAILED otherwise
+ */
+int
+fluid_synth_tuning_dump(fluid_synth_t *synth, int bank, int prog,
+ char *name, int len, double *pitch)
+{
+ fluid_tuning_t *tuning;
+
+ fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
+ fluid_synth_api_enter(synth);
+
+ tuning = fluid_synth_get_tuning(synth, bank, prog);
+
+ if(tuning)
+ {
+ if(name)
+ {
+ FLUID_SNPRINTF(name, len - 1, "%s", fluid_tuning_get_name(tuning));
+ name[len - 1] = 0; /* make sure the string is null terminated */
+ }
+
+ if(pitch)
+ {
+ FLUID_MEMCPY(pitch, fluid_tuning_get_all(tuning), 128 * sizeof(double));
+ }
+ }
+
+ FLUID_API_RETURN(tuning ? FLUID_OK : FLUID_FAILED);
+}
+
+/**
+ * Get settings assigned to a synth.
+ * @param synth FluidSynth instance
+ * @return FluidSynth settings which are assigned to the synth
+ */
+fluid_settings_t *
+fluid_synth_get_settings(fluid_synth_t *synth)
+{
+ fluid_return_val_if_fail(synth != NULL, NULL);
+
+ return synth->settings;
+}
+
+/**
+ * Set a SoundFont generator (effect) value on a MIDI channel in real-time.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param param SoundFont generator ID (#fluid_gen_type)
+ * @param value Offset or absolute generator value to assign to the MIDI channel
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ *
+ * This function allows for setting all effect parameters in real time on a
+ * MIDI channel. Setting absolute to non-zero will cause the value to override
+ * any generator values set in the instruments played on the MIDI channel.
+ * See SoundFont 2.01 spec, paragraph 8.1.3, page 48 for details on SoundFont
+ * generator parameters and valid ranges.
+ */
+int fluid_synth_set_gen(fluid_synth_t *synth, int chan, int param, float value)
+{
+ return fluid_synth_set_gen2(synth, chan, param, value, FALSE, FALSE);
+}
+
+/**
+ * Set a SoundFont generator (effect) value on a MIDI channel in real-time.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param param SoundFont generator ID (#fluid_gen_type)
+ * @param value Offset or absolute generator value to assign to the MIDI channel
+ * @param absolute FALSE to assign a relative value, TRUE to assign an absolute value
+ * @param normalized FALSE if value is specified in the native units of the generator,
+ * TRUE to take the value as a 0.0-1.0 range and apply it to the valid
+ * generator effect range (scaled and shifted as necessary).
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ *
+ * This function allows for setting all effect parameters in real time on a
+ * MIDI channel. Setting absolute to non-zero will cause the value to override
+ * any generator values set in the instruments played on the MIDI channel.
+ * See SoundFont 2.01 spec, paragraph 8.1.3, page 48 for details on SoundFont
+ * generator parameters and valid ranges.
+ */
+int
+fluid_synth_set_gen2(fluid_synth_t *synth, int chan, int param,
+ float value, int absolute, int normalized)
+{
+ float v;
+ fluid_return_val_if_fail(param >= 0 && param < GEN_LAST, FLUID_FAILED);
+ FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+ v = normalized ? fluid_gen_scale(param, value) : value;
+
+ fluid_synth_set_gen_LOCAL(synth, chan, param, v, absolute);
+
+ FLUID_API_RETURN(FLUID_OK);
+}
+
+/* Synthesis thread local set gen function */
+static void
+fluid_synth_set_gen_LOCAL(fluid_synth_t *synth, int chan, int param, float value,
+ int absolute)
+{
+ fluid_voice_t *voice;
+ int i;
+
+ fluid_channel_set_gen(synth->channel[chan], param, value, absolute);
+
+ for(i = 0; i < synth->polyphony; i++)
+ {
+ voice = synth->voice[i];
+
+ if(fluid_voice_get_channel(voice) == chan)
+ {
+ fluid_voice_set_param(voice, param, value, absolute);
+ }
+ }
+}
+
+/**
+ * Get generator value assigned to a MIDI channel.
+ * @param synth FluidSynth instance
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param param SoundFont generator ID (#fluid_gen_type)
+ * @return Current generator value assigned to MIDI channel
+ */
+float
+fluid_synth_get_gen(fluid_synth_t *synth, int chan, int param)
+{
+ float result;
+ fluid_return_val_if_fail(param >= 0 && param < GEN_LAST, FLUID_FAILED);
+ FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+
+ result = fluid_channel_get_gen(synth->channel[chan], param);
+ FLUID_API_RETURN(result);
+}
+
+/**
+ * Handle MIDI event from MIDI router, used as a callback function.
+ * @param data FluidSynth instance
+ * @param event MIDI event to handle
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ */
+int
+fluid_synth_handle_midi_event(void *data, fluid_midi_event_t *event)
+{
+ fluid_synth_t *synth = (fluid_synth_t *) data;
+ int type = fluid_midi_event_get_type(event);
+ int chan = fluid_midi_event_get_channel(event);
+
+ switch(type)
+ {
+ case NOTE_ON:
+ return fluid_synth_noteon(synth, chan,
+ fluid_midi_event_get_key(event),
+ fluid_midi_event_get_velocity(event));
+
+ case NOTE_OFF:
+ return fluid_synth_noteoff(synth, chan, fluid_midi_event_get_key(event));
+
+ case CONTROL_CHANGE:
+ return fluid_synth_cc(synth, chan,
+ fluid_midi_event_get_control(event),
+ fluid_midi_event_get_value(event));
+
+ case PROGRAM_CHANGE:
+ return fluid_synth_program_change(synth, chan, fluid_midi_event_get_program(event));
+
+ case CHANNEL_PRESSURE:
+ return fluid_synth_channel_pressure(synth, chan, fluid_midi_event_get_program(event));
+
+ case KEY_PRESSURE:
+ return fluid_synth_key_pressure(synth, chan,
+ fluid_midi_event_get_key(event),
+ fluid_midi_event_get_value(event));
+
+ case PITCH_BEND:
+ return fluid_synth_pitch_bend(synth, chan, fluid_midi_event_get_pitch(event));
+
+ case MIDI_SYSTEM_RESET:
+ return fluid_synth_system_reset(synth);
+
+ case MIDI_SYSEX:
+ return fluid_synth_sysex(synth, event->paramptr, event->param1, NULL, NULL, NULL, FALSE);
+
+ case MIDI_TEXT:
+ case MIDI_LYRIC:
+ case MIDI_SET_TEMPO:
+ return FLUID_OK;
+ }
+
+ return FLUID_FAILED;
+}
+
+/**
+ * Create and start voices using a preset and a MIDI note on event.
+ * @param synth FluidSynth instance
+ * @param id Voice group ID to use (can be used with fluid_synth_stop()).
+ * @param preset Preset to synthesize
+ * @param audio_chan Unused currently, set to 0
+ * @param chan MIDI channel number (0 to MIDI channel count - 1)
+ * @param key MIDI note number (0-127)
+ * @param vel MIDI velocity number (1-127)
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ *
+ * @note Should only be called from within synthesis thread, which includes
+ * SoundFont loader preset noteon method.
+ */
+int
+fluid_synth_start(fluid_synth_t *synth, unsigned int id, fluid_preset_t *preset,
+ int audio_chan, int chan, int key, int vel)
+{
+ int result;
+ fluid_return_val_if_fail(preset != NULL, FLUID_FAILED);
+ fluid_return_val_if_fail(key >= 0 && key <= 127, FLUID_FAILED);
+ fluid_return_val_if_fail(vel >= 1 && vel <= 127, FLUID_FAILED);
+ FLUID_API_ENTRY_CHAN(FLUID_FAILED);
+ synth->storeid = id;
+ result = fluid_preset_noteon(preset, synth, chan, key, vel);
+ FLUID_API_RETURN(result);
+}
+
+/**
+ * Stop notes for a given note event voice ID.
+ * @param synth FluidSynth instance
+ * @param id Voice note event ID
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ *
+ * @note In FluidSynth versions prior to 1.1.0 #FLUID_FAILED would be returned
+ * if no matching voice note event ID was found. Versions after 1.1.0 only
+ * return #FLUID_FAILED if an error occurs.
+ */
+int
+fluid_synth_stop(fluid_synth_t *synth, unsigned int id)
+{
+ int result;
+ fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
+ fluid_synth_api_enter(synth);
+ fluid_synth_stop_LOCAL(synth, id);
+ result = FLUID_OK;
+ FLUID_API_RETURN(result);
+}
+
+/* Local synthesis thread variant of fluid_synth_stop */
+static void
+fluid_synth_stop_LOCAL(fluid_synth_t *synth, unsigned int id)
+{
+ fluid_voice_t *voice;
+ int i;
+
+ for(i = 0; i < synth->polyphony; i++)
+ {
+ voice = synth->voice[i];
+
+ if(fluid_voice_is_on(voice) && (fluid_voice_get_id(voice) == id))
+ {
+ fluid_voice_noteoff(voice);
+ }
+ }
+}
+
+/**
+ * Offset the bank numbers of a loaded SoundFont, i.e.\ subtract
+ * \c offset from any bank number when assigning instruments.
+ *
+ * @param synth FluidSynth instance
+ * @param sfont_id ID of a loaded SoundFont
+ * @param offset Bank offset value to apply to all instruments
+ * @return #FLUID_OK if the offset was set successfully, #FLUID_FAILED otherwise
+ */
+int
+fluid_synth_set_bank_offset(fluid_synth_t *synth, int sfont_id, int offset)
+{
+ fluid_sfont_t *sfont;
+ fluid_list_t *list;
+
+ fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
+ fluid_synth_api_enter(synth);
+
+ for(list = synth->sfont; list; list = fluid_list_next(list))
+ {
+ sfont = fluid_list_get(list);
+
+ if(fluid_sfont_get_id(sfont) == sfont_id)
+ {
+ sfont->bankofs = offset;
+ break;
+ }
+ }
+
+ if(!list)
+ {
+ FLUID_LOG(FLUID_ERR, "No SoundFont with id = %d", sfont_id);
+ FLUID_API_RETURN(FLUID_FAILED);
+ }
+
+ FLUID_API_RETURN(FLUID_OK);
+}
+
+/**
+ * Get bank offset of a loaded SoundFont.
+ * @param synth FluidSynth instance
+ * @param sfont_id ID of a loaded SoundFont
+ * @return SoundFont bank offset value
+ */
+int
+fluid_synth_get_bank_offset(fluid_synth_t *synth, int sfont_id)