Chris@49: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- */ Chris@0: Chris@0: /* dssi.h Chris@0: Chris@0: DSSI version 0.10 Chris@0: Copyright (c) 2004,2005 Chris Cannam, Steve Harris and Sean Bolton Chris@0: Chris@0: This library is free software; you can redistribute it and/or Chris@0: modify it under the terms of the GNU Lesser General Public License Chris@0: as published by the Free Software Foundation; either version 2.1 of Chris@0: the License, or (at your option) any later version. Chris@0: Chris@0: This library is distributed in the hope that it will be useful, but Chris@0: WITHOUT ANY WARRANTY; without even the implied warranty of Chris@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Chris@0: Lesser General Public License for more details. Chris@0: Chris@0: You should have received a copy of the GNU Lesser General Public Chris@0: License along with this library; if not, write to the Free Software Chris@0: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 Chris@0: USA. Chris@0: */ Chris@0: Chris@0: #ifndef DSSI_INCLUDED Chris@0: #define DSSI_INCLUDED Chris@0: Chris@0: #include "ladspa.h" Chris@0: #include "alsa/seq_event.h" Chris@0: Chris@0: #define DSSI_VERSION "0.10" Chris@0: #define DSSI_VERSION_MAJOR 0 Chris@0: #define DSSI_VERSION_MINOR 10 Chris@0: Chris@0: #ifdef __cplusplus Chris@0: extern "C" { Chris@0: #endif Chris@0: Chris@0: /* Chris@0: There is a need for an API that supports hosted MIDI soft synths Chris@0: with GUIs in Linux audio applications. In time the GMPI initiative Chris@0: should comprehensively address this need, but the requirement for Chris@0: Linux applications to be able to support simple hosted synths is Chris@0: here now, and GMPI is not. This proposal (the "DSSI Soft Synth Chris@0: Interface" or DSSI, pronounced "dizzy") aims to provide a simple Chris@0: solution in a way that we hope will prove complete and compelling Chris@0: enough to support now, yet not so compelling as to supplant GMPI or Chris@0: any other comprehensive future proposal. Chris@0: Chris@0: For simplicity and familiarity, this API is based as far as Chris@0: possible on existing work -- the LADSPA plugin API for control Chris@0: values and audio processing, and the ALSA sequencer event types for Chris@0: MIDI event communication. The GUI part of the proposal is quite Chris@0: new, but may also be applicable retroactively to LADSPA plugins Chris@0: that do not otherwise support this synth interface. Chris@0: */ Chris@0: Chris@0: /* Chris@0: A program wishing to use the DSSI v2 API should set the following Chris@0: symbol to 2 before including this header. Chris@0: */ Chris@0: #if (!defined DSSI_API_LEVEL) Chris@0: #define DSSI_API_LEVEL 1 Chris@0: #endif Chris@0: Chris@0: typedef struct _DSSI_Program_Descriptor { Chris@0: Chris@0: /** Bank number for this program. Note that DSSI does not support Chris@0: MIDI-style separation of bank LSB and MSB values. There is no Chris@0: restriction on the set of available banks: the numbers do not Chris@0: need to be contiguous, there does not need to be a bank 0, etc. */ Chris@0: unsigned long Bank; Chris@0: Chris@0: /** Program number (unique within its bank) for this program. Chris@0: There is no restriction on the set of available programs: the Chris@0: numbers do not need to be contiguous, there does not need to Chris@0: be a program 0, etc. */ Chris@0: unsigned long Program; Chris@0: Chris@0: /** Name of the program. */ Chris@0: const char * Name; Chris@0: Chris@0: } DSSI_Program_Descriptor; Chris@0: Chris@0: Chris@0: #define DSSI_TRANSPORT_VALID_STATE 0x01 Chris@0: #define DSSI_TRANSPORT_VALID_BPM 0x02 Chris@0: #define DSSI_TRANSPORT_VALID_BBT 0x10 Chris@0: #define DSSI_TRANSPORT_VALID_TIME 0x20 Chris@0: Chris@0: #define DSSI_TRANSPORT_STATE_STOPPED 0 Chris@0: #define DSSI_TRANSPORT_STATE_RUNNING 1 Chris@0: #define DSSI_TRANSPORT_STATE_FREEWHEELING 2 Chris@0: #define DSSI_TRANSPORT_STATE_OTHER 3 /* waiting for sync, ? */ Chris@0: Chris@0: typedef struct _DSSI_Transport_Info { Chris@0: Chris@0: /** The value of this field indicates which of the following Chris@0: * transport information fields contain valid values. It is Chris@0: * the logical OR of the DSSI_TRANSPORT_VALID_* bits defined Chris@0: * above, and may be zero. */ Chris@0: int Valid; Chris@0: Chris@0: Chris@0: /** This field is valid when (Valid & DSSI_TRANSPORT_VALID_STATE) Chris@0: * is true: Chris@0: * Chris@0: * ---- The current transport state, one of the DSSI_TRANSPORT_STATE_* Chris@0: * values defined above. */ Chris@0: int State; Chris@0: Chris@0: Chris@0: /** This field is valid when (Valid & DSSI_TRANSPORT_VALID_BPM) Chris@0: * is true: Chris@0: * Chris@0: * ---- The current tempo, in beats per minute. */ Chris@0: double Beats_Per_Minute; Chris@0: Chris@0: Chris@0: /** These six fields are valid when (Valid & DSSI_TRANSPORT_VALID_BBT) Chris@0: * is true: Chris@0: * Chris@0: * ---- The bar number at the beginning of the current process cycle. */ Chris@0: unsigned long Bar; Chris@0: Chris@0: /** ---- The beat within that Bar. */ Chris@0: unsigned long Beat; Chris@0: Chris@0: /** ---- The tick within that Beat. */ Chris@0: unsigned long Tick; Chris@0: Chris@0: /** ---- The (possibly fractional) tick count since transport 'start' Chris@0: * and the beginning of the current Bar. */ Chris@0: double Bar_Start_Tick; Chris@0: Chris@0: /** ---- The number of beats per bar. */ Chris@0: float Beats_Per_Bar; Chris@0: Chris@0: /** ---- The number of ticks for each beat. */ Chris@0: double Ticks_Per_Beat; Chris@0: Chris@0: /* [Sean says: I left out the 'beat_type' (time signature "denominator") Chris@0: * field of the jack_position_t structure, because I think it's useless Chris@0: * except to a notation program. Does anybody else feel like we need it?] Chris@0: */ Chris@0: Chris@0: /** These two fields are valid when (Valid & DSSI_TRANSPORT_VALID_TIME) Chris@0: * is true: Chris@0: * Chris@0: * ---- The transport time at the beginning of the current process Chris@0: * cycle, in seconds. */ Chris@0: double Current_Time; Chris@0: Chris@0: /** ---- The transport time at the beginning of the next process Chris@0: cycle, unless repositioning occurs. */ Chris@0: double Next_Time; Chris@0: Chris@0: } DSSI_Transport_Info; Chris@0: Chris@0: typedef struct _DSSI_Host_Descriptor DSSI_Host_Descriptor; /* below */ Chris@0: Chris@0: typedef struct _DSSI_Descriptor { Chris@0: Chris@0: /** Chris@0: * DSSI_API_Version Chris@0: * Chris@0: * This member indicates the DSSI API level used by this plugin. Chris@0: * All plugins must set this to 1 or 2. The version 1 API contains Chris@0: * all DSSI_Descriptor fields through run_multiple_synths_adding(), Chris@0: * while the version 2 API adds the receive_host_descriptor(). Chris@0: */ Chris@0: int DSSI_API_Version; Chris@0: Chris@0: /** Chris@0: * LADSPA_Plugin Chris@0: * Chris@0: * A DSSI synth plugin consists of a LADSPA plugin plus an Chris@0: * additional framework for controlling program settings and Chris@0: * transmitting MIDI events. A plugin must fully implement the Chris@0: * LADSPA descriptor fields as well as the required LADSPA Chris@0: * functions including instantiate() and (de)activate(). It Chris@0: * should also implement run(), with the same behaviour as if Chris@0: * run_synth() (below) were called with no synth events. Chris@0: * Chris@0: * In order to instantiate a synth the host calls the LADSPA Chris@0: * instantiate function, passing in this LADSPA_Descriptor Chris@0: * pointer. The returned LADSPA_Handle is used as the argument Chris@0: * for the DSSI functions below as well as for the LADSPA ones. Chris@0: */ Chris@0: const LADSPA_Descriptor *LADSPA_Plugin; Chris@0: Chris@0: /** Chris@0: * configure() Chris@0: * Chris@0: * This member is a function pointer that sends a piece of Chris@0: * configuration data to the plugin. The key argument specifies Chris@0: * some aspect of the synth's configuration that is to be changed, Chris@0: * and the value argument specifies a new value for it. A plugin Chris@0: * that does not require this facility at all may set this member Chris@0: * to NULL. Chris@0: * Chris@0: * This call is intended to set some session-scoped aspect of a Chris@0: * plugin's behaviour, for example to tell the plugin to load Chris@0: * sample data from a particular file. The plugin should act Chris@0: * immediately on the request. The call should return NULL on Chris@0: * success, or an error string that may be shown to the user. The Chris@0: * host will free the returned value after use if it is non-NULL. Chris@0: * Chris@0: * Calls to configure() are not automated as timed events. Chris@0: * Instead, a host should remember the last value associated with Chris@0: * each key passed to configure() during a given session for a Chris@0: * given plugin instance, and should call configure() with the Chris@0: * correct value for each key the next time it instantiates the Chris@0: * "same" plugin instance, for example on reloading a project in Chris@0: * which the plugin was used before. Plugins should note that a Chris@0: * host may typically instantiate a plugin multiple times with the Chris@0: * same configuration values, and should share data between Chris@0: * instances where practical. Chris@0: * Chris@0: * Calling configure() completely invalidates the program and bank Chris@0: * information last obtained from the plugin. Chris@0: * Chris@0: * Reserved and special key prefixes Chris@0: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Chris@0: * The DSSI: prefix Chris@0: * ---------------- Chris@0: * Configure keys starting with DSSI: are reserved for particular Chris@0: * purposes documented in the DSSI specification. At the moment, Chris@0: * there is one such key: DSSI:PROJECT_DIRECTORY. A host may call Chris@0: * configure() passing this key and a directory path value. This Chris@0: * indicates to the plugin and its UI that a directory at that Chris@0: * path exists and may be used for project-local data. Plugins Chris@0: * may wish to use the project directory as a fallback location Chris@0: * when looking for other file data, or as a base for relative Chris@0: * paths in other configuration values. Chris@0: * Chris@0: * The GLOBAL: prefix Chris@0: * ------------------ Chris@0: * Configure keys starting with GLOBAL: may be used by the plugin Chris@0: * and its UI for any purpose, but are treated specially by the Chris@0: * host. When one of these keys is used in a configure OSC call Chris@0: * from the plugin UI, the host makes the corresponding configure Chris@0: * call (preserving the GLOBAL: prefix) not only to the target Chris@0: * plugin but also to all other plugins in the same instance Chris@0: * group, as well as their UIs. Note that if any instance Chris@0: * returns non-NULL from configure to indicate error, the host Chris@0: * may stop there (and the set of plugins on which configure has Chris@0: * been called will thus depend on the host implementation). Chris@0: * See also the configure OSC call documentation in RFC.txt. Chris@0: */ Chris@0: char *(*configure)(LADSPA_Handle Instance, Chris@0: const char *Key, Chris@0: const char *Value); Chris@0: Chris@0: #define DSSI_RESERVED_CONFIGURE_PREFIX "DSSI:" Chris@0: #define DSSI_GLOBAL_CONFIGURE_PREFIX "GLOBAL:" Chris@0: #define DSSI_PROJECT_DIRECTORY_KEY \ Chris@0: DSSI_RESERVED_CONFIGURE_PREFIX "PROJECT_DIRECTORY" Chris@0: Chris@0: /** Chris@0: * get_program() Chris@0: * Chris@0: * This member is a function pointer that provides a description Chris@0: * of a program (named preset sound) available on this synth. A Chris@0: * plugin that does not support programs at all should set this Chris@0: * member to NULL. Chris@0: * Chris@0: * The Index argument is an index into the plugin's list of Chris@0: * programs, not a program number as represented by the Program Chris@0: * field of the DSSI_Program_Descriptor. (This distinction is Chris@0: * needed to support synths that use non-contiguous program or Chris@0: * bank numbers.) Chris@0: * Chris@0: * This function returns a DSSI_Program_Descriptor pointer that is Chris@0: * guaranteed to be valid only until the next call to get_program, Chris@0: * deactivate, or configure, on the same plugin instance. This Chris@0: * function must return NULL if passed an Index argument out of Chris@0: * range, so that the host can use it to query the number of Chris@0: * programs as well as their properties. Chris@0: */ Chris@0: const DSSI_Program_Descriptor *(*get_program)(LADSPA_Handle Instance, Chris@0: unsigned long Index); Chris@0: Chris@0: /** Chris@0: * select_program() Chris@0: * Chris@0: * This member is a function pointer that selects a new program Chris@0: * for this synth. The program change should take effect Chris@0: * immediately at the start of the next run_synth() call. (This Chris@0: * means that a host providing the capability of changing programs Chris@0: * between any two notes on a track must vary the block size so as Chris@0: * to place the program change at the right place. A host that Chris@0: * wanted to avoid this would probably just instantiate a plugin Chris@0: * for each program.) Chris@0: * Chris@0: * A plugin that does not support programs at all should set this Chris@0: * member NULL. Plugins should ignore a select_program() call Chris@0: * with an invalid bank or program. Chris@0: * Chris@0: * A plugin is not required to select any particular default Chris@0: * program on activate(): it's the host's duty to set a program Chris@0: * explicitly. The current program is invalidated by any call to Chris@0: * configure(). Chris@0: * Chris@0: * A plugin is permitted to re-write the values of its input Chris@0: * control ports when select_program is called. The host should Chris@0: * re-read the input control port values and update its own Chris@0: * records appropriately. (This is the only circumstance in Chris@0: * which a DSSI plugin is allowed to modify its own input ports.) Chris@0: */ Chris@0: void (*select_program)(LADSPA_Handle Instance, Chris@0: unsigned long Bank, Chris@0: unsigned long Program); Chris@0: Chris@0: /** Chris@0: * get_midi_controller_for_port() Chris@0: * Chris@0: * This member is a function pointer that returns the MIDI Chris@0: * controller number or NRPN that should be mapped to the given Chris@0: * input control port. If the given port should not have any MIDI Chris@0: * controller mapped to it, the function should return DSSI_NONE. Chris@0: * The behaviour of this function is undefined if the given port Chris@0: * number does not correspond to an input control port. A plugin Chris@0: * that does not want MIDI controllers mapped to ports at all may Chris@0: * set this member NULL. Chris@0: * Chris@0: * Correct values can be got using the macros DSSI_CC(num) and Chris@0: * DSSI_NRPN(num) as appropriate, and values can be combined using Chris@0: * bitwise OR: e.g. DSSI_CC(23) | DSSI_NRPN(1069) means the port Chris@0: * should respond to CC #23 and NRPN #1069. Chris@0: * Chris@0: * The host is responsible for doing proper scaling from MIDI Chris@0: * controller and NRPN value ranges to port ranges according to Chris@0: * the plugin's LADSPA port hints. Hosts should not deliver Chris@0: * through run_synth any MIDI controller events that have already Chris@0: * been mapped to control port values. Chris@0: * Chris@0: * A plugin should not attempt to request mappings from Chris@0: * controllers 0 or 32 (MIDI Bank Select MSB and LSB). Chris@0: */ Chris@0: int (*get_midi_controller_for_port)(LADSPA_Handle Instance, Chris@0: unsigned long Port); Chris@0: Chris@0: /** Chris@0: * run_synth() Chris@0: * Chris@0: * This member is a function pointer that runs a synth for a Chris@0: * block. This is identical in function to the LADSPA run() Chris@0: * function, except that it also supplies events to the synth. Chris@0: * Chris@0: * A plugin may provide this function, run_multiple_synths() (see Chris@0: * below), both, or neither (if it is not in fact a synth). A Chris@0: * plugin that does not provide this function must set this member Chris@0: * to NULL. Authors of synth plugins are encouraged to provide Chris@0: * this function if at all possible. Chris@0: * Chris@0: * The Events pointer points to a block of EventCount ALSA Chris@0: * sequencer events, which is used to communicate MIDI and related Chris@0: * events to the synth. Each event is timestamped relative to the Chris@0: * start of the block, (mis)using the ALSA "tick time" field as a Chris@0: * frame count. The host is responsible for ensuring that events Chris@0: * with differing timestamps are already ordered by time. Chris@0: * Chris@0: * See also the notes on activation, port connection etc in Chris@0: * ladpsa.h, in the context of the LADSPA run() function. Chris@0: * Chris@0: * Note Events Chris@0: * ~~~~~~~~~~~ Chris@0: * There are two minor requirements aimed at making the plugin Chris@0: * writer's life as simple as possible: Chris@0: * Chris@0: * 1. A host must never send events of type SND_SEQ_EVENT_NOTE. Chris@0: * Notes should always be sent as separate SND_SEQ_EVENT_NOTE_ON Chris@0: * and NOTE_OFF events. A plugin should discard any one-point Chris@0: * NOTE events it sees. Chris@0: * Chris@0: * 2. A host must not attempt to switch notes off by sending Chris@0: * zero-velocity NOTE_ON events. It should always send true Chris@0: * NOTE_OFFs. It is the host's responsibility to remap events in Chris@0: * cases where an external MIDI source has sent it zero-velocity Chris@0: * NOTE_ONs. Chris@0: * Chris@0: * Bank and Program Events Chris@0: * ~~~~~~~~~~~~~~~~~~~~~~~ Chris@0: * Hosts must map MIDI Bank Select MSB and LSB (0 and 32) Chris@0: * controllers and MIDI Program Change events onto the banks and Chris@0: * programs specified by the plugin, using the DSSI select_program Chris@0: * call. No host should ever deliver a program change or bank Chris@0: * select controller to a plugin via run_synth. Chris@0: */ Chris@0: void (*run_synth)(LADSPA_Handle Instance, Chris@0: unsigned long SampleCount, Chris@0: snd_seq_event_t *Events, Chris@0: unsigned long EventCount); Chris@0: Chris@0: /** Chris@0: * run_synth_adding() Chris@0: * Chris@0: * This member is a function pointer that runs an instance of a Chris@0: * synth for a block, adding its outputs to the values already Chris@0: * present at the output ports. This is provided for symmetry Chris@0: * with LADSPA run_adding(), and is equally optional. A plugin Chris@0: * that does not provide it must set this member to NULL. Chris@0: */ Chris@0: void (*run_synth_adding)(LADSPA_Handle Instance, Chris@0: unsigned long SampleCount, Chris@0: snd_seq_event_t *Events, Chris@0: unsigned long EventCount); Chris@0: Chris@0: /** Chris@0: * run_multiple_synths() Chris@0: * Chris@0: * This member is a function pointer that runs multiple synth Chris@0: * instances for a block. This is very similar to run_synth(), Chris@0: * except that Instances, Events, and EventCounts each point to Chris@0: * arrays that hold the LADSPA handles, event buffers, and Chris@0: * event counts for each of InstanceCount instances. That is, Chris@0: * Instances points to an array of InstanceCount pointers to Chris@0: * DSSI plugin instantiations, Events points to an array of Chris@0: * pointers to each instantiation's respective event list, and Chris@0: * EventCounts points to an array containing each instantiation's Chris@0: * respective event count. Chris@0: * Chris@0: * A host using this function must guarantee that ALL active Chris@0: * instances of the plugin are represented in each call to the Chris@0: * function -- that is, a host may not call run_multiple_synths() Chris@0: * for some instances of a given plugin and then call run_synth() Chris@0: * as well for others. 'All .. instances of the plugin' means Chris@0: * every instance sharing the same LADSPA label and shared object Chris@0: * (*.so) file (rather than every instance sharing the same *.so). Chris@0: * 'Active' means any instance for which activate() has been called Chris@0: * but deactivate() has not. Chris@0: * Chris@0: * A plugin may provide this function, run_synths() (see above), Chris@0: * both, or neither (if it not in fact a synth). A plugin that Chris@0: * does not provide this function must set this member to NULL. Chris@0: * Plugin authors implementing run_multiple_synths are strongly Chris@0: * encouraged to implement run_synth as well if at all possible, Chris@0: * to aid simplistic hosts, even where it would be less efficient Chris@0: * to use it. Chris@0: */ Chris@0: void (*run_multiple_synths)(unsigned long InstanceCount, Chris@0: LADSPA_Handle *Instances, Chris@0: unsigned long SampleCount, Chris@0: snd_seq_event_t **Events, Chris@0: unsigned long *EventCounts); Chris@0: Chris@0: /** Chris@0: * run_multiple_synths_adding() Chris@0: * Chris@0: * This member is a function pointer that runs multiple synth Chris@0: * instances for a block, adding each synth's outputs to the Chris@0: * values already present at the output ports. This is provided Chris@0: * for symmetry with both the DSSI run_multiple_synths() and LADSPA Chris@0: * run_adding() functions, and is equally optional. A plugin Chris@0: * that does not provide it must set this member to NULL. Chris@0: */ Chris@0: void (*run_multiple_synths_adding)(unsigned long InstanceCount, Chris@0: LADSPA_Handle *Instances, Chris@0: unsigned long SampleCount, Chris@0: snd_seq_event_t **Events, Chris@0: unsigned long *EventCounts); Chris@0: Chris@0: #if (DSSI_API_LEVEL > 1) Chris@0: Chris@0: /** Chris@0: * receive_host_descriptor() Chris@0: * Chris@0: * This member is a function pointer by which a host may provide Chris@0: * a plugin with a pointer to its DSSI_Host_Descriptor. Hosts Chris@0: * which provide host descriptor support must call this function Chris@0: * once per plugin shared object file, before any calls to Chris@0: * instantiate(). Chris@0: * Chris@0: * NOTE: This field was added in version 2 of the DSSI API. Hosts Chris@0: * supporting version 2 must not access this field in a plugin Chris@0: * whose DSSI_API_Version is 1, and plugins supporting version 2 Chris@0: * should behave reasonably under hosts (of any version) which do Chris@0: * not implement this function. A version 2 plugin that does not Chris@0: * provide this function must set this member to NULL. Chris@0: */ Chris@0: void (*receive_host_descriptor)(const DSSI_Host_Descriptor *Descriptor); Chris@0: Chris@0: #endif Chris@0: Chris@0: } DSSI_Descriptor; Chris@0: Chris@0: struct _DSSI_Host_Descriptor { Chris@0: Chris@0: /** Chris@0: * DSSI_API_Version Chris@0: * Chris@0: * This member indicates the DSSI API level used by this host. Chris@0: * All hosts must set this to 2. Hopefully, we'll get this right Chris@0: * the first time, and this will never be needed. Chris@0: */ Chris@0: int DSSI_API_Version; Chris@0: Chris@0: /** Chris@0: * request_transport_information() Chris@0: * Chris@0: * This member is a function pointer by which a plugin instance may Chris@0: * request that a host begin providing transport information (if Chris@0: * Request is non-zero), or notify the host that it no longer needs Chris@0: * transport information (if Request is zero). Upon receiving a Chris@0: * non-zero request, the host should return a pointer to a Chris@0: * DSSI_Transport_Info structure if it is able to provide transport Chris@0: * information, or NULL otherwise. Chris@0: * Chris@0: * Once a plugin instance has received a non-null transport Chris@0: * information pointer, it may read from the structure at any time Chris@0: * within the execution of an audio class function (see doc/RFC.txt). Chris@0: * It should not consider the structure contents to be meaningful Chris@0: * while within a instantiation or control class function. Also, Chris@0: * since the validity of fields within the structure may change Chris@0: * between each new invocation of an audio class function, a plugin Chris@0: * instance must check the Valid field of the structure accordingly Chris@0: * before using the structure's other contents. Chris@0: * Chris@0: * A host which does not support this function must set this member Chris@0: * to NULL. Chris@0: */ Chris@0: DSSI_Transport_Info * Chris@0: (*request_transport_information)(LADSPA_Handle Instance, Chris@0: int Request); Chris@0: Chris@0: /** Chris@0: * request_midi_send() Chris@0: * Chris@0: * This member is a function pointer that allows a plugin to Chris@0: * request the ability to send MIDI events to the host. Chris@0: * Chris@0: * While the interpretation of plugin-generated MIDI events is Chris@0: * host implementation specific, a mechanism exists by which a Chris@0: * plugin may declare to the host the number of destination Chris@0: * 'ports' and MIDI channels it can expect will be used in the Chris@0: * plugin-generated events. Plugins which generate unchannelized Chris@0: * MIDI should supply zero for both Ports and Channels, otherwise Chris@0: * they should supply the maximum numbers for Ports and Channels Chris@0: * they expect to use. Chris@0: * Chris@0: * A plugin instance must call this function during instantiate(). Chris@0: * [Sean says: this restriction seems reasonable to me, since Chris@0: * the host may need to create output ports, etc., and instantiate() Chris@0: * seems like a good place to do such things. I'm sure I haven't Chris@0: * fully thought through all the details, though....] Chris@0: * Chris@0: * The host should return a non-zero value if it is able to Chris@0: * provide MIDI send for the plugin instance, otherwise it should Chris@0: * return zero, and the plugin instance may not subsequently call Chris@0: * midi_send(). Chris@0: * Chris@0: * A host which does not support the MIDI send function must set Chris@0: * both this member and (*midi_send)() below to NULL. Chris@0: */ Chris@0: int (*request_midi_send)(LADSPA_Handle Instance, Chris@0: unsigned char Ports, Chris@0: unsigned char Channels); Chris@0: Chris@0: /** Chris@0: * midi_send() Chris@0: * Chris@0: * This member is a function pointer by which a plugin actually Chris@0: * sends MIDI events to the host (provided it has received a non- Chris@0: * zero return from request_midi_send()). As in the run_synth() Chris@0: * functions, the Event pointer points to a block of EventCount Chris@0: * ALSA sequencer events. The dest.port and data.*.channel fields Chris@0: * of each event are used to specify destination port and channel, Chris@0: * respectively, when the plugin is supplying channelized events. Chris@0: * Chris@0: * A plugin may only call this function from within the execution Chris@0: * of the audio class run_*() or select_program() functions. When Chris@0: * called from a run_*() functions, the events are timestamped Chris@0: * relative to the start of the block, (mis)using the ALSA "tick Chris@0: * time" field as a frame count. The plugin is responsible for Chris@0: * ensuring that events with differing timestamps are already Chris@0: * ordered by time, and that timestamps across multiple calls to Chris@0: * midi_send() from within the same run_*() invocation are Chris@0: * monotonic. When midi_send() is called from within Chris@0: * select_program(), the timestamps are ignored, and the events Chris@0: * are considered to originate at the same frame time as the Chris@0: * select_program() call, if such a timing can be considered Chris@0: * meaningful. Chris@0: * Chris@0: * The memory pointed to by Event belongs to the plugin, and it is Chris@0: * the host's responsibility to copy the events as needed before Chris@0: * returning from the midi_send() call. Chris@0: * Chris@0: * A host which does not support the MIDI send function must set Chris@0: * both this member and (*request_midi_send)() above to NULL. Chris@0: */ Chris@0: void (*midi_send)(LADSPA_Handle Instance, Chris@0: snd_seq_event_t *Event, Chris@0: unsigned long EventCount); Chris@0: Chris@0: /** Chris@0: * . . . additional fields could follow here, possibly supporting: Chris@0: * Chris@0: * - a facility by which a plugin instance may request from a Chris@0: * host a non-realtime thread in which to do off-line Chris@0: * rendering, I/O, etc., thus (hopefully) avoiding the Chris@0: * crashes that seem to occur when plugins create their own Chris@0: * threads. I got this idea after noticing that ZynAddSubFX Chris@0: * achieves its gorgeous textures while remaining very Chris@0: * responsive by doing a lot of non-real-time rendering. Chris@0: * Several other uses for it have been mentioned on the DSSI Chris@0: * list; I forget what. Chris@0: * Chris@0: * - per-voice audio output Chris@0: */ Chris@0: Chris@0: int (*request_non_rt_thread)(LADSPA_Handle Instance, Chris@0: void (*RunFunction)(LADSPA_Handle Instance)); Chris@0: }; Chris@0: Chris@0: /** Chris@0: * DSSI supports a plugin discovery method similar to that of LADSPA: Chris@0: * Chris@0: * - DSSI hosts may wish to locate DSSI plugin shared object files by Chris@0: * searching the paths contained in the DSSI_PATH and LADSPA_PATH Chris@0: * environment variables, if they are present. Both are expected Chris@0: * to be colon-separated lists of directories to be searched (in Chris@0: * order), and DSSI_PATH should be searched first if both variables Chris@0: * are set. Chris@0: * Chris@0: * - Each shared object file containing DSSI plugins must include a Chris@0: * function dssi_descriptor(), with the following function prototype Chris@0: * and C-style linkage. Hosts may enumerate the plugin types Chris@0: * available in the shared object file by repeatedly calling Chris@0: * this function with successive Index values (beginning from 0), Chris@0: * until a return value of NULL indicates no more plugin types are Chris@0: * available. Each non-NULL return is the DSSI_Descriptor Chris@0: * of a distinct plugin type. Chris@0: */ Chris@0: Chris@0: const DSSI_Descriptor *dssi_descriptor(unsigned long Index); Chris@0: Chris@0: typedef const DSSI_Descriptor *(*DSSI_Descriptor_Function)(unsigned long Index); Chris@0: Chris@0: /* Chris@0: * Macros to specify particular MIDI controllers in return values from Chris@0: * get_midi_controller_for_port() Chris@0: */ Chris@0: Chris@0: #define DSSI_CC_BITS 0x20000000 Chris@0: #define DSSI_NRPN_BITS 0x40000000 Chris@0: Chris@0: #define DSSI_NONE -1 Chris@0: #define DSSI_CONTROLLER_IS_SET(n) (DSSI_NONE != (n)) Chris@0: Chris@0: #define DSSI_CC(n) (DSSI_CC_BITS | (n)) Chris@0: #define DSSI_IS_CC(n) (DSSI_CC_BITS & (n)) Chris@0: #define DSSI_CC_NUMBER(n) ((n) & 0x7f) Chris@0: Chris@0: #define DSSI_NRPN(n) (DSSI_NRPN_BITS | ((n) << 7)) Chris@0: #define DSSI_IS_NRPN(n) (DSSI_NRPN_BITS & (n)) Chris@0: #define DSSI_NRPN_NUMBER(n) (((n) >> 7) & 0x3fff) Chris@0: Chris@0: #ifdef __cplusplus Chris@0: } Chris@0: #endif Chris@0: Chris@0: #endif /* DSSI_INCLUDED */