/* Copyright 2003 Jacob Robbins Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ ----------about input maps------------------ Input maps are a user configureable framework for translating realtime events into actions performed by/on realtime objects. The overall input map structure is called an EventInputMap. Each realtime object can have its own EventInputMap or use a shared generic one. An EventInputMap can only handle one type of event (ex: MIDI event) and the application is responsible for feeding the right type of event into the map. The EventInputMap struct is made up of EventSubmap structs, each of which handles one parameter in the event type (ex: MIDI status byte). Each EventSubmap has an array of InputMapTest's. These are individual tests on an event parameter which either result in a match or no match. When an event is run through an input map, the event is run through the first submap. In that submap the event is tested against the array of map tests, starting at the beginning of the array. If a test of an event on a map element succeeds, then the linked list of MapAction's associated with that test element are executed. If a test of an event on a map element fails, then the event is tested against the next test element in the submap. If there are no more tests in the submap, then the event is dropped. -----------input maps part 2: actions--------- A MapAction describes the action to be done when an incoming event satisfies a certain criteria. There are 4 types of actions: -Remap External: passes the event on to a different InputMap owned by a different realtime object. A list of pointers to other InputMap's is part of the InputMap struct (note: this has been altered in the example implementation such that the application holds a single list of input maps), the pass target must be a member of this list. The MapAction value called target_array_index determines which member of the pointer list the event is sent to. -Remap Internal: passes the event to a different submap in the same input map. (ex: a match of MIDIStatusByte == CC results in passing the event to the CC submap). The MapAction value called target_array_index determines which submap the event is sent to. -Set Parameter: a specific parameter in the owner object is set to the value of one of the parameters in the incoming event. The MapAction value called target_array_index determines which parameter in the object's parameter list is to be set. The MapAction value called event_parameter_index determines which part of the event is used to set the value. The MapAction can also specify a fixed value to set the parameter to, ignoring the event contents. (note: this should be amended to facilitate applying a function to the event parameter and then using the result of the function to set the object's parameter value. However, as the function to be used depends on both the event parameter's type/range and the object parameter's type/range, this is outside the scope of the input map). -Callback Function: a method (function) of the realtime object is executed w/ values according to Set Parameter as above. (note: the example implementation only uses callback functions and Set parameter is implemented as a callback. It is expected that Set Parameter will be completely subsumed into Callback Functions by the application using input maps. ). Multiple MapAction's can be triggered by a single InputMapElement. The map element contains the first in a linked-list of actions to be executed. In other words, each MapAction has a pointer to another MapAction, and execution of a MapAction implies subsequently executing the MapAction that it points to, if there is one. After all actions have been executed, the event is discarded. ------------input maps part 3: customizing-------- The following describes how to integrate input_maps into an application. For each realtime object, a default input map should be created immediately after the object is created. The default map attaches parameters to corresponding events using whatever information the application has available to it. The default map can be overriden by adding map entries, or whole additional submaps can be added. These additions are local to the particular realtime object and are saved along with that object as a diff to the default input map. (note that the sample implementation does not yet have import/export functions). It makes sense for an application to have more than one default input map to accomodate the most common uses of objects it is intended to handle. -----------input maps part 4: a tentative implementation------- Here is a simple set of C structures to illustrate the input map framework as applied to MIDI events: (NOTE: the sample implementation outdates this and should be referred to instead of these structs as it includes functions as well) #define MAP_ELEMENT_TYPE_EXACT 0 #define MAP_ELEMENT_TYPE_RANGE 1 #define MAP_ACTION_TYPE_REMAP_EXTERN 0 #define MAP_ACTION_TYPE_REMAP_LOCAL 1 #define MAP_ACTION_TYPE_SET_PARAMETER 2 struct event_input_map{ event_submap* submap_list; int remap_list_size; event_input_map** remap_list; }; struct event_submap{ char event_parameter_index; char array_type; /*exact or range*/ int array_size; union { map_element_exact* array_exact; map_element_range* array_range; }array; int action_list_size; map_action* action_list; }; /*note: range elements require 1 more comparison than exact elements. a mixed type array would require 1 or 2 more comparisons than exact elements (1 for element type another if range). Therefore it is faster to use all ranges if any range comparisons are necessary. Usually a submap will only need one type anyway: MIDI status byte => exact, divying up a continuous controller => range. It is important to custom tailor the map_element test to the event type it is used on because this is a very commonly used part of any application and you can potentially waste a lot of time by testing in ways that are not very useful. I would assume other people use hash functions for this but i don't know what hash functions are so that is not an option for me.*/ struct map_element_exact{ int parameter_test; int action; /* (action==0) => discard event, (action!=0) => do action*/ }; struct map_element_range{ int parameter_min; int parameter_max; int action; /* (action==0) => discard event, (action!=0) => do action*/ }; struct map_action{ char action_type; /*remap extern, remap local, set parameter*/ int target_array_index; /* (target_array_index<0) => use fixed_value instead event value*/ int fixed_value; char event_parameter_index; int apply_function_to_parameter_index; map_action* next; };