# src/fluid_midi_router.c

    00001 /* FluidSynth - A Software Synthesizer
    00002  *
    00003  * Copyright (C) 2003  Peter Hanappe and others.
    00004  *
    00005  * This library is free software; you can redistribute it and/or
    00006  * modify it under the terms of the GNU Library General Public License
    00007  * as published by the Free Software Foundation; either version 2 of
    00008  * the License, or (at your option) any later version.
    00009  *
    00010  * This library is distributed in the hope that it will be useful, but
    00011  * WITHOUT ANY WARRANTY; without even the implied warranty of
    00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    00013  * Library General Public License for more details.
    00014  *
    00015  * You should have received a copy of the GNU Library General Public
    00016  * License along with this library; if not, write to the Free
    00017  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    00018  * 02111-1307, USA
    00019  */
    00020
    00021 /* Author: Markus Nentwig, nentwig@users.sourceforge.net
    00022  */
    00023
    00024 #include "fluid_midi_router.h"
    00025 #include "fluid_midi.h"
    00026 #include "fluid_synth.h"
    00027 #include "fluid_io.h"
    00028
    00039 fluid_midi_router_t*
    00040 new_fluid_midi_router(fluid_settings_t* settings, handle_midi_event_func_t handler, void* event_handler_data)
    00041 {
    00042   fluid_midi_router_t* router=NULL;
    00043   fluid_midi_router_rule_t* rule=NULL;
    00044
    00045   /* create the router */
    00046   router = FLUID_NEW(fluid_midi_router_t); if (router == NULL){
    00047     FLUID_LOG(FLUID_ERR, "Out of memory");
    00048     return NULL;
    00049   };
    00050
    00051   /* Clear the router, so that error_recovery can safely free all the rules.
    00052    * Dump functions are also NULLed.
    00053    */
    00054   FLUID_MEMSET(router, 0, sizeof(fluid_midi_router_t));
    00055   /* Retrieve the number of MIDI channels for range limiting */
    00056   fluid_settings_getint(settings, "synth.midi-channels", &router->nr_midi_channels);
    00057
    00058   fluid_mutex_init(router->ruletables_mutex);
    00059
    00060   router->synth = (fluid_synth_t*) event_handler_data;
    00061   router->event_handler=handler;
    00062   router->event_handler_data=event_handler_data;
    00063
    00064   /* Create the default routing rules
    00065   * They accept events on any channel for any range of parameters,
    00066   * and route them unchanged ("result=par*1.0+0") */
    00067
    00068   if (fluid_midi_router_create_default_rules(router) != FLUID_OK) goto error_recovery;
    00069
    00070   return router;
    00071
    00072  error_recovery:
    00073   FLUID_LOG(FLUID_ERR, "new_fluid_midi_router failed");
    00074   fluid_midi_router_destroy_all_rules(router);
    00075   FLUID_FREE(router);
    00076   return NULL;
    00077 }
    00078
    00084 int
    00085 delete_fluid_midi_router(fluid_midi_router_t* router)
    00086 {
    00087   if (router == NULL) {
    00088     return FLUID_OK;
    00089   }
    00090   fluid_midi_router_destroy_all_rules(router);
    00091   FLUID_FREE(router);
    00092   return FLUID_OK;
    00093 }
    00094
    00095 /*
    00096  * fluid_midi_router_destroy_all_rules(fluid_midi_router_t* router)
    00097  * Purpose:
    00098  * Frees the used memory. This is used only for shutdown!
    00099  */
    00100 void fluid_midi_router_destroy_all_rules(fluid_midi_router_t* router){
    00101   fluid_midi_router_rule_t* rules[6];
    00102   fluid_midi_router_rule_t* current_rule;
    00103   fluid_midi_router_rule_t* next_rule;
    00104   int i;
    00105
    00106   rules[0]=router->note_rules;
    00107   rules[1]=router->cc_rules;
    00108   rules[2]=router->progchange_rules;
    00109   rules[3]=router->pitchbend_rules;
    00110   rules[4]=router->channel_pressure_rules;
    00111   rules[5]=router->key_pressure_rules;
    00112   for (i=0; i < 6; i++){
    00113     current_rule=rules[i];
    00114     while (current_rule){
    00115       next_rule=current_rule->next;
    00116       FLUID_FREE(current_rule);
    00117       current_rule=next_rule;
    00118     };
    00119   };
    00120 }
    00121
    00122 /*
    00123  * new_fluid_midi_router_rule
    00124  */
    00125 fluid_midi_router_rule_t* new_fluid_midi_router_rule(void)
    00126 {
    00127   fluid_midi_router_rule_t* rule=FLUID_NEW(fluid_midi_router_rule_t);
    00128   if (rule == NULL) {
    00129     FLUID_LOG(FLUID_ERR, "Out of memory");
    00130     return NULL;
    00131   }
    00132
    00133   FLUID_MEMSET(rule, 0, sizeof(fluid_midi_router_rule_t));
    00134   return rule;
    00135 };
    00136
    00137 /*
    00138  * delete_fluid_midi_router_rule
    00139  */
    00140 int
    00141 delete_fluid_midi_router_rule(fluid_midi_router_rule_t* rule)
    00142 {
    00143   FLUID_FREE(rule);
    00144   return FLUID_OK;
    00145 }
    00146
    00147
    00148 int fluid_midi_router_create_default_rules(fluid_midi_router_t* router)
    00149 {
    00150   fluid_midi_router_rule_t** rules[6];
    00151   int i;
    00152
    00153   rules[0]=&router->note_rules;
    00154   rules[1]=&router->cc_rules;
    00155   rules[2]=&router->progchange_rules;
    00156   rules[3]=&router->pitchbend_rules;
    00157   rules[4]=&router->channel_pressure_rules;
    00158   rules[5]=&router->key_pressure_rules;
    00159
    00160   for (i=0; i < 6; i++){
    00161     /* Start a new rule. rules[i] is the destination. _end will insert
    00162      * a new rule there into the linked list.
    00163      */
    00164     if (fluid_midi_router_begin(router, rules[i]) != FLUID_OK) goto error_recovery;
    00165     if (fluid_midi_router_end(router) != FLUID_OK) goto error_recovery;
    00166   };
    00167
    00168   return FLUID_OK;
    00169  error_recovery:
    00170   FLUID_LOG(FLUID_ERR, "fluid_midi_router_create_default_rules failed");
    00171   return FLUID_FAILED;
    00172 };
    00173
    00174 /* Purpose:
    00175  * Resets the new-rule registers in 'router' to their default values.
    00176  * Stores the destination (the linked list into which the new rule is inserted by _end)
    00177  */
    00178 int fluid_midi_router_begin(fluid_midi_router_t* router, fluid_midi_router_rule_t** dest)
    00179 {
    00180   if (!dest) goto error_recovery;
    00181   router->dest=dest;
    00182
    00183   /* In the days before the router, MIDI data went straight to the synth.
    00184    * So there is no need to check for reasonable values here.
    00185    */
    00186   router->new_rule_chan_min=0;
    00187   router->new_rule_chan_max=999999;
    00188   router->new_rule_chan_mul=1.;
    00189   router->new_rule_chan_add=0;
    00190   router->new_rule_par1_min=0;
    00191   router->new_rule_par1_max=999999;
    00192   router->new_rule_par1_mul=1.;
    00193   router->new_rule_par1_add=0;
    00194   router->new_rule_par2_min=0;
    00195   router->new_rule_par2_max=999999;
    00196   router->new_rule_par2_mul=1.;
    00197   router->new_rule_par2_add=0;
    00198
    00199   return FLUID_OK;
    00200  error_recovery:
    00201   FLUID_LOG(FLUID_ERR, "fluid_midi_router_begin failed");
    00202   return FLUID_FAILED;
    00203 };
    00204
    00205 /* Purpose:
    00206  * Concludes a sequence of commands:
    00207  * - router_start type
    00208  * - router_chan
    00209  * - router_par1
    00210  * - router_par2
    00211  * - router_end (this call).
    00212  * Creates a new event from the given parameters and stores it in
    00213  * the correct list.
    00214  */
    00215
    00216 int fluid_midi_router_end(fluid_midi_router_t* router){
    00217   fluid_midi_router_rule_t* rule=new_fluid_midi_router_rule(); if (rule == NULL) goto error_recovery;
    00218
    00219   rule->chan_min=router->new_rule_chan_min;
    00220   rule->chan_max=router->new_rule_chan_max;
    00221   rule->chan_mul=router->new_rule_chan_mul;
    00222   rule->chan_add=router->new_rule_chan_add;
    00223   rule->par1_min=router->new_rule_par1_min;
    00224   rule->par1_max=router->new_rule_par1_max;
    00225   rule->par1_mul=router->new_rule_par1_mul;
    00226   rule->par1_add=router->new_rule_par1_add;
    00227   rule->par2_min=router->new_rule_par2_min;
    00228   rule->par2_max=router->new_rule_par2_max;
    00229   rule->par2_mul=router->new_rule_par2_mul;
    00230   rule->par2_add=router->new_rule_par2_add;
    00231
    00232   /* Now we modify the rules table. Lock it to make sure, that the RT
    00233    * thread is not in the middle of an event. Also prevent, that some
    00234    * other thread modifies the table at the same time.
    00235    */
    00236
    00237   fluid_mutex_lock(router->ruletables_mutex);
    00238
    00239   rule->next= *(router->dest);
    00240   *router->dest=rule;
    00241
    00242   fluid_mutex_unlock(router->ruletables_mutex);
    00243
    00244   return FLUID_OK;
    00245
    00246  error_recovery:
    00247   FLUID_LOG(FLUID_ERR, "fluid_midi_router_end failed");
    00248   delete_fluid_midi_router_rule(rule);
    00249   return FLUID_FAILED;
    00250 };
    00251
    00275 int
    00276 fluid_midi_router_handle_midi_event(void* data, fluid_midi_event_t* event)
    00277 {
    00278   fluid_midi_router_t* router=(fluid_midi_router_t*)data;
    00279   fluid_synth_t* synth = router->synth;
    00280   fluid_midi_router_rule_t* rule=NULL;
    00281   fluid_midi_router_rule_t* next_rule=NULL;
    00282   int event_has_par2=0; /* Flag, indicates that current event needs two parameters */
    00283   int negative_event=0; /* Flag, indicates that current event releases a key / pedal */
    00284   int par1_max=127;     /* Range limit for par1 */
    00285   int par2_max=127;     /* Range limit for par2 */
    00286   int ret_val=FLUID_OK;
    00287
    00288   /* Some keyboards report noteoff through a noteon event with vel=0.
    00289    * Convert those to noteoff to ease processing.*/
    00290   if (event->type == NOTE_ON && event->param2 == 0) {
    00291     /* Channel: Remains the same
    00292      * Param 1: Note number, remains the same
    00293      * Param 2: release velocity), set to max
    00294      */
    00295     event->type = NOTE_OFF;
    00296     event->param2=127;
    00297   };
    00298
    00299   /* Lock the rules table, so that for example the shell thread doesn't
    00300    * clear the rules we are just working with */
    00301   fluid_mutex_lock(router->ruletables_mutex);
    00302
    00303   /* Depending on the event type, choose the correct table of rules.
    00304    * Also invoke the appropriate callback function for the event type
    00305    * to notify external applications.
    00306    * Note: An event type not listed here simply won't find any rules and
    00307    * will be ignored.
    00308    */
    00309   switch (event->type) {
    00310       case NOTE_ON:
    00311         rule=router->note_rules;
    00312         event_has_par2=1;
    00313         break;
    00314       case NOTE_OFF:
    00315         rule=router->note_rules;
    00316         event_has_par2=1;
    00317         break;
    00318       case CONTROL_CHANGE:
    00319         rule=router->cc_rules;
    00320         event_has_par2=1;
    00321         break;
    00322       case PROGRAM_CHANGE:
    00323         rule=router->progchange_rules;
    00324         break;
    00325       case PITCH_BEND:
    00326         rule=router->pitchbend_rules;
    00327         par1_max=16383;
    00328         break;
    00329       case CHANNEL_PRESSURE:
    00330         rule=router->channel_pressure_rules;
    00331         break;
    00332       case KEY_PRESSURE:
    00333         rule=router->key_pressure_rules;
    00334         event_has_par2=1;
    00335         break;
    00336       case MIDI_SYSTEM_RESET:
    00337         return router->event_handler(router->event_handler_data,event);
    00338       default:
    00339         break;
    00340   }
    00341
    00342   /* At this point 'rule' contains the first rule in a linked list.
    00343    * Check for all rules, whether channel and parameter ranges match.
    00344    */
    00345
    00346   while (rule){
    00347     int chan; /* Channel of the generated event */
    00348     int par1; /* par1 of the generated event */
    00349     int par2=0;
    00350     int event_par1=(int)event->param1;
    00351     int event_par2=(int)event->param2;
    00352     fluid_midi_event_t new_event;
    00353
    00354     /* Store the pointer to the next rule right now. If the rule is later flagged for destruction,
    00355      * it may not be accessed anymore.
    00356      */
    00357     next_rule=rule->next;
    00358
    00359     /* Check, whether the rule is still active. Expired rules cannot be removed immediately,
    00360      * because freeing memory in a realtime thread is no good idea. And the MIDI thread,
    00361      * which calls us here, has raised priority.*/
    00362     if (rule->state == MIDIRULE_DONE){
    00363       goto do_next_rule;
    00364     };
    00365
    00366     /* Channel window */
    00367     if (rule->chan_min > rule->chan_max){
    00368       /* Inverted rule: Exclude everything between max and min (but not min/max) */
    00369       if (event->channel > rule->chan_max && event->channel < rule->chan_min){
    00370         goto do_next_rule;
    00371       }
    00372     } else {
    00373       /* Normal rule: Exclude everything < max or > min (but not min/max) */
    00374       if (event->channel > rule->chan_max || event->channel < rule->chan_min){
    00375         goto do_next_rule;
    00376       }
    00377     };
    00378
    00379     /* Par 1 window */
    00380     if (rule->par1_min > rule->par1_max){
    00381       /* Inverted rule: Exclude everything between max and min (but not min/max) */
    00382       if (event_par1 > rule->par1_max && event_par1 < rule->par1_min){
    00383         goto do_next_rule;
    00384       }
    00385     } else {
    00386       /* Normal rule: Exclude everything < max or > min (but not min/max)*/
    00387       if (event_par1 > rule->par1_max || event_par1 < rule->par1_min){
    00388         goto do_next_rule;
    00389       }
    00390     };
    00391
    00392     /* Par 2 window (only applies to event types, which have 2 pars)
    00393      * For noteoff events, velocity switching doesn't make any sense.
    00394      * Velocity scaling might be useful, though.
    00395      */
    00396     if (event_has_par2 && event->type != NOTE_OFF){
    00397       if (rule->par2_min > rule->par2_max){
    00398         /* Inverted rule: Exclude everything between max and min (but not min/max) */
    00399         if (event_par2 > rule->par2_max && event_par2 < rule->par2_min){
    00400           goto do_next_rule;
    00401         }
    00402       } else {
    00403         /* Normal rule: Exclude everything < max or > min (but not min/max)*/
    00404         if (event_par2 > rule->par2_max || event_par2 < rule->par2_min){
    00405           goto do_next_rule;
    00406         };
    00407       };
    00408     };
    00409
    00410     /* Channel scaling / offset
    00411      * Note: rule->chan_mul will probably be 0 or 1. If it's 0, input from all
    00412      * input channels is mapped to the same synth channel.
    00413      */
    00414     chan=(int)((fluid_real_t)event->channel * (fluid_real_t)rule->chan_mul + (fluid_real_t)rule->chan_add + 0.5);
    00415
    00416     /* Par 1 scaling / offset */
    00417     par1=(int)((fluid_real_t)event_par1 * (fluid_real_t)rule->par1_mul + (fluid_real_t)rule->par1_add + 0.5);
    00418
    00419     /* Par 2 scaling / offset, if applicable */
    00420     if (event_has_par2){
    00421       par2=(int)((fluid_real_t)event_par2 * (fluid_real_t)rule->par2_mul + (fluid_real_t)rule->par2_add + 0.5);
    00422     };
    00423
    00424     /* Channel range limiting */
    00425     if (chan < 0){
    00426       chan=0;
    00427       /* Upper limit is hard to implement, because the number of MIDI channels can be changed via settings any time. */
    00428     };
    00429
    00430     /* Par1 range limiting */
    00431     if (par1 < 0){
    00432       par1=0;
    00433     } else if (par1 > par1_max){
    00434       par1=par1_max;
    00435     };
    00436
    00437     /* Par2 range limiting */
    00438     if (event_has_par2){
    00439       if (par2 < 0){
    00440         par2=0;
    00441       } else if (par2 > par2_max){
    00442         par2=par2_max;
    00443       };
    00444     };
    00445
    00446     /* At this point we have to create an event of event->type on 'chan' with par1 (maybe par2).
    00447      * We keep track on the state of noteon and sustain pedal events. If the application tries
    00448      * to delete a rule, it will only be fully removed, if pending noteoff / pedal off events have
    00449      * arrived. In the meantime (MIDIRULE_WAITING) state, it will only let through 'negative' events
    00450      * (noteoff or pedal up).
    00451      */
    00452
    00453     if (
    00454       event->type == NOTE_ON
    00455       || (event->type == CONTROL_CHANGE && par1 == SUSTAIN_SWITCH && par2 >= 64)
    00456       ){
    00457       /* Noteon or sustain pedal down event generated */
    00458       if (rule->keys_cc[par1] == 0){
    00459         rule->keys_cc[par1]=1;
    00460         rule->pending_events++;
    00461       };
    00462     } else if (
    00463       event->type == NOTE_OFF
    00464       || (event->type == CONTROL_CHANGE && par1 == SUSTAIN_SWITCH && par2 < 64)
    00465       ){
    00466       /* Noteoff or sustain pedal up event generated */
    00467       if (rule->keys_cc[par1] > 0){
    00468         rule->keys_cc[par1]=0;
    00469         rule->pending_events--;
    00470         negative_event=1;
    00471       };
    00472     };
    00473
    00474     if (rule->state == MIDIRULE_WAITING){
    00475       if (negative_event){
    00476         if (rule->pending_events == 0){
    00477           /* There are no more pending events in the rule - all keys are up.
    00478            * Change its state to 'unused', it will be cleared up at the next
    00479            * opportunity to run 'free' safely.
    00480            * Note: After this, the rule may disappear at any time
    00481            */
    00482             rule->state=MIDIRULE_DONE;
    00483         };
    00484       } else {
    00485
    00486         /* This rule only exists because of some unfinished business:
    00487          * There is still a note or sustain pedal waiting to be
    00488          * released. But the event that came in does not release any.
    00489          * So we just ignore the event and go on with the next rule.
    00490          */
    00491         goto do_next_rule;
    00492       };
    00493     };
    00494
    00495     /* At this point it is decided, what is sent to the synth.
    00496      * Create a new event and make the appropriate call
    00497      */
    00498
    00499     fluid_midi_event_set_type(&new_event, event->type);
    00500     fluid_midi_event_set_channel(&new_event, chan);
    00501 /*     fluid_midi_event_set_param1(&new_event, par1); */
    00502 /*     fluid_midi_event_set_param2(&new_event, par2); */
    00503     new_event.param1 = par1;
    00504     new_event.param2 = par2;
    00505
    00506     /* Don't know what to do with return values: What is, if
    00507      * generating one event fails?  Current solution: Make all calls,
    00508      * regardless of failures, but report failure to the caller, as
    00509      * soon as only one call fails.  Strategy should be thought
    00510      * through.
    00511      */
    00512     if (router->event_handler(router->event_handler_data, &new_event) != FLUID_OK) {
    00513       ret_val=FLUID_FAILED;
    00514     }
    00515
    00516   do_next_rule:
    00517     rule=next_rule;
    00518   };
    00519
    00520   /* We are finished with the rule tables. Allow the other threads to do their job. */
    00521   fluid_mutex_unlock(router->ruletables_mutex);
    00522
    00523   return ret_val;
    00524 }
    00525
    00526 int fluid_midi_router_handle_clear(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
    00527 {
    00528   fluid_midi_router_t* router=synth->midi_router;
    00529
    00530   if (ac != 0) {
    00531     fluid_ostream_printf(out, "router_clear needs no arguments.\n");
    00532     goto error_recovery;
    00533   }
    00534
    00535   /* Disable rules and mark for destruction */
    00536   fluid_midi_router_disable_all_rules(router);
    00537
    00538   /* Free unused rules */
    00539   fluid_midi_router_free_unused_rules(router);
    00540
    00541   return 0;
    00542  error_recovery:
    00543   return -1;
    00544 };
    00545
    00546 int fluid_midi_router_handle_default(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
    00547 {
    00548   fluid_midi_router_t* router=synth->midi_router;
    00549
    00550   if (ac != 0) {
    00551     fluid_ostream_printf(out, "router_default needs no arguments.\n");
    00552     return -1;
    00553   }
    00554
    00555   /* Disable rules and mark for destruction */
    00556   fluid_midi_router_disable_all_rules(router);
    00557
    00558   /* Create default rules */
    00559   if (fluid_midi_router_create_default_rules(router) != FLUID_OK){
    00560     FLUID_LOG(FLUID_ERR, "create_default_rules failed");
    00561     goto error_recovery;
    00562   };
    00563
    00564   /* Free unused rules */
    00565   fluid_midi_router_free_unused_rules(router);
    00566
    00567   return 0;
    00568  error_recovery:
    00569   return -1;
    00570 };
    00571
    00572 int fluid_midi_router_handle_begin(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
    00573 {
    00574   fluid_midi_router_t* router=synth->midi_router;
    00575   fluid_midi_router_rule_t** dest=NULL;
    00576
    00577   if (ac != 1) {
    00578     fluid_ostream_printf(out, "router_begin needs no arguments.\n");
    00579       goto error_recovery;
    00580   }
    00581
    00582   if (FLUID_STRCMP(av[0],"note") == 0){
    00583     dest=& router->note_rules;
    00584   } else if (FLUID_STRCMP(av[0],"cc") == 0){
    00585     dest=& router->cc_rules;
    00586   } else if (FLUID_STRCMP(av[0],"prog") == 0){
    00587     dest=& router->progchange_rules;
    00588   } else if (FLUID_STRCMP(av[0],"pbend") == 0){
    00589     dest=& router->pitchbend_rules;
    00590   } else if (FLUID_STRCMP(av[0],"cpress") == 0){
    00591     dest=& router->channel_pressure_rules;
    00592   } else if (FLUID_STRCMP(av[0],"kpress") == 0){
    00593     dest=& router->key_pressure_rules;
    00594   };
    00595
    00596   if (dest == NULL){
    00597       fluid_ostream_printf(out, "router_begin args: note, cc, prog, pbend, cpress, kpress\n");
    00598       goto error_recovery;
    00599   };
    00600
    00601   if (fluid_midi_router_begin(router, dest) != FLUID_OK){
    00602     goto error_recovery;
    00603   };
    00604
    00605   /* Free unused rules (give it a try) */
    00606   fluid_midi_router_free_unused_rules(router);
    00607
    00608   return 0;
    00609
    00610  error_recovery:
    00611   return -1;
    00612 };
    00613
    00614 int fluid_midi_router_handle_end(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
    00615 {
    00616   fluid_midi_router_t* router=synth->midi_router;
    00617
    00618   if (ac != 0) {
    00619     fluid_ostream_printf(out, "router_end needs no arguments.");
    00620     goto error_recovery;
    00621   }
    00622
    00623   if (fluid_midi_router_end(router) != FLUID_OK){
    00624     FLUID_LOG(FLUID_ERR, "midi_router_end failed");
    00625     goto error_recovery;
    00626   };
    00627
    00628   /* Free unused rules (give it a try) */
    00629   fluid_midi_router_free_unused_rules(router);
    00630
    00631   return 0;
    00632
    00633  error_recovery:
    00634   return -1;
    00635 };
    00636
    00637 int fluid_midi_router_handle_chan(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
    00638 {
    00639   fluid_midi_router_t* router=synth->midi_router;
    00640
    00641   if (ac != 4) {
    00642     fluid_ostream_printf(out, "router_chan needs four args: min, max, mul, add.");
    00643     goto error_recovery;
    00644   }
    00645
    00646   router->new_rule_chan_min=atoi(av[0]);
    00647   router->new_rule_chan_max=atoi(av[1]);
    00648   router->new_rule_chan_mul=atoi(av[2]);
    00649   router->new_rule_chan_add=atoi(av[3]);
    00650
    00651   /* Free unused rules (give it a try) */
    00652   fluid_midi_router_free_unused_rules(router);
    00653
    00654   return 0;
    00655
    00656  error_recovery:
    00657   return -1;
    00658 };
    00659
    00660 int fluid_midi_router_handle_par1(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
    00661 {
    00662   fluid_midi_router_t* router=synth->midi_router;
    00663
    00664   if (ac != 4) {
    00665     fluid_ostream_printf(out, "router_par1 needs four args: min, max, mul, add.");
    00666     goto error_recovery;
    00667   }
    00668
    00669   router->new_rule_par1_min=atoi(av[0]);
    00670   router->new_rule_par1_max=atoi(av[1]);
    00671   router->new_rule_par1_mul=atoi(av[2]);
    00672   router->new_rule_par1_add=atoi(av[3]);
    00673
    00674   /* Free unused rules (give it a try) */
    00675   fluid_midi_router_free_unused_rules(router);
    00676
    00677   return 0;
    00678
    00679  error_recovery:
    00680   return -1;
    00681 };
    00682
    00683 int fluid_midi_router_handle_par2(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
    00684 {
    00685   fluid_midi_router_t* router=synth->midi_router;
    00686
    00687   if (ac != 4) {
    00688     fluid_ostream_printf(out, "router_par2 needs four args: min, max, mul, add.");
    00689     goto error_recovery;
    00690   }
    00691
    00692   router->new_rule_par2_min=atoi(av[0]);
    00693   router->new_rule_par2_max=atoi(av[1]);
    00694   router->new_rule_par2_mul=atoi(av[2]);
    00695   router->new_rule_par2_add=atoi(av[3]);
    00696
    00697   /* Free unused rules (give it a try) */
    00698   fluid_midi_router_free_unused_rules(router);
    00699   return 0;
    00700
    00701  error_recovery:
    00702   return -1;
    00703 };
    00704
    00705 void fluid_midi_router_disable_all_rules(fluid_midi_router_t* router)
    00706 {
    00707   /* Go through all rules. If the rule has no outstanding events, then
    00708    * flag it for destruction. Otherwise change it to wait mode. Then
    00709    * it will handle the pending events, and flag itself for destruction
    00710    * as soon as all events have arrived.
    00711    */
    00712
    00713   fluid_midi_router_rule_t* rules[6];
    00714   fluid_midi_router_rule_t* current_rule;
    00715   int i;
    00716   rules[0]=router->note_rules;
    00717   rules[1]=router->cc_rules;
    00718   rules[2]=router->progchange_rules;
    00719   rules[3]=router->pitchbend_rules;
    00720   rules[4]=router->channel_pressure_rules;
    00721   rules[5]=router->key_pressure_rules;
    00722
    00723   /* Lock the rules table. We live on the assumption, that the rules
    00724    * table does not change while we are chewing at it.
    00725    * Changes between the processing of note / cc etc are permitted.
    00726    */
    00727   for (i=0; i < 6; i++){
    00728     fluid_mutex_lock(router->ruletables_mutex);
    00729     current_rule=rules[i];
    00730     while (current_rule){
    00731       if (current_rule->pending_events == 0){
    00732         /* Flag for destruction */
    00733         current_rule->state=MIDIRULE_DONE;
    00734       } else {
    00735         /* Wait mode */
    00736         current_rule->state=MIDIRULE_WAITING;
    00737       };
    00738       current_rule=current_rule->next;
    00739     };
    00740     fluid_mutex_unlock(router->ruletables_mutex);
    00741   };
    00742 };
    00743
    00744 void fluid_midi_router_free_unused_rules(fluid_midi_router_t* router)
    00745 {
    00746   int i;
    00747
    00748   for (i=0; i < 6; i++){
    00749     fluid_midi_router_rule_t** p=NULL;
    00750
    00751     /* We assume, that the table does not change while we are at it.
    00752      * Between different types (note, cc etc) we can allow changes.
    00753      */
    00754     fluid_mutex_lock(router->ruletables_mutex);
    00755     switch(i){
    00756         case 0:
    00757           p=&router->note_rules;
    00758           break;
    00759         case 1:
    00760           p=&router->cc_rules;
    00761           break;
    00762         case 2:
    00763           p=&router->progchange_rules;
    00764           break;
    00765         case 3:
    00766           p=&router->pitchbend_rules;
    00767           break;
    00768         case 4:
    00769           p=&router->channel_pressure_rules;
    00770           break;
    00771         case 5:
    00772           p=&router->key_pressure_rules;
    00773           break;
    00774         default:
    00775           break;
    00776     };
    00777
    00778     while (*p){
    00779       fluid_midi_router_rule_t* current_rule=*p;
    00780       fluid_midi_router_rule_t* next_rule=current_rule->next;
    00781
    00782       if (current_rule->state == MIDIRULE_DONE){
    00783         /* p points to current_rule.
    00784          * current_rule->next points to next_rule.
    00785          * Unlink current_rule from the chain by setting the content
    00786          * of p to next_rule.
    00787          */
    00788
    00789         *p=next_rule;
    00790
    00791         /* Now the rule is not in the chain anymore. Destroy it. */
    00792         delete_fluid_midi_router_rule(current_rule);
    00793
    00794       } else {
    00795         /* We have to keep the rule, there is still unfinished business. */
    00796         p = &current_rule->next;
    00797       };
    00798     };
    00799     fluid_mutex_unlock(router->ruletables_mutex);
    00800   };
    00801 };
    00802
    00813 int fluid_midi_dump_prerouter(void* data, fluid_midi_event_t* event)
    00814 {
    00815   switch (event->type) {
    00816       case NOTE_ON:
    00817         fprintf(stdout, "event_pre_noteon %i %i %i\n",
    00818                 event->channel, event->param1, event->param2);
    00819         break;
    00820       case NOTE_OFF:
    00821         fprintf(stdout, "event_pre_noteoff %i %i %i\n",
    00822                 event->channel, event->param1, event->param2);
    00823         break;
    00824       case CONTROL_CHANGE:
    00825         fprintf(stdout, "event_pre_cc %i %i %i\n",
    00826                 event->channel, event->param1, event->param2);
    00827         break;
    00828       case PROGRAM_CHANGE:
    00829         fprintf(stdout, "event_pre_prog %i %i\n", event->channel, event->param1);
    00830         break;
    00831       case PITCH_BEND:
    00832         fprintf(stdout, "event_pre_pitch %i %i\n", event->channel, event->param1);
    00833         break;
    00834       case CHANNEL_PRESSURE:
    00835         fprintf(stdout, "event_pre_cpress %i %i\n", event->channel, event->param1);
    00836         break;
    00837       case KEY_PRESSURE:
    00838         fprintf(stdout, "event_pre_kpress %i %i %i\n",
    00839                 event->channel, event->param1, event->param2);
    00840         break;
    00841       default:
    00842         break;
    00843   };
    00844   return fluid_midi_router_handle_midi_event((fluid_midi_router_t*) data, event);
    00845 };
    00846
    00857 int fluid_midi_dump_postrouter(void* data, fluid_midi_event_t* event)
    00858 {
    00859   switch (event->type) {
    00860       case NOTE_ON:
    00861         fprintf(stdout, "event_post_noteon %i %i %i\n",
    00862                 event->channel, event->param1, event->param2);
    00863         break;
    00864       case NOTE_OFF:
    00865         fprintf(stdout, "event_post_noteoff %i %i %i\n",
    00866                 event->channel, event->param1, event->param2);
    00867         break;
    00868       case CONTROL_CHANGE:
    00869         fprintf(stdout, "event_post_cc %i %i %i\n",
    00870                 event->channel, event->param1, event->param2);
    00871         break;
    00872       case PROGRAM_CHANGE:
    00873         fprintf(stdout, "event_post_prog %i %i\n", event->channel, event->param1);
    00874         break;
    00875       case PITCH_BEND:
    00876         fprintf(stdout, "event_post_pitch %i %i\n", event->channel, event->param1);
    00877         break;
    00878       case CHANNEL_PRESSURE:
    00879         fprintf(stdout, "event_post_cpress %i %i\n", event->channel, event->param1);
    00880         break;
    00881       case KEY_PRESSURE:
    00882         fprintf(stdout, "event_post_kpress %i %i %i\n",
    00883                 event->channel, event->param1, event->param2);
    00884         break;
    00885       default:
    00886         break;
    00887   };
    00888   return fluid_synth_handle_midi_event((fluid_synth_t*) data, event);
    00889 };