questzdoom/Projects/Android/jni/SupportLibs/fluidsynth/fluid_midi_router.c
2021-04-20 21:09:02 +01:00

889 lines
26 KiB
C

/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
/* Author: Markus Nentwig, nentwig@users.sourceforge.net
*/
#include "fluid_midi_router.h"
#include "fluid_midi.h"
#include "fluid_synth.h"
#include "fluid_io.h"
/**
* Create a new midi router.
* @param settings Settings used to configure MIDI router
* @param handler MIDI event callback
* @param event_handler_data Caller defined data pointer which gets passed to 'handler'
* @return New MIDI router instance or NULL on error
*
* A midi handler connects to a midi input
* device and forwards incoming midi events to the synthesizer.
*/
fluid_midi_router_t*
new_fluid_midi_router(fluid_settings_t* settings, handle_midi_event_func_t handler, void* event_handler_data)
{
fluid_midi_router_t* router=NULL;
fluid_midi_router_rule_t* rule=NULL;
/* create the router */
router = FLUID_NEW(fluid_midi_router_t); if (router == NULL){
FLUID_LOG(FLUID_ERR, "Out of memory");
return NULL;
};
/* Clear the router, so that error_recovery can safely free all the rules.
* Dump functions are also NULLed.
*/
FLUID_MEMSET(router, 0, sizeof(fluid_midi_router_t));
/* Retrieve the number of MIDI channels for range limiting */
fluid_settings_getint(settings, "synth.midi-channels", &router->nr_midi_channels);
fluid_mutex_init(router->ruletables_mutex);
router->synth = (fluid_synth_t*) event_handler_data;
router->event_handler=handler;
router->event_handler_data=event_handler_data;
/* Create the default routing rules
* They accept events on any channel for any range of parameters,
* and route them unchanged ("result=par*1.0+0") */
if (fluid_midi_router_create_default_rules(router) != FLUID_OK) goto error_recovery;
return router;
error_recovery:
FLUID_LOG(FLUID_ERR, "new_fluid_midi_router failed");
fluid_midi_router_destroy_all_rules(router);
FLUID_FREE(router);
return NULL;
}
/**
* Delete a MIDI router instance.
* @param router MIDI router to delete
* @return Always returns 0
*/
int
delete_fluid_midi_router(fluid_midi_router_t* router)
{
if (router == NULL) {
return FLUID_OK;
}
fluid_midi_router_destroy_all_rules(router);
FLUID_FREE(router);
return FLUID_OK;
}
/*
* fluid_midi_router_destroy_all_rules(fluid_midi_router_t* router)
* Purpose:
* Frees the used memory. This is used only for shutdown!
*/
void fluid_midi_router_destroy_all_rules(fluid_midi_router_t* router){
fluid_midi_router_rule_t* rules[6];
fluid_midi_router_rule_t* current_rule;
fluid_midi_router_rule_t* next_rule;
int i;
rules[0]=router->note_rules;
rules[1]=router->cc_rules;
rules[2]=router->progchange_rules;
rules[3]=router->pitchbend_rules;
rules[4]=router->channel_pressure_rules;
rules[5]=router->key_pressure_rules;
for (i=0; i < 6; i++){
current_rule=rules[i];
while (current_rule){
next_rule=current_rule->next;
FLUID_FREE(current_rule);
current_rule=next_rule;
};
};
}
/*
* new_fluid_midi_router_rule
*/
fluid_midi_router_rule_t* new_fluid_midi_router_rule(void)
{
fluid_midi_router_rule_t* rule=FLUID_NEW(fluid_midi_router_rule_t);
if (rule == NULL) {
FLUID_LOG(FLUID_ERR, "Out of memory");
return NULL;
}
FLUID_MEMSET(rule, 0, sizeof(fluid_midi_router_rule_t));
return rule;
};
/*
* delete_fluid_midi_router_rule
*/
int
delete_fluid_midi_router_rule(fluid_midi_router_rule_t* rule)
{
FLUID_FREE(rule);
return FLUID_OK;
}
int fluid_midi_router_create_default_rules(fluid_midi_router_t* router)
{
fluid_midi_router_rule_t** rules[6];
int i;
rules[0]=&router->note_rules;
rules[1]=&router->cc_rules;
rules[2]=&router->progchange_rules;
rules[3]=&router->pitchbend_rules;
rules[4]=&router->channel_pressure_rules;
rules[5]=&router->key_pressure_rules;
for (i=0; i < 6; i++){
/* Start a new rule. rules[i] is the destination. _end will insert
* a new rule there into the linked list.
*/
if (fluid_midi_router_begin(router, rules[i]) != FLUID_OK) goto error_recovery;
if (fluid_midi_router_end(router) != FLUID_OK) goto error_recovery;
};
return FLUID_OK;
error_recovery:
FLUID_LOG(FLUID_ERR, "fluid_midi_router_create_default_rules failed");
return FLUID_FAILED;
};
/* Purpose:
* Resets the new-rule registers in 'router' to their default values.
* Stores the destination (the linked list into which the new rule is inserted by _end)
*/
int fluid_midi_router_begin(fluid_midi_router_t* router, fluid_midi_router_rule_t** dest)
{
if (!dest) goto error_recovery;
router->dest=dest;
/* In the days before the router, MIDI data went straight to the synth.
* So there is no need to check for reasonable values here.
*/
router->new_rule_chan_min=0;
router->new_rule_chan_max=999999;
router->new_rule_chan_mul=1.;
router->new_rule_chan_add=0;
router->new_rule_par1_min=0;
router->new_rule_par1_max=999999;
router->new_rule_par1_mul=1.;
router->new_rule_par1_add=0;
router->new_rule_par2_min=0;
router->new_rule_par2_max=999999;
router->new_rule_par2_mul=1.;
router->new_rule_par2_add=0;
return FLUID_OK;
error_recovery:
FLUID_LOG(FLUID_ERR, "fluid_midi_router_begin failed");
return FLUID_FAILED;
};
/* Purpose:
* Concludes a sequence of commands:
* - router_start type
* - router_chan
* - router_par1
* - router_par2
* - router_end (this call).
* Creates a new event from the given parameters and stores it in
* the correct list.
*/
int fluid_midi_router_end(fluid_midi_router_t* router){
fluid_midi_router_rule_t* rule=new_fluid_midi_router_rule(); if (rule == NULL) goto error_recovery;
rule->chan_min=router->new_rule_chan_min;
rule->chan_max=router->new_rule_chan_max;
rule->chan_mul=router->new_rule_chan_mul;
rule->chan_add=router->new_rule_chan_add;
rule->par1_min=router->new_rule_par1_min;
rule->par1_max=router->new_rule_par1_max;
rule->par1_mul=router->new_rule_par1_mul;
rule->par1_add=router->new_rule_par1_add;
rule->par2_min=router->new_rule_par2_min;
rule->par2_max=router->new_rule_par2_max;
rule->par2_mul=router->new_rule_par2_mul;
rule->par2_add=router->new_rule_par2_add;
/* Now we modify the rules table. Lock it to make sure, that the RT
* thread is not in the middle of an event. Also prevent, that some
* other thread modifies the table at the same time.
*/
fluid_mutex_lock(router->ruletables_mutex);
rule->next= *(router->dest);
*router->dest=rule;
fluid_mutex_unlock(router->ruletables_mutex);
return FLUID_OK;
error_recovery:
FLUID_LOG(FLUID_ERR, "fluid_midi_router_end failed");
delete_fluid_midi_router_rule(rule);
return FLUID_FAILED;
};
/**
* Handle a MIDI event through a MIDI router instance.
* @param data MIDI router instance #fluid_midi_router_t (DOCME why is it a void *?)
* @param event MIDI event to handle
* @return 0 on success, -1 otherwise
*
* Purpose: The midi router is called for each event, that is received
* via the 'physical' midi input. Each event can trigger an arbitrary number
* of generated events.
*
* In default mode, a noteon event is just forwarded to the synth's 'noteon' function,
* a 'CC' event to the synth's 'CC' function and so on.
*
* The router can be used to
* - filter messages (for example: Pass sustain pedal CCs only to selected channels),
* - split the keyboard (noteon with notenr < x: to ch 1, >x to ch 2),
* - layer sounds (for each noteon received on ch 1, create a noteon on ch1, ch2, ch3,...)
* - velocity scaling (for each noteon event, scale the velocity by 1.27 to give DX7 users
* a chance)
* - velocity switching ("v <=100: Angel Choir; V > 100: Hell's Bells")
* - get rid of aftertouch
* - ...
*/
int
fluid_midi_router_handle_midi_event(void* data, fluid_midi_event_t* event)
{
fluid_midi_router_t* router=(fluid_midi_router_t*)data;
fluid_synth_t* synth = router->synth;
fluid_midi_router_rule_t* rule=NULL;
fluid_midi_router_rule_t* next_rule=NULL;
int event_has_par2=0; /* Flag, indicates that current event needs two parameters */
int negative_event=0; /* Flag, indicates that current event releases a key / pedal */
int par1_max=127; /* Range limit for par1 */
int par2_max=127; /* Range limit for par2 */
int ret_val=FLUID_OK;
/* Some keyboards report noteoff through a noteon event with vel=0.
* Convert those to noteoff to ease processing.*/
if (event->type == NOTE_ON && event->param2 == 0) {
/* Channel: Remains the same
* Param 1: Note number, remains the same
* Param 2: release velocity), set to max
*/
event->type = NOTE_OFF;
event->param2=127;
};
/* Lock the rules table, so that for example the shell thread doesn't
* clear the rules we are just working with */
fluid_mutex_lock(router->ruletables_mutex);
/* Depending on the event type, choose the correct table of rules.
* Also invoke the appropriate callback function for the event type
* to notify external applications.
* Note: An event type not listed here simply won't find any rules and
* will be ignored.
*/
switch (event->type) {
case NOTE_ON:
rule=router->note_rules;
event_has_par2=1;
break;
case NOTE_OFF:
rule=router->note_rules;
event_has_par2=1;
break;
case CONTROL_CHANGE:
rule=router->cc_rules;
event_has_par2=1;
break;
case PROGRAM_CHANGE:
rule=router->progchange_rules;
break;
case PITCH_BEND:
rule=router->pitchbend_rules;
par1_max=16383;
break;
case CHANNEL_PRESSURE:
rule=router->channel_pressure_rules;
break;
case KEY_PRESSURE:
rule=router->key_pressure_rules;
event_has_par2=1;
break;
case MIDI_SYSTEM_RESET:
return router->event_handler(router->event_handler_data,event);
default:
break;
}
/* At this point 'rule' contains the first rule in a linked list.
* Check for all rules, whether channel and parameter ranges match.
*/
while (rule){
int chan; /* Channel of the generated event */
int par1; /* par1 of the generated event */
int par2=0;
int event_par1=(int)event->param1;
int event_par2=(int)event->param2;
fluid_midi_event_t new_event;
/* Store the pointer to the next rule right now. If the rule is later flagged for destruction,
* it may not be accessed anymore.
*/
next_rule=rule->next;
/* Check, whether the rule is still active. Expired rules cannot be removed immediately,
* because freeing memory in a realtime thread is no good idea. And the MIDI thread,
* which calls us here, has raised priority.*/
if (rule->state == MIDIRULE_DONE){
goto do_next_rule;
};
/* Channel window */
if (rule->chan_min > rule->chan_max){
/* Inverted rule: Exclude everything between max and min (but not min/max) */
if (event->channel > rule->chan_max && event->channel < rule->chan_min){
goto do_next_rule;
}
} else {
/* Normal rule: Exclude everything < max or > min (but not min/max) */
if (event->channel > rule->chan_max || event->channel < rule->chan_min){
goto do_next_rule;
}
};
/* Par 1 window */
if (rule->par1_min > rule->par1_max){
/* Inverted rule: Exclude everything between max and min (but not min/max) */
if (event_par1 > rule->par1_max && event_par1 < rule->par1_min){
goto do_next_rule;
}
} else {
/* Normal rule: Exclude everything < max or > min (but not min/max)*/
if (event_par1 > rule->par1_max || event_par1 < rule->par1_min){
goto do_next_rule;
}
};
/* Par 2 window (only applies to event types, which have 2 pars)
* For noteoff events, velocity switching doesn't make any sense.
* Velocity scaling might be useful, though.
*/
if (event_has_par2 && event->type != NOTE_OFF){
if (rule->par2_min > rule->par2_max){
/* Inverted rule: Exclude everything between max and min (but not min/max) */
if (event_par2 > rule->par2_max && event_par2 < rule->par2_min){
goto do_next_rule;
}
} else {
/* Normal rule: Exclude everything < max or > min (but not min/max)*/
if (event_par2 > rule->par2_max || event_par2 < rule->par2_min){
goto do_next_rule;
};
};
};
/* Channel scaling / offset
* Note: rule->chan_mul will probably be 0 or 1. If it's 0, input from all
* input channels is mapped to the same synth channel.
*/
chan=(int)((fluid_real_t)event->channel * (fluid_real_t)rule->chan_mul + (fluid_real_t)rule->chan_add + 0.5);
/* Par 1 scaling / offset */
par1=(int)((fluid_real_t)event_par1 * (fluid_real_t)rule->par1_mul + (fluid_real_t)rule->par1_add + 0.5);
/* Par 2 scaling / offset, if applicable */
if (event_has_par2){
par2=(int)((fluid_real_t)event_par2 * (fluid_real_t)rule->par2_mul + (fluid_real_t)rule->par2_add + 0.5);
};
/* Channel range limiting */
if (chan < 0){
chan=0;
/* Upper limit is hard to implement, because the number of MIDI channels can be changed via settings any time. */
};
/* Par1 range limiting */
if (par1 < 0){
par1=0;
} else if (par1 > par1_max){
par1=par1_max;
};
/* Par2 range limiting */
if (event_has_par2){
if (par2 < 0){
par2=0;
} else if (par2 > par2_max){
par2=par2_max;
};
};
/* At this point we have to create an event of event->type on 'chan' with par1 (maybe par2).
* We keep track on the state of noteon and sustain pedal events. If the application tries
* to delete a rule, it will only be fully removed, if pending noteoff / pedal off events have
* arrived. In the meantime (MIDIRULE_WAITING) state, it will only let through 'negative' events
* (noteoff or pedal up).
*/
if (
event->type == NOTE_ON
|| (event->type == CONTROL_CHANGE && par1 == SUSTAIN_SWITCH && par2 >= 64)
){
/* Noteon or sustain pedal down event generated */
if (rule->keys_cc[par1] == 0){
rule->keys_cc[par1]=1;
rule->pending_events++;
};
} else if (
event->type == NOTE_OFF
|| (event->type == CONTROL_CHANGE && par1 == SUSTAIN_SWITCH && par2 < 64)
){
/* Noteoff or sustain pedal up event generated */
if (rule->keys_cc[par1] > 0){
rule->keys_cc[par1]=0;
rule->pending_events--;
negative_event=1;
};
};
if (rule->state == MIDIRULE_WAITING){
if (negative_event){
if (rule->pending_events == 0){
/* There are no more pending events in the rule - all keys are up.
* Change its state to 'unused', it will be cleared up at the next
* opportunity to run 'free' safely.
* Note: After this, the rule may disappear at any time
*/
rule->state=MIDIRULE_DONE;
};
} else {
/* This rule only exists because of some unfinished business:
* There is still a note or sustain pedal waiting to be
* released. But the event that came in does not release any.
* So we just ignore the event and go on with the next rule.
*/
goto do_next_rule;
};
};
/* At this point it is decided, what is sent to the synth.
* Create a new event and make the appropriate call
*/
fluid_midi_event_set_type(&new_event, event->type);
fluid_midi_event_set_channel(&new_event, chan);
/* fluid_midi_event_set_param1(&new_event, par1); */
/* fluid_midi_event_set_param2(&new_event, par2); */
new_event.param1 = par1;
new_event.param2 = par2;
/* Don't know what to do with return values: What is, if
* generating one event fails? Current solution: Make all calls,
* regardless of failures, but report failure to the caller, as
* soon as only one call fails. Strategy should be thought
* through.
*/
if (router->event_handler(router->event_handler_data, &new_event) != FLUID_OK) {
ret_val=FLUID_FAILED;
}
do_next_rule:
rule=next_rule;
};
/* We are finished with the rule tables. Allow the other threads to do their job. */
fluid_mutex_unlock(router->ruletables_mutex);
return ret_val;
}
int fluid_midi_router_handle_clear(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
{
fluid_midi_router_t* router=synth->midi_router;
if (ac != 0) {
fluid_ostream_printf(out, "router_clear needs no arguments.\n");
goto error_recovery;
}
/* Disable rules and mark for destruction */
fluid_midi_router_disable_all_rules(router);
/* Free unused rules */
fluid_midi_router_free_unused_rules(router);
return 0;
error_recovery:
return -1;
};
int fluid_midi_router_handle_default(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
{
fluid_midi_router_t* router=synth->midi_router;
if (ac != 0) {
fluid_ostream_printf(out, "router_default needs no arguments.\n");
return -1;
}
/* Disable rules and mark for destruction */
fluid_midi_router_disable_all_rules(router);
/* Create default rules */
if (fluid_midi_router_create_default_rules(router) != FLUID_OK){
FLUID_LOG(FLUID_ERR, "create_default_rules failed");
goto error_recovery;
};
/* Free unused rules */
fluid_midi_router_free_unused_rules(router);
return 0;
error_recovery:
return -1;
};
int fluid_midi_router_handle_begin(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
{
fluid_midi_router_t* router=synth->midi_router;
fluid_midi_router_rule_t** dest=NULL;
if (ac != 1) {
fluid_ostream_printf(out, "router_begin needs no arguments.\n");
goto error_recovery;
}
if (FLUID_STRCMP(av[0],"note") == 0){
dest=& router->note_rules;
} else if (FLUID_STRCMP(av[0],"cc") == 0){
dest=& router->cc_rules;
} else if (FLUID_STRCMP(av[0],"prog") == 0){
dest=& router->progchange_rules;
} else if (FLUID_STRCMP(av[0],"pbend") == 0){
dest=& router->pitchbend_rules;
} else if (FLUID_STRCMP(av[0],"cpress") == 0){
dest=& router->channel_pressure_rules;
} else if (FLUID_STRCMP(av[0],"kpress") == 0){
dest=& router->key_pressure_rules;
};
if (dest == NULL){
fluid_ostream_printf(out, "router_begin args: note, cc, prog, pbend, cpress, kpress\n");
goto error_recovery;
};
if (fluid_midi_router_begin(router, dest) != FLUID_OK){
goto error_recovery;
};
/* Free unused rules (give it a try) */
fluid_midi_router_free_unused_rules(router);
return 0;
error_recovery:
return -1;
};
int fluid_midi_router_handle_end(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
{
fluid_midi_router_t* router=synth->midi_router;
if (ac != 0) {
fluid_ostream_printf(out, "router_end needs no arguments.");
goto error_recovery;
}
if (fluid_midi_router_end(router) != FLUID_OK){
FLUID_LOG(FLUID_ERR, "midi_router_end failed");
goto error_recovery;
};
/* Free unused rules (give it a try) */
fluid_midi_router_free_unused_rules(router);
return 0;
error_recovery:
return -1;
};
int fluid_midi_router_handle_chan(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
{
fluid_midi_router_t* router=synth->midi_router;
if (ac != 4) {
fluid_ostream_printf(out, "router_chan needs four args: min, max, mul, add.");
goto error_recovery;
}
router->new_rule_chan_min=atoi(av[0]);
router->new_rule_chan_max=atoi(av[1]);
router->new_rule_chan_mul=atoi(av[2]);
router->new_rule_chan_add=atoi(av[3]);
/* Free unused rules (give it a try) */
fluid_midi_router_free_unused_rules(router);
return 0;
error_recovery:
return -1;
};
int fluid_midi_router_handle_par1(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
{
fluid_midi_router_t* router=synth->midi_router;
if (ac != 4) {
fluid_ostream_printf(out, "router_par1 needs four args: min, max, mul, add.");
goto error_recovery;
}
router->new_rule_par1_min=atoi(av[0]);
router->new_rule_par1_max=atoi(av[1]);
router->new_rule_par1_mul=atoi(av[2]);
router->new_rule_par1_add=atoi(av[3]);
/* Free unused rules (give it a try) */
fluid_midi_router_free_unused_rules(router);
return 0;
error_recovery:
return -1;
};
int fluid_midi_router_handle_par2(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
{
fluid_midi_router_t* router=synth->midi_router;
if (ac != 4) {
fluid_ostream_printf(out, "router_par2 needs four args: min, max, mul, add.");
goto error_recovery;
}
router->new_rule_par2_min=atoi(av[0]);
router->new_rule_par2_max=atoi(av[1]);
router->new_rule_par2_mul=atoi(av[2]);
router->new_rule_par2_add=atoi(av[3]);
/* Free unused rules (give it a try) */
fluid_midi_router_free_unused_rules(router);
return 0;
error_recovery:
return -1;
};
void fluid_midi_router_disable_all_rules(fluid_midi_router_t* router)
{
/* Go through all rules. If the rule has no outstanding events, then
* flag it for destruction. Otherwise change it to wait mode. Then
* it will handle the pending events, and flag itself for destruction
* as soon as all events have arrived.
*/
fluid_midi_router_rule_t* rules[6];
fluid_midi_router_rule_t* current_rule;
int i;
rules[0]=router->note_rules;
rules[1]=router->cc_rules;
rules[2]=router->progchange_rules;
rules[3]=router->pitchbend_rules;
rules[4]=router->channel_pressure_rules;
rules[5]=router->key_pressure_rules;
/* Lock the rules table. We live on the assumption, that the rules
* table does not change while we are chewing at it.
* Changes between the processing of note / cc etc are permitted.
*/
for (i=0; i < 6; i++){
fluid_mutex_lock(router->ruletables_mutex);
current_rule=rules[i];
while (current_rule){
if (current_rule->pending_events == 0){
/* Flag for destruction */
current_rule->state=MIDIRULE_DONE;
} else {
/* Wait mode */
current_rule->state=MIDIRULE_WAITING;
};
current_rule=current_rule->next;
};
fluid_mutex_unlock(router->ruletables_mutex);
};
};
void fluid_midi_router_free_unused_rules(fluid_midi_router_t* router)
{
int i;
for (i=0; i < 6; i++){
fluid_midi_router_rule_t** p=NULL;
/* We assume, that the table does not change while we are at it.
* Between different types (note, cc etc) we can allow changes.
*/
fluid_mutex_lock(router->ruletables_mutex);
switch(i){
case 0:
p=&router->note_rules;
break;
case 1:
p=&router->cc_rules;
break;
case 2:
p=&router->progchange_rules;
break;
case 3:
p=&router->pitchbend_rules;
break;
case 4:
p=&router->channel_pressure_rules;
break;
case 5:
p=&router->key_pressure_rules;
break;
default:
break;
};
while (*p){
fluid_midi_router_rule_t* current_rule=*p;
fluid_midi_router_rule_t* next_rule=current_rule->next;
if (current_rule->state == MIDIRULE_DONE){
/* p points to current_rule.
* current_rule->next points to next_rule.
* Unlink current_rule from the chain by setting the content
* of p to next_rule.
*/
*p=next_rule;
/* Now the rule is not in the chain anymore. Destroy it. */
delete_fluid_midi_router_rule(current_rule);
} else {
/* We have to keep the rule, there is still unfinished business. */
p = &current_rule->next;
};
};
fluid_mutex_unlock(router->ruletables_mutex);
};
};
/**
* MIDI event callback function to display event information to stdout
* @param data MIDI router instance
* @param event MIDI event data
* @return 0 on success, -1 otherwise
*
* An implementation of the #handle_midi_event_func_t function type, used for
* displaying MIDI event information between the MIDI driver and router to
* stdout. Useful for adding into a MIDI router chain for debugging MIDI events.
*/
int fluid_midi_dump_prerouter(void* data, fluid_midi_event_t* event)
{
switch (event->type) {
case NOTE_ON:
fprintf(stdout, "event_pre_noteon %i %i %i\n",
event->channel, event->param1, event->param2);
break;
case NOTE_OFF:
fprintf(stdout, "event_pre_noteoff %i %i %i\n",
event->channel, event->param1, event->param2);
break;
case CONTROL_CHANGE:
fprintf(stdout, "event_pre_cc %i %i %i\n",
event->channel, event->param1, event->param2);
break;
case PROGRAM_CHANGE:
fprintf(stdout, "event_pre_prog %i %i\n", event->channel, event->param1);
break;
case PITCH_BEND:
fprintf(stdout, "event_pre_pitch %i %i\n", event->channel, event->param1);
break;
case CHANNEL_PRESSURE:
fprintf(stdout, "event_pre_cpress %i %i\n", event->channel, event->param1);
break;
case KEY_PRESSURE:
fprintf(stdout, "event_pre_kpress %i %i %i\n",
event->channel, event->param1, event->param2);
break;
default:
break;
};
return fluid_midi_router_handle_midi_event((fluid_midi_router_t*) data, event);
};
/**
* MIDI event callback function to display event information to stdout
* @param data MIDI router instance
* @param event MIDI event data
* @return 0 on success, -1 otherwise
*
* An implementation of the #handle_midi_event_func_t function type, used for
* displaying MIDI event information between the MIDI driver and router to
* stdout. Useful for adding into a MIDI router chain for debugging MIDI events.
*/
int fluid_midi_dump_postrouter(void* data, fluid_midi_event_t* event)
{
switch (event->type) {
case NOTE_ON:
fprintf(stdout, "event_post_noteon %i %i %i\n",
event->channel, event->param1, event->param2);
break;
case NOTE_OFF:
fprintf(stdout, "event_post_noteoff %i %i %i\n",
event->channel, event->param1, event->param2);
break;
case CONTROL_CHANGE:
fprintf(stdout, "event_post_cc %i %i %i\n",
event->channel, event->param1, event->param2);
break;
case PROGRAM_CHANGE:
fprintf(stdout, "event_post_prog %i %i\n", event->channel, event->param1);
break;
case PITCH_BEND:
fprintf(stdout, "event_post_pitch %i %i\n", event->channel, event->param1);
break;
case CHANNEL_PRESSURE:
fprintf(stdout, "event_post_cpress %i %i\n", event->channel, event->param1);
break;
case KEY_PRESSURE:
fprintf(stdout, "event_post_kpress %i %i %i\n",
event->channel, event->param1, event->param2);
break;
default:
break;
};
return fluid_synth_handle_midi_event((fluid_synth_t*) data, event);
};