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 };

Generated on Sat Nov 17 13:40:23 2007 for libfluidsynth by  doxygen 1.5.3