From f00c2cba30cf3151674bf2b73dab98660009f2c5 Mon Sep 17 00:00:00 2001 From: Jamie Bullock Date: Tue, 21 Sep 2010 15:40:20 +0000 Subject: - implemented |dssi remap( message. This remaps MIDI channel numbers to instances. e.g. |dssi remap 1 1 2( maps channel 1 to instances 1 & 2, and channel 2 to instance 3. svn path=/trunk/externals/postlude/; revision=14166 --- dssi/TODO | 30 --- dssi/include/dssi.h | 426 +++++++++++++++++++++++++++++++++++ dssi/include/ladspa.h | 603 ++++++++++++++++++++++++++++++++++++++++++++++++++ dssi/makefile | 5 +- dssi/src/dssi~.c | 522 +++++++++++++++++++++++-------------------- dssi/src/dssi~.h | 3 +- 6 files changed, 1312 insertions(+), 277 deletions(-) delete mode 100644 dssi/TODO create mode 100644 dssi/include/dssi.h create mode 100644 dssi/include/ladspa.h diff --git a/dssi/TODO b/dssi/TODO deleted file mode 100644 index 6f62751..0000000 --- a/dssi/TODO +++ /dev/null @@ -1,30 +0,0 @@ - 1. Fix memory allocation issues. - DONE! - 2. Restore Trivial Synth compatibility, i.e. is GUI fails, don't crash. - DONE! - 3. Reomve alsa.h dependency - Included header file! - 4. Get Hexter/Fluidsynth working - DONE! - 5. Get LTS working (full functionality i.e Pitch Bend, Control etc.) - DONE! - 6. Do OSC/GUI - DONE! - 7. Multiple instances -DONE! - 8. If OSC is received from another source - update the GUI. - 9. Patch saving/loading. -DONE (for Hexter)! - 10. Make GUI close when app closes, or new plugin loaded. -DONE! - 11. Make ll-scope work. - 12. Fix note hangs. - DONE - this was due to precedence problems in the PD patch! - 13. Fix inability to run two instances of the dssi~ external. - DONE! - 14. Fix exiting handler -DONE! - 15. Fix GUI close when PD quit - don't leave defunct process - 16. Fix free() call if plugin isn't loaded successfully -DONE! - 17. Implement GUI show/hide. -DONE! - 18. Fix segfault if dssi~ recieves on MIDI channel it doesn't have an instance for -DONE! - 19. Fix program crash when patch load if audio running. -DONE! - 20. Make info logged to pd console more meaningful - 21. Add more error checking. - 22. Fix: global polyphony handling - DONE! - 23. Fix: config malloc bugs - 24. Fix: exit call instance deactivate function - DONE! - 25. Check DSSI spec conformity! - 26. FIX: Why does is incorrect patch name chown when program is changed from GUI? - DONE! - query_programs must be sent for each configure call - 27. FIX: Generic valgrind error - also in jack-dssi-host. - 28. FIX: crash on close if incorrect plugin path given on command line -DONE! - 29. FIX: crashes if load new patches, set programs, play sequence then set new programs - run through GDB - 30. FIX: fluidsynth-dssi errors. Example [pd guts] in madonna demo ctrl-x, ctrl-v crashes PD. diff --git a/dssi/include/dssi.h b/dssi/include/dssi.h new file mode 100644 index 0000000..dd0cd91 --- /dev/null +++ b/dssi/include/dssi.h @@ -0,0 +1,426 @@ +/* -*- c-basic-offset: 4 -*- */ + +/* dssi.h + + DSSI version 0.9 + Copyright (c) 2004 Chris Cannam, Steve Harris and Sean Bolton + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +*/ + +#ifndef DSSI_INCLUDED +#define DSSI_INCLUDED + +#include +#include + +#define DSSI_VERSION "0.9" +#define DSSI_VERSION_MAJOR 0 +#define DSSI_VERSION_MINOR 9 + +#ifdef __cplusplus +extern "C" { +#endif + +/* + There is a need for an API that supports hosted MIDI soft synths + with GUIs in Linux audio applications. In time the GMPI initiative + should comprehensively address this need, but the requirement for + Linux applications to be able to support simple hosted synths is + here now, and GMPI is not. This proposal (the "DSSI Soft Synth + Interface" or DSSI, pronounced "dizzy") aims to provide a simple + solution in a way that we hope will prove complete and compelling + enough to support now, yet not so compelling as to supplant GMPI or + any other comprehensive future proposal. + + For simplicity and familiarity, this API is based as far as + possible on existing work -- the LADSPA plugin API for control + values and audio processing, and the ALSA sequencer event types for + MIDI event communication. The GUI part of the proposal is quite + new, but may also be applicable retroactively to LADSPA plugins + that do not otherwise support this synth interface. +*/ + +typedef struct _DSSI_Program_Descriptor { + + /** Bank number for this program. Note that DSSI does not support + MIDI-style separation of bank LSB and MSB values. There is no + restriction on the set of available banks: the numbers do not + need to be contiguous, there does not need to be a bank 0, etc. */ + unsigned long Bank; + + /** Program number (unique within its bank) for this program. + There is no restriction on the set of available programs: the + numbers do not need to be contiguous, there does not need to + be a program 0, etc. */ + unsigned long Program; + + /** Name of the program. */ + const char * Name; + +} DSSI_Program_Descriptor; + + +typedef struct _DSSI_Descriptor { + + /** + * DSSI_API_Version + * + * This member indicates the DSSI API level used by this plugin. + * If we're lucky, this will never be needed. For now all plugins + * must set it to 1. + */ + int DSSI_API_Version; + + /** + * LADSPA_Plugin + * + * A DSSI synth plugin consists of a LADSPA plugin plus an + * additional framework for controlling program settings and + * transmitting MIDI events. A plugin must fully implement the + * LADSPA descriptor fields as well as the required LADSPA + * functions including instantiate() and (de)activate(). It + * should also implement run(), with the same behaviour as if + * run_synth() (below) were called with no synth events. + * + * In order to instantiate a synth the host calls the LADSPA + * instantiate function, passing in this LADSPA_Descriptor + * pointer. The returned LADSPA_Handle is used as the argument + * for the DSSI functions below as well as for the LADSPA ones. + */ + const LADSPA_Descriptor *LADSPA_Plugin; + + /** + * configure() + * + * This member is a function pointer that sends a piece of + * configuration data to the plugin. The key argument specifies + * some aspect of the synth's configuration that is to be changed, + * and the value argument specifies a new value for it. A plugin + * that does not require this facility at all may set this member + * to NULL. + * + * This call is intended to set some session-scoped aspect of a + * plugin's behaviour, for example to tell the plugin to load + * sample data from a particular file. The plugin should act + * immediately on the request. The call should return NULL on + * success, or an error string that may be shown to the user. The + * host will free the returned value after use if it is non-NULL. + * + * Calls to configure() are not automated as timed events. + * Instead, a host should remember the last value associated with + * each key passed to configure() during a given session for a + * given plugin instance, and should call configure() with the + * correct value for each key the next time it instantiates the + * "same" plugin instance, for example on reloading a project in + * which the plugin was used before. Plugins should note that a + * host may typically instantiate a plugin multiple times with the + * same configuration values, and should share data between + * instances where practical. + * + * Calling configure() completely invalidates the program and bank + * information last obtained from the plugin. + * + * Reserved and special key prefixes + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * The DSSI: prefix + * ---------------- + * Configure keys starting with DSSI: are reserved for particular + * purposes documented in the DSSI specification. At the moment, + * there is one such key: DSSI:PROJECT_DIRECTORY. A host may call + * configure() passing this key and a directory path value. This + * indicates to the plugin and its UI that a directory at that + * path exists and may be used for project-local data. Plugins + * may wish to use the project directory as a fallback location + * when looking for other file data, or as a base for relative + * paths in other configuration values. + * + * The GLOBAL: prefix + * ------------------ + * Configure keys starting with GLOBAL: may be used by the plugin + * and its UI for any purpose, but are treated specially by the + * host. When one of these keys is used in a configure OSC call + * from the plugin UI, the host makes the corresponding configure + * call (preserving the GLOBAL: prefix) not only to the target + * plugin but also to all other plugins in the same instance + * group, as well as their UIs. Note that if any instance + * returns non-NULL from configure to indicate error, the host + * may stop there (and the set of plugins on which configure has + * been called will thus depend on the host implementation). + * See also the configure OSC call documentation in RFC.txt. + */ + char *(*configure)(LADSPA_Handle Instance, + const char *Key, + const char *Value); + + #define DSSI_RESERVED_CONFIGURE_PREFIX "DSSI:" + #define DSSI_GLOBAL_CONFIGURE_PREFIX "GLOBAL:" + #define DSSI_PROJECT_DIRECTORY_KEY \ + DSSI_RESERVED_CONFIGURE_PREFIX "PROJECT_DIRECTORY" + + /** + * get_program() + * + * This member is a function pointer that provides a description + * of a program (named preset sound) available on this synth. A + * plugin that does not support programs at all should set this + * member to NULL. + * + * The Index argument is an index into the plugin's list of + * programs, not a program number as represented by the Program + * field of the DSSI_Program_Descriptor. (This distinction is + * needed to support synths that use non-contiguous program or + * bank numbers.) + * + * This function returns a DSSI_Program_Descriptor pointer that is + * guaranteed to be valid only until the next call to get_program, + * deactivate, or configure, on the same plugin instance. This + * function must return NULL if passed an Index argument out of + * range, so that the host can use it to query the number of + * programs as well as their properties. + */ + const DSSI_Program_Descriptor *(*get_program)(LADSPA_Handle Instance, + unsigned long Index); + + /** + * select_program() + * + * This member is a function pointer that selects a new program + * for this synth. The program change should take effect + * immediately at the start of the next run_synth() call. (This + * means that a host providing the capability of changing programs + * between any two notes on a track must vary the block size so as + * to place the program change at the right place. A host that + * wanted to avoid this would probably just instantiate a plugin + * for each program.) + * + * A plugin that does not support programs at all should set this + * member NULL. Plugins should ignore a select_program() call + * with an invalid bank or program. + * + * A plugin is not required to select any particular default + * program on activate(): it's the host's duty to set a program + * explicitly. The current program is invalidated by any call to + * configure(). + * + * A plugin is permitted to re-write the values of its input + * control ports when select_program is called. The host should + * re-read the input control port values and update its own + * records appropriately. (This is the only circumstance in + * which a DSSI plugin is allowed to modify its own input ports.) + */ + void (*select_program)(LADSPA_Handle Instance, + unsigned long Bank, + unsigned long Program); + + /** + * get_midi_controller_for_port() + * + * This member is a function pointer that returns the MIDI + * controller number or NRPN that should be mapped to the given + * input control port. If the given port should not have any MIDI + * controller mapped to it, the function should return DSSI_NONE. + * The behaviour of this function is undefined if the given port + * number does not correspond to an input control port. A plugin + * that does not want MIDI controllers mapped to ports at all may + * set this member NULL. + * + * Correct values can be got using the macros DSSI_CC(num) and + * DSSI_NRPN(num) as appropriate, and values can be combined using + * bitwise OR: e.g. DSSI_CC(23) | DSSI_NRPN(1069) means the port + * should respond to CC #23 and NRPN #1069. + * + * The host is responsible for doing proper scaling from MIDI + * controller and NRPN value ranges to port ranges according to + * the plugin's LADSPA port hints. Hosts should not deliver + * through run_synth any MIDI controller events that have already + * been mapped to control port values. + * + * A plugin should not attempt to request mappings from + * controllers 0 or 32 (MIDI Bank Select MSB and LSB). + */ + int (*get_midi_controller_for_port)(LADSPA_Handle Instance, + unsigned long Port); + + /** + * run_synth() + * + * This member is a function pointer that runs a synth for a + * block. This is identical in function to the LADSPA run() + * function, except that it also supplies events to the synth. + * + * A plugin may provide this function, run_multiple_synths() (see + * below), both, or neither (if it is not in fact a synth). A + * plugin that does not provide this function must set this member + * to NULL. Authors of synth plugins are encouraged to provide + * this function if at all possible. + * + * The Events pointer points to a block of EventCount ALSA + * sequencer events, which is used to communicate MIDI and related + * events to the synth. Each event is timestamped relative to the + * start of the block, (mis)using the ALSA "tick time" field as a + * frame count. The host is responsible for ensuring that events + * with differing timestamps are already ordered by time. + * + * See also the notes on activation, port connection etc in + * ladpsa.h, in the context of the LADSPA run() function. + * + * Note Events + * ~~~~~~~~~~~ + * There are two minor requirements aimed at making the plugin + * writer's life as simple as possible: + * + * 1. A host must never send events of type SND_SEQ_EVENT_NOTE. + * Notes should always be sent as separate SND_SEQ_EVENT_NOTE_ON + * and NOTE_OFF events. A plugin should discard any one-point + * NOTE events it sees. + * + * 2. A host must not attempt to switch notes off by sending + * zero-velocity NOTE_ON events. It should always send true + * NOTE_OFFs. It is the host's responsibility to remap events in + * cases where an external MIDI source has sent it zero-velocity + * NOTE_ONs. + * + * Bank and Program Events + * ~~~~~~~~~~~~~~~~~~~~~~~ + * Hosts must map MIDI Bank Select MSB and LSB (0 and 32) + * controllers and MIDI Program Change events onto the banks and + * programs specified by the plugin, using the DSSI select_program + * call. No host should ever deliver a program change or bank + * select controller to a plugin via run_synth. + */ + void (*run_synth)(LADSPA_Handle Instance, + unsigned long SampleCount, + snd_seq_event_t *Events, + unsigned long EventCount); + + /** + * run_synth_adding() + * + * This member is a function pointer that runs an instance of a + * synth for a block, adding its outputs to the values already + * present at the output ports. This is provided for symmetry + * with LADSPA run_adding(), and is equally optional. A plugin + * that does not provide it must set this member to NULL. + */ + void (*run_synth_adding)(LADSPA_Handle Instance, + unsigned long SampleCount, + snd_seq_event_t *Events, + unsigned long EventCount); + + /** + * run_multiple_synths() + * + * This member is a function pointer that runs multiple synth + * instances for a block. This is very similar to run_synth(), + * except that Instances, Events, and EventCounts each point to + * arrays that hold the LADSPA handles, event buffers, and + * event counts for each of InstanceCount instances. That is, + * Instances points to an array of InstanceCount pointers to + * DSSI plugin instantiations, Events points to an array of + * pointers to each instantiation's respective event list, and + * EventCounts points to an array containing each instantiation's + * respective event count. + * + * A host using this function must guarantee that ALL active + * instances of the plugin are represented in each call to the + * function -- that is, a host may not call run_multiple_synths() + * for some instances of a given plugin and then call run_synth() + * as well for others. 'All .. instances of the plugin' means + * every instance sharing the same LADSPA label and shared object + * (*.so) file (rather than every instance sharing the same *.so). + * 'Active' means any instance for which activate() has been called + * but deactivate() has not. + * + * A plugin may provide this function, run_synths() (see above), + * both, or neither (if it not in fact a synth). A plugin that + * does not provide this function must set this member to NULL. + * Plugin authors implementing run_multiple_synths are strongly + * encouraged to implement run_synth as well if at all possible, + * to aid simplistic hosts, even where it would be less efficient + * to use it. + */ + void (*run_multiple_synths)(unsigned long InstanceCount, + LADSPA_Handle *Instances, + unsigned long SampleCount, + snd_seq_event_t **Events, + unsigned long *EventCounts); + + /** + * run_multiple_synths_adding() + * + * This member is a function pointer that runs multiple synth + * instances for a block, adding each synth's outputs to the + * values already present at the output ports. This is provided + * for symmetry with both the DSSI run_multiple_synths() and LADSPA + * run_adding() functions, and is equally optional. A plugin + * that does not provide it must set this member to NULL. + */ + void (*run_multiple_synths_adding)(unsigned long InstanceCount, + LADSPA_Handle *Instances, + unsigned long SampleCount, + snd_seq_event_t **Events, + unsigned long *EventCounts); +} DSSI_Descriptor; + +/** + * DSSI supports a plugin discovery method similar to that of LADSPA: + * + * - DSSI hosts may wish to locate DSSI plugin shared object files by + * searching the paths contained in the DSSI_PATH and LADSPA_PATH + * environment variables, if they are present. Both are expected + * to be colon-separated lists of directories to be searched (in + * order), and DSSI_PATH should be searched first if both variables + * are set. + * + * - Each shared object file containing DSSI plugins must include a + * function dssi_descriptor(), with the following function prototype + * and C-style linkage. Hosts may enumerate the plugin types + * available in the shared object file by repeatedly calling + * this function with successive Index values (beginning from 0), + * until a return value of NULL indicates no more plugin types are + * available. Each non-NULL return is the DSSI_Descriptor + * of a distinct plugin type. + */ + +const DSSI_Descriptor *dssi_descriptor(unsigned long Index); + +typedef const DSSI_Descriptor *(*DSSI_Descriptor_Function)(unsigned long Index); + +/* + * Macros to specify particular MIDI controllers in return values from + * get_midi_controller_for_port() + */ + +#define DSSI_CC_BITS 0x20000000 +#define DSSI_NRPN_BITS 0x40000000 + +#define DSSI_NONE -1 +#define DSSI_CONTROLLER_IS_SET(n) (DSSI_NONE != (n)) + +#define DSSI_CC(n) (DSSI_CC_BITS | (n)) +#define DSSI_IS_CC(n) (DSSI_CC_BITS & (n)) +#define DSSI_CC_NUMBER(n) ((n) & 0x7f) + +#define DSSI_NRPN(n) (DSSI_NRPN_BITS | ((n) << 7)) +#define DSSI_IS_NRPN(n) (DSSI_NRPN_BITS & (n)) +#define DSSI_NRPN_NUMBER(n) (((n) >> 7) & 0x3fff) + +#ifdef __cplusplus +} +#endif + +#endif /* DSSI_INCLUDED */ diff --git a/dssi/include/ladspa.h b/dssi/include/ladspa.h new file mode 100644 index 0000000..5c30a8a --- /dev/null +++ b/dssi/include/ladspa.h @@ -0,0 +1,603 @@ +/* ladspa.h + + Linux Audio Developer's Simple Plugin API Version 1.1[LGPL]. + Copyright (C) 2000-2002 Richard W.E. Furse, Paul Barton-Davis, + Stefan Westerfeld. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. */ + +#ifndef LADSPA_INCLUDED +#define LADSPA_INCLUDED + +#define LADSPA_VERSION "1.1" +#define LADSPA_VERSION_MAJOR 1 +#define LADSPA_VERSION_MINOR 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/*****************************************************************************/ + +/* Overview: + + There is a large number of synthesis packages in use or development + on the Linux platform at this time. This API (`The Linux Audio + Developer's Simple Plugin API') attempts to give programmers the + ability to write simple `plugin' audio processors in C/C++ and link + them dynamically (`plug') into a range of these packages (`hosts'). + It should be possible for any host and any plugin to communicate + completely through this interface. + + This API is deliberately short and simple. To achieve compatibility + with a range of promising Linux sound synthesis packages it + attempts to find the `greatest common divisor' in their logical + behaviour. Having said this, certain limiting decisions are + implicit, notably the use of a fixed type (LADSPA_Data) for all + data transfer and absence of a parameterised `initialisation' + phase. See below for the LADSPA_Data typedef. + + Plugins are expected to distinguish between control and audio + data. Plugins have `ports' that are inputs or outputs for audio or + control data and each plugin is `run' for a `block' corresponding + to a short time interval measured in samples. Audio data is + communicated using arrays of LADSPA_Data, allowing a block of audio + to be processed by the plugin in a single pass. Control data is + communicated using single LADSPA_Data values. Control data has a + single value at the start of a call to the `run()' or `run_adding()' + function, and may be considered to remain this value for its + duration. The plugin may assume that all its input and output ports + have been connected to the relevant data location (see the + `connect_port()' function below) before it is asked to run. + + Plugins will reside in shared object files suitable for dynamic + linking by dlopen() and family. The file will provide a number of + `plugin types' that can be used to instantiate actual plugins + (sometimes known as `plugin instances') that can be connected + together to perform tasks. + + This API contains very limited error-handling. */ + +/*****************************************************************************/ + +/* Fundamental data type passed in and out of plugin. This data type + is used to communicate audio samples and control values. It is + assumed that the plugin will work sensibly given any numeric input + value although it may have a preferred range (see hints below). + + For audio it is generally assumed that 1.0f is the `0dB' reference + amplitude and is a `normal' signal level. */ + +typedef float LADSPA_Data; + +/*****************************************************************************/ + +/* Special Plugin Properties: + + Optional features of the plugin type are encapsulated in the + LADSPA_Properties type. This is assembled by ORing individual + properties together. */ + +typedef int LADSPA_Properties; + +/* Property LADSPA_PROPERTY_REALTIME indicates that the plugin has a + real-time dependency (e.g. listens to a MIDI device) and so its + output must not be cached or subject to significant latency. */ +#define LADSPA_PROPERTY_REALTIME 0x1 + +/* Property LADSPA_PROPERTY_INPLACE_BROKEN indicates that the plugin + may cease to work correctly if the host elects to use the same data + location for both input and output (see connect_port()). This + should be avoided as enabling this flag makes it impossible for + hosts to use the plugin to process audio `in-place.' */ +#define LADSPA_PROPERTY_INPLACE_BROKEN 0x2 + +/* Property LADSPA_PROPERTY_HARD_RT_CAPABLE indicates that the plugin + is capable of running not only in a conventional host but also in a + `hard real-time' environment. To qualify for this the plugin must + satisfy all of the following: + + (1) The plugin must not use malloc(), free() or other heap memory + management within its run() or run_adding() functions. All new + memory used in run() must be managed via the stack. These + restrictions only apply to the run() function. + + (2) The plugin will not attempt to make use of any library + functions with the exceptions of functions in the ANSI standard C + and C maths libraries, which the host is expected to provide. + + (3) The plugin will not access files, devices, pipes, sockets, IPC + or any other mechanism that might result in process or thread + blocking. + + (4) The plugin will take an amount of time to execute a run() or + run_adding() call approximately of form (A+B*SampleCount) where A + and B depend on the machine and host in use. This amount of time + may not depend on input signals or plugin state. The host is left + the responsibility to perform timings to estimate upper bounds for + A and B. */ +#define LADSPA_PROPERTY_HARD_RT_CAPABLE 0x4 + +#define LADSPA_IS_REALTIME(x) ((x) & LADSPA_PROPERTY_REALTIME) +#define LADSPA_IS_INPLACE_BROKEN(x) ((x) & LADSPA_PROPERTY_INPLACE_BROKEN) +#define LADSPA_IS_HARD_RT_CAPABLE(x) ((x) & LADSPA_PROPERTY_HARD_RT_CAPABLE) + +/*****************************************************************************/ + +/* Plugin Ports: + + Plugins have `ports' that are inputs or outputs for audio or + data. Ports can communicate arrays of LADSPA_Data (for audio + inputs/outputs) or single LADSPA_Data values (for control + input/outputs). This information is encapsulated in the + LADSPA_PortDescriptor type which is assembled by ORing individual + properties together. + + Note that a port must be an input or an output port but not both + and that a port must be a control or audio port but not both. */ + +typedef int LADSPA_PortDescriptor; + +/* Property LADSPA_PORT_INPUT indicates that the port is an input. */ +#define LADSPA_PORT_INPUT 0x1 + +/* Property LADSPA_PORT_OUTPUT indicates that the port is an output. */ +#define LADSPA_PORT_OUTPUT 0x2 + +/* Property LADSPA_PORT_CONTROL indicates that the port is a control + port. */ +#define LADSPA_PORT_CONTROL 0x4 + +/* Property LADSPA_PORT_AUDIO indicates that the port is a audio + port. */ +#define LADSPA_PORT_AUDIO 0x8 + +#define LADSPA_IS_PORT_INPUT(x) ((x) & LADSPA_PORT_INPUT) +#define LADSPA_IS_PORT_OUTPUT(x) ((x) & LADSPA_PORT_OUTPUT) +#define LADSPA_IS_PORT_CONTROL(x) ((x) & LADSPA_PORT_CONTROL) +#define LADSPA_IS_PORT_AUDIO(x) ((x) & LADSPA_PORT_AUDIO) + +/*****************************************************************************/ + +/* Plugin Port Range Hints: + + The host may wish to provide a representation of data entering or + leaving a plugin (e.g. to generate a GUI automatically). To make + this more meaningful, the plugin should provide `hints' to the host + describing the usual values taken by the data. + + Note that these are only hints. The host may ignore them and the + plugin must not assume that data supplied to it is meaningful. If + the plugin receives invalid input data it is expected to continue + to run without failure and, where possible, produce a sensible + output (e.g. a high-pass filter given a negative cutoff frequency + might switch to an all-pass mode). + + Hints are meaningful for all input and output ports but hints for + input control ports are expected to be particularly useful. + + More hint information is encapsulated in the + LADSPA_PortRangeHintDescriptor type which is assembled by ORing + individual hint types together. Hints may require further + LowerBound and UpperBound information. + + All the hint information for a particular port is aggregated in the + LADSPA_PortRangeHint structure. */ + +typedef int LADSPA_PortRangeHintDescriptor; + +/* Hint LADSPA_HINT_BOUNDED_BELOW indicates that the LowerBound field + of the LADSPA_PortRangeHint should be considered meaningful. The + value in this field should be considered the (inclusive) lower + bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also + specified then the value of LowerBound should be multiplied by the + sample rate. */ +#define LADSPA_HINT_BOUNDED_BELOW 0x1 + +/* Hint LADSPA_HINT_BOUNDED_ABOVE indicates that the UpperBound field + of the LADSPA_PortRangeHint should be considered meaningful. The + value in this field should be considered the (inclusive) upper + bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also + specified then the value of UpperBound should be multiplied by the + sample rate. */ +#define LADSPA_HINT_BOUNDED_ABOVE 0x2 + +/* Hint LADSPA_HINT_TOGGLED indicates that the data item should be + considered a Boolean toggle. Data less than or equal to zero should + be considered `off' or `false,' and data above zero should be + considered `on' or `true.' LADSPA_HINT_TOGGLED may not be used in + conjunction with any other hint except LADSPA_HINT_DEFAULT_0 or + LADSPA_HINT_DEFAULT_1. */ +#define LADSPA_HINT_TOGGLED 0x4 + +/* Hint LADSPA_HINT_SAMPLE_RATE indicates that any bounds specified + should be interpreted as multiples of the sample rate. For + instance, a frequency range from 0Hz to the Nyquist frequency (half + the sample rate) could be requested by this hint in conjunction + with LowerBound = 0 and UpperBound = 0.5. Hosts that support bounds + at all must support this hint to retain meaning. */ +#define LADSPA_HINT_SAMPLE_RATE 0x8 + +/* Hint LADSPA_HINT_LOGARITHMIC indicates that it is likely that the + user will find it more intuitive to view values using a logarithmic + scale. This is particularly useful for frequencies and gains. */ +#define LADSPA_HINT_LOGARITHMIC 0x10 + +/* Hint LADSPA_HINT_INTEGER indicates that a user interface would + probably wish to provide a stepped control taking only integer + values. Any bounds set should be slightly wider than the actual + integer range required to avoid floating point rounding errors. For + instance, the integer set {0,1,2,3} might be described as [-0.1, + 3.1]. */ +#define LADSPA_HINT_INTEGER 0x20 + +/* The various LADSPA_HINT_HAS_DEFAULT_* hints indicate a `normal' + value for the port that is sensible as a default. For instance, + this value is suitable for use as an initial value in a user + interface or as a value the host might assign to a control port + when the user has not provided one. Defaults are encoded using a + mask so only one default may be specified for a port. Some of the + hints make use of lower and upper bounds, in which case the + relevant bound or bounds must be available and + LADSPA_HINT_SAMPLE_RATE must be applied as usual. The resulting + default must be rounded if LADSPA_HINT_INTEGER is present. Default + values were introduced in LADSPA v1.1. */ +#define LADSPA_HINT_DEFAULT_MASK 0x3C0 + +/* This default values indicates that no default is provided. */ +#define LADSPA_HINT_DEFAULT_NONE 0x0 + +/* This default hint indicates that the suggested lower bound for the + port should be used. */ +#define LADSPA_HINT_DEFAULT_MINIMUM 0x40 + +/* This default hint indicates that a low value between the suggested + lower and upper bounds should be chosen. For ports with + LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.75 + + log(upper) * 0.25). Otherwise, this should be (lower * 0.75 + upper + * 0.25). */ +#define LADSPA_HINT_DEFAULT_LOW 0x80 + +/* This default hint indicates that a middle value between the + suggested lower and upper bounds should be chosen. For ports with + LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.5 + + log(upper) * 0.5). Otherwise, this should be (lower * 0.5 + upper * + 0.5). */ +#define LADSPA_HINT_DEFAULT_MIDDLE 0xC0 + +/* This default hint indicates that a high value between the suggested + lower and upper bounds should be chosen. For ports with + LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.25 + + log(upper) * 0.75). Otherwise, this should be (lower * 0.25 + upper + * 0.75). */ +#define LADSPA_HINT_DEFAULT_HIGH 0x100 + +/* This default hint indicates that the suggested upper bound for the + port should be used. */ +#define LADSPA_HINT_DEFAULT_MAXIMUM 0x140 + +/* This default hint indicates that the number 0 should be used. Note + that this default may be used in conjunction with + LADSPA_HINT_TOGGLED. */ +#define LADSPA_HINT_DEFAULT_0 0x200 + +/* This default hint indicates that the number 1 should be used. Note + that this default may be used in conjunction with + LADSPA_HINT_TOGGLED. */ +#define LADSPA_HINT_DEFAULT_1 0x240 + +/* This default hint indicates that the number 100 should be used. */ +#define LADSPA_HINT_DEFAULT_100 0x280 + +/* This default hint indicates that the Hz frequency of `concert A' + should be used. This will be 440 unless the host uses an unusual + tuning convention, in which case it may be within a few Hz. */ +#define LADSPA_HINT_DEFAULT_440 0x2C0 + +#define LADSPA_IS_HINT_BOUNDED_BELOW(x) ((x) & LADSPA_HINT_BOUNDED_BELOW) +#define LADSPA_IS_HINT_BOUNDED_ABOVE(x) ((x) & LADSPA_HINT_BOUNDED_ABOVE) +#define LADSPA_IS_HINT_TOGGLED(x) ((x) & LADSPA_HINT_TOGGLED) +#define LADSPA_IS_HINT_SAMPLE_RATE(x) ((x) & LADSPA_HINT_SAMPLE_RATE) +#define LADSPA_IS_HINT_LOGARITHMIC(x) ((x) & LADSPA_HINT_LOGARITHMIC) +#define LADSPA_IS_HINT_INTEGER(x) ((x) & LADSPA_HINT_INTEGER) + +#define LADSPA_IS_HINT_HAS_DEFAULT(x) ((x) & LADSPA_HINT_DEFAULT_MASK) +#define LADSPA_IS_HINT_DEFAULT_MINIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_MINIMUM) +#define LADSPA_IS_HINT_DEFAULT_LOW(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_LOW) +#define LADSPA_IS_HINT_DEFAULT_MIDDLE(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_MIDDLE) +#define LADSPA_IS_HINT_DEFAULT_HIGH(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_HIGH) +#define LADSPA_IS_HINT_DEFAULT_MAXIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_MAXIMUM) +#define LADSPA_IS_HINT_DEFAULT_0(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_0) +#define LADSPA_IS_HINT_DEFAULT_1(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_1) +#define LADSPA_IS_HINT_DEFAULT_100(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_100) +#define LADSPA_IS_HINT_DEFAULT_440(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_440) + +typedef struct _LADSPA_PortRangeHint { + + /* Hints about the port. */ + LADSPA_PortRangeHintDescriptor HintDescriptor; + + /* Meaningful when hint LADSPA_HINT_BOUNDED_BELOW is active. When + LADSPA_HINT_SAMPLE_RATE is also active then this value should be + multiplied by the relevant sample rate. */ + LADSPA_Data LowerBound; + + /* Meaningful when hint LADSPA_HINT_BOUNDED_ABOVE is active. When + LADSPA_HINT_SAMPLE_RATE is also active then this value should be + multiplied by the relevant sample rate. */ + LADSPA_Data UpperBound; + +} LADSPA_PortRangeHint; + +/*****************************************************************************/ + +/* Plugin Handles: + + This plugin handle indicates a particular instance of the plugin + concerned. It is valid to compare this to NULL (0 for C++) but + otherwise the host should not attempt to interpret it. The plugin + may use it to reference internal instance data. */ + +typedef void * LADSPA_Handle; + +/*****************************************************************************/ + +/* Descriptor for a Type of Plugin: + + This structure is used to describe a plugin type. It provides a + number of functions to examine the type, instantiate it, link it to + buffers and workspaces and to run it. */ + +typedef struct _LADSPA_Descriptor { + + /* This numeric identifier indicates the plugin type + uniquely. Plugin programmers may reserve ranges of IDs from a + central body to avoid clashes. Hosts may assume that IDs are + below 0x1000000. */ + unsigned long UniqueID; + + /* This identifier can be used as a unique, case-sensitive + identifier for the plugin type within the plugin file. Plugin + types should be identified by file and label rather than by index + or plugin name, which may be changed in new plugin + versions. Labels must not contain white-space characters. */ + const char * Label; + + /* This indicates a number of properties of the plugin. */ + LADSPA_Properties Properties; + + /* This member points to the null-terminated name of the plugin + (e.g. "Sine Oscillator"). */ + const char * Name; + + /* This member points to the null-terminated string indicating the + maker of the plugin. This can be an empty string but not NULL. */ + const char * Maker; + + /* This member points to the null-terminated string indicating any + copyright applying to the plugin. If no Copyright applies the + string "None" should be used. */ + const char * Copyright; + + /* This indicates the number of ports (input AND output) present on + the plugin. */ + unsigned long PortCount; + + /* This member indicates an array of port descriptors. Valid indices + vary from 0 to PortCount-1. */ + const LADSPA_PortDescriptor * PortDescriptors; + + /* This member indicates an array of null-terminated strings + describing ports (e.g. "Frequency (Hz)"). Valid indices vary from + 0 to PortCount-1. */ + const char * const * PortNames; + + /* This member indicates an array of range hints for each port (see + above). Valid indices vary from 0 to PortCount-1. */ + const LADSPA_PortRangeHint * PortRangeHints; + + /* This may be used by the plugin developer to pass any custom + implementation data into an instantiate call. It must not be used + or interpreted by the host. It is expected that most plugin + writers will not use this facility as LADSPA_Handle should be + used to hold instance data. */ + void * ImplementationData; + + /* This member is a function pointer that instantiates a plugin. A + handle is returned indicating the new plugin instance. The + instantiation function accepts a sample rate as a parameter. The + plugin descriptor from which this instantiate function was found + must also be passed. This function must return NULL if + instantiation fails. + + Note that instance initialisation should generally occur in + activate() rather than here. */ + LADSPA_Handle (*instantiate)(const struct _LADSPA_Descriptor * Descriptor, + unsigned long SampleRate); + + /* This member is a function pointer that connects a port on an + instantiated plugin to a memory location at which a block of data + for the port will be read/written. The data location is expected + to be an array of LADSPA_Data for audio ports or a single + LADSPA_Data value for control ports. Memory issues will be + managed by the host. The plugin must read/write the data at these + locations every time run() or run_adding() is called and the data + present at the time of this connection call should not be + considered meaningful. + + connect_port() may be called more than once for a plugin instance + to allow the host to change the buffers that the plugin is + reading or writing. These calls may be made before or after + activate() or deactivate() calls. + + connect_port() must be called at least once for each port before + run() or run_adding() is called. When working with blocks of + LADSPA_Data the plugin should pay careful attention to the block + size passed to the run function as the block allocated may only + just be large enough to contain the block of samples. + + Plugin writers should be aware that the host may elect to use the + same buffer for more than one port and even use the same buffer + for both input and output (see LADSPA_PROPERTY_INPLACE_BROKEN). + However, overlapped buffers or use of a single buffer for both + audio and control data may result in unexpected behaviour. */ + void (*connect_port)(LADSPA_Handle Instance, + unsigned long Port, + LADSPA_Data * DataLocation); + + /* This member is a function pointer that initialises a plugin + instance and activates it for use. This is separated from + instantiate() to aid real-time support and so that hosts can + reinitialise a plugin instance by calling deactivate() and then + activate(). In this case the plugin instance must reset all state + information dependent on the history of the plugin instance + except for any data locations provided by connect_port() and any + gain set by set_run_adding_gain(). If there is nothing for + activate() to do then the plugin writer may provide a NULL rather + than an empty function. + + When present, hosts must call this function once before run() (or + run_adding()) is called for the first time. This call should be + made as close to the run() call as possible and indicates to + real-time plugins that they are now live. Plugins should not rely + on a prompt call to run() after activate(). activate() may not be + called again unless deactivate() is called first. Note that + connect_port() may be called before or after a call to + activate(). */ + void (*activate)(LADSPA_Handle Instance); + + /* This method is a function pointer that runs an instance of a + plugin for a block. Two parameters are required: the first is a + handle to the particular instance to be run and the second + indicates the block size (in samples) for which the plugin + instance may run. + + Note that if an activate() function exists then it must be called + before run() or run_adding(). If deactivate() is called for a + plugin instance then the plugin instance may not be reused until + activate() has been called again. + + If the plugin has the property LADSPA_PROPERTY_HARD_RT_CAPABLE + then there are various things that the plugin should not do + within the run() or run_adding() functions (see above). */ + void (*run)(LADSPA_Handle Instance, + unsigned long SampleCount); + + /* This method is a function pointer that runs an instance of a + plugin for a block. This has identical behaviour to run() except + in the way data is output from the plugin. When run() is used, + values are written directly to the memory areas associated with + the output ports. However when run_adding() is called, values + must be added to the values already present in the memory + areas. Furthermore, output values written must be scaled by the + current gain set by set_run_adding_gain() (see below) before + addition. + + run_adding() is optional. When it is not provided by a plugin, + this function pointer must be set to NULL. When it is provided, + the function set_run_adding_gain() must be provided also. */ + void (*run_adding)(LADSPA_Handle Instance, + unsigned long SampleCount); + + /* This method is a function pointer that sets the output gain for + use when run_adding() is called (see above). If this function is + never called the gain is assumed to default to 1. Gain + information should be retained when activate() or deactivate() + are called. + + This function should be provided by the plugin if and only if the + run_adding() function is provided. When it is absent this + function pointer must be set to NULL. */ + void (*set_run_adding_gain)(LADSPA_Handle Instance, + LADSPA_Data Gain); + + /* This is the counterpart to activate() (see above). If there is + nothing for deactivate() to do then the plugin writer may provide + a NULL rather than an empty function. + + Hosts must deactivate all activated units after they have been + run() (or run_adding()) for the last time. This call should be + made as close to the last run() call as possible and indicates to + real-time plugins that they are no longer live. Plugins should + not rely on prompt deactivation. Note that connect_port() may be + called before or after a call to deactivate(). + + Deactivation is not similar to pausing as the plugin instance + will be reinitialised when activate() is called to reuse it. */ + void (*deactivate)(LADSPA_Handle Instance); + + /* Once an instance of a plugin has been finished with it can be + deleted using the following function. The instance handle passed + ceases to be valid after this call. + + If activate() was called for a plugin instance then a + corresponding call to deactivate() must be made before cleanup() + is called. */ + void (*cleanup)(LADSPA_Handle Instance); + +} LADSPA_Descriptor; + +/**********************************************************************/ + +/* Accessing a Plugin: */ + +/* The exact mechanism by which plugins are loaded is host-dependent, + however all most hosts will need to know is the name of shared + object file containing the plugin types. To allow multiple hosts to + share plugin types, hosts may wish to check for environment + variable LADSPA_PATH. If present, this should contain a + colon-separated path indicating directories that should be searched + (in order) when loading plugin types. + + A plugin programmer must include a function called + "ladspa_descriptor" with the following function prototype within + the shared object file. This function will have C-style linkage (if + you are using C++ this is taken care of by the `extern "C"' clause + at the top of the file). + + A host will find the plugin shared object file by one means or + another, find the ladspa_descriptor() function, call it, and + proceed from there. + + Plugin types are accessed by index (not ID) using values from 0 + upwards. Out of range indexes must result in this function + returning NULL, so the plugin count can be determined by checking + for the least index that results in NULL being returned. */ + +const LADSPA_Descriptor * ladspa_descriptor(unsigned long Index); + +/* Datatype corresponding to the ladspa_descriptor() function. */ +typedef const LADSPA_Descriptor * +(*LADSPA_Descriptor_Function)(unsigned long Index); + +/**********************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif /* LADSPA_INCLUDED */ + +/* EOF */ diff --git a/dssi/makefile b/dssi/makefile index 96511e6..6a87da9 100755 --- a/dssi/makefile +++ b/dssi/makefile @@ -4,6 +4,7 @@ CSYM=dssi~ LIBDIR=/usr/local/lib PDDIR=$(LIBDIR)/pd INSTALLPATH=$(PDDIR)/extra/ +ARCHITECTURE=i386 DEBUG=0 current: pd_linux @@ -41,7 +42,7 @@ pd_darwin: src/$(NAME).pd_darwin .SUFFIXES: .pd_darwin -DARWINCFLAGS = -DPD -O3 -Wall -W -Wshadow -Wstrict-prototypes \ +DARWINCFLAGS = -DPD -arch $(ARCHITECTURE) -O3 -Wall -W -Wshadow -Wstrict-prototypes \ -Wno-unused -Wno-parentheses -Wno-switch -L/usr/local/lib/ -DDEBUG=$(DEBUG) DARWININCLUDE = -I ./ -I ../src -I/usr/local/include/ -I ./include -I/usr/local/include/dssi/ @@ -50,7 +51,7 @@ DARWININCLUDE = -I ./ -I ../src -I/usr/local/include/ -I ./include -I/usr/local/ $(CC) $(DARWINCFLAGS) $(DARWININCLUDE) -c src/jsearch.c $(CC) $(DARWINCFLAGS) $(DARWININCLUDE) -c src/jload.c $(CC) $(DARWINCFLAGS) $(DARWININCLUDE) -c src/dssi~.c - $(CC) -bundle -undefined suppress -flat_namespace -llo -o $(NAME).pd_darwin dssi~.o jload.o jsearch.o + $(CC) -arch $(ARCHITECTURE) -bundle -undefined suppress -flat_namespace -llo -o $(NAME).pd_darwin dssi~.o jload.o jsearch.o rm -f *.o # ----------------------- Generic ----------------------- diff --git a/dssi/src/dssi~.c b/dssi/src/dssi~.c index 8f205ef..8e7296f 100644 --- a/dssi/src/dssi~.c +++ b/dssi/src/dssi~.c @@ -26,6 +26,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include + #include "dssi~.h" #include "jutils.h" @@ -341,11 +343,14 @@ static void dssi_tilde_connect_ports(t_dssi_tilde *x, t_int instance){ } -static void dssi_tilde_activate_plugin(t_dssi_tilde *x, t_float instance_f){ +static void dssi_tilde_activate_plugin(t_dssi_tilde *x, t_int instance){ - t_int instance = (t_int)instance_f; - if(x->descriptor->LADSPA_Plugin->activate) + if(x->descriptor->LADSPA_Plugin->activate){ +#if DEBUG + post("trying to activate instance: %d", instance); +#endif x->descriptor->LADSPA_Plugin->activate(x->instanceHandles[instance]); + } #if DEBUG post("plugin activated!"); #endif @@ -1559,275 +1564,287 @@ static const char* plugin_tilde_search_plugin_by_label (t_dssi_tilde *x, } -static t_int dssi_tilde_dssi_methods(t_dssi_tilde *x, t_symbol *s, int argc, t_atom *argv) { - if(x->is_DSSI){ - char *msg_type, - *debug, - *filename, - *filepath, - *key, - *value, - *temp, - mydir[MAXPDSTRING]; - int instance = -1, - pathlen, - toggle, - fd, - n_instances = x->n_instances, - count, - maxpatches; - t_float val; - long filelength = 0; - unsigned char *raw_patch_data = NULL; - FILE *fp = NULL; - size_t filename_length, key_size, value_size; - dx7_patch_t *patchbuf, *firstpatch; - msg_type = (char *)malloc(TYPE_STRING_SIZE); - atom_string(argv, msg_type, TYPE_STRING_SIZE); - debug = NULL; - key = NULL; - value = NULL; - maxpatches = 128; - patchbuf = malloc(32 * sizeof(dx7_patch_t)); - firstpatch = &patchbuf[0]; - val = 0; - - /*FIX: Temporary - at the moment we always load the first 32 patches to 0 */ - if(strcmp(msg_type, "configure")){ - instance = (int)atom_getfloatarg(2, argc, argv) - 1; - - if(!strcmp(msg_type, "load") && x->descriptor->configure){ - filename = argv[1].a_w.w_symbol->s_name; - post("dssi~: loading patch: %s for instance %d", filename, instance); - - if(!strcmp(x->descriptor->LADSPA_Plugin->Label, "hexter") || - !strcmp(x->descriptor->LADSPA_Plugin->Label, "hexter6")) { - - key = malloc(10 * sizeof(char)); /* holds "patchesN" */ - strcpy(key, "patches0"); - - /* FIX: duplicates code from load_plugin() */ - fd = canvas_open(x->x_canvas, filename, "", - mydir, &filename, MAXPDSTRING, 0); - - if(fd >= 0){ - filepath = mydir; - pathlen = strlen(mydir); - temp = &mydir[pathlen]; - sprintf(temp, "/%s", filename); - fp = fopen(filepath, "rb"); - } - else{ - post("dssi~: unable to get file descriptor"); - } +static t_int dssi_tilde_dssi_methods(t_dssi_tilde *x, t_symbol *s, int argc, t_atom *argv) +{ + if (!x->is_DSSI) { + post("dssi~: plugin is not a DSSI plugin, operation not supported"); + return 0; + } + char *msg_type, + *debug, + *filename, + *filepath, + *key, + *value, + *temp, + mydir[MAXPDSTRING]; + int instance = -1, + pathlen, + toggle, + fd, + n_instances = x->n_instances, + count, + i, + chan, + maxpatches; + t_float val; + long filelength = 0; + unsigned char *raw_patch_data = NULL; + FILE *fp = NULL; + size_t filename_length, key_size, value_size; + dx7_patch_t *patchbuf, *firstpatch; + msg_type = (char *)malloc(TYPE_STRING_SIZE); + atom_string(argv, msg_type, TYPE_STRING_SIZE); + debug = NULL; + key = NULL; + value = NULL; + maxpatches = 128; + patchbuf = malloc(32 * sizeof(dx7_patch_t)); + firstpatch = &patchbuf[0]; + val = 0; + + /*FIX: Temporary - at the moment we always load the first 32 patches to 0 */ + if(strcmp(msg_type, "configure")){ + instance = (int)atom_getfloatarg(2, argc, argv) - 1; + + if(!strcmp(msg_type, "load") && x->descriptor->configure){ + filename = argv[1].a_w.w_symbol->s_name; + post("dssi~: loading patch: %s for instance %d", filename, instance); + + if(!strcmp(x->descriptor->LADSPA_Plugin->Label, "hexter") || + !strcmp(x->descriptor->LADSPA_Plugin->Label, "hexter6")) { + + key = malloc(10 * sizeof(char)); /* holds "patchesN" */ + strcpy(key, "patches0"); + + /* FIX: duplicates code from load_plugin() */ + fd = canvas_open(x->x_canvas, filename, "", + mydir, &filename, MAXPDSTRING, 0); + + if(fd >= 0){ + filepath = mydir; + pathlen = strlen(mydir); + temp = &mydir[pathlen]; + sprintf(temp, "/%s", filename); + fp = fopen(filepath, "rb"); + } + else{ + post("dssi~: unable to get file descriptor"); + } - /*From dx7_voice_data by Sean Bolton */ - if(fp == NULL){ - post("dssi~: unable to open patch file: %s", filename); - return 0; - } - if (fseek(fp, 0, SEEK_END) || - (filelength = ftell(fp)) == -1 || - fseek(fp, 0, SEEK_SET)) { - post("dssi~: couldn't get length of patch file: %s", - filename); - fclose(fp); - return 0; - } - if (filelength == 0) { - post("dssi~: patch file has zero length"); - fclose(fp); - return 0; - } else if (filelength > 16384) { - post("dssi~: patch file is too large"); - fclose(fp); - return 0; - } - if (!(raw_patch_data = (unsigned char *) - malloc(filelength))) { - post( - "dssi~: couldn't allocate memory for raw patch file"); - fclose(fp); - return 0; - } - if (fread(raw_patch_data, 1, filelength, fp) - != (size_t)filelength) { - post("dssi~: short read on patch file: %s", filename); - free(raw_patch_data); - fclose(fp); - return 0; - } + /*From dx7_voice_data by Sean Bolton */ + if(fp == NULL){ + post("dssi~: unable to open patch file: %s", filename); + return 0; + } + if (fseek(fp, 0, SEEK_END) || + (filelength = ftell(fp)) == -1 || + fseek(fp, 0, SEEK_SET)) { + post("dssi~: couldn't get length of patch file: %s", + filename); fclose(fp); + return 0; + } + if (filelength == 0) { + post("dssi~: patch file has zero length"); + fclose(fp); + return 0; + } else if (filelength > 16384) { + post("dssi~: patch file is too large"); + fclose(fp); + return 0; + } + if (!(raw_patch_data = (unsigned char *) + malloc(filelength))) { + post( + "dssi~: couldn't allocate memory for raw patch file"); + fclose(fp); + return 0; + } + if (fread(raw_patch_data, 1, filelength, fp) + != (size_t)filelength) { + post("dssi~: short read on patch file: %s", filename); + free(raw_patch_data); + fclose(fp); + return 0; + } + fclose(fp); #if DEBUG - post("Patch file length is %ul", filelength); + post("Patch file length is %ul", filelength); #endif - /* figure out what kind of file it is */ - filename_length = strlen(filename); - if (filename_length > 4 && - !strcmp(filename + filename_length - 4, ".dx7") && - filelength % DX7_VOICE_SIZE_PACKED == 0) { - /* It's a raw DX7 patch bank */ + /* figure out what kind of file it is */ + filename_length = strlen(filename); + if (filename_length > 4 && + !strcmp(filename + filename_length - 4, ".dx7") && + filelength % DX7_VOICE_SIZE_PACKED == 0) { + /* It's a raw DX7 patch bank */ #if DEBUG - post("Raw DX7 format patch bank passed"); + post("Raw DX7 format patch bank passed"); #endif - count = filelength / DX7_VOICE_SIZE_PACKED; - if (count > maxpatches) - count = maxpatches; - memcpy(firstpatch, raw_patch_data, count * - DX7_VOICE_SIZE_PACKED); + count = filelength / DX7_VOICE_SIZE_PACKED; + if (count > maxpatches) + count = maxpatches; + memcpy(firstpatch, raw_patch_data, count * + DX7_VOICE_SIZE_PACKED); - } else if (filelength > 6 && - raw_patch_data[0] == 0xf0 && - raw_patch_data[1] == 0x43 && - /*This was used to fix some problem with Galaxy exports - possibly dump in worng format. It is not needed, but it did work, so in future, we may be able to support more formats not just DX7 */ - /* ((raw_patch_data[2] & 0xf0) == 0x00 || - raw_patch_data[2] == 0x7e) &&*/ - (raw_patch_data[2] & 0xf0) == 0x00 && - raw_patch_data[3] == 0x09 && - (raw_patch_data[4] == 0x10 || - raw_patch_data[4] == 0x20) && - /* 0x10 is actual, 0x20 matches typo in manual */ - raw_patch_data[5] == 0x00) { - /* It's a DX7 sys-ex 32 voice dump */ + } else if (filelength > 6 && + raw_patch_data[0] == 0xf0 && + raw_patch_data[1] == 0x43 && + /*This was used to fix some problem with Galaxy exports - possibly dump in worng format. It is not needed, but it did work, so in future, we may be able to support more formats not just DX7 */ + /* ((raw_patch_data[2] & 0xf0) == 0x00 || + raw_patch_data[2] == 0x7e) &&*/ + (raw_patch_data[2] & 0xf0) == 0x00 && + raw_patch_data[3] == 0x09 && + (raw_patch_data[4] == 0x10 || + raw_patch_data[4] == 0x20) && + /* 0x10 is actual, 0x20 matches typo in manual */ + raw_patch_data[5] == 0x00) { + /* It's a DX7 sys-ex 32 voice dump */ #if DEBUG - post("SYSEX header check passed"); + post("SYSEX header check passed"); #endif - if (filelength != DX7_DUMP_SIZE_BULK || - raw_patch_data[DX7_DUMP_SIZE_BULK - 1] != 0xf7) { - post("dssi~: badly formatted DX7 32 voice dump!"); - count = 0; + if (filelength != DX7_DUMP_SIZE_BULK || + raw_patch_data[DX7_DUMP_SIZE_BULK - 1] != 0xf7) { + post("dssi~: badly formatted DX7 32 voice dump!"); + count = 0; #ifdef CHECKSUM_PATCH_FILES_ON_LOAD - } else if (dx7_bulk_dump_checksum(&raw_patch_data[6], - DX7_VOICE_SIZE_PACKED * 32) != - raw_patch_data[DX7_DUMP_SIZE_BULK - 2]) { + } else if (dx7_bulk_dump_checksum(&raw_patch_data[6], + DX7_VOICE_SIZE_PACKED * 32) != + raw_patch_data[DX7_DUMP_SIZE_BULK - 2]) { - post("dssi~: DX7 32 voice dump with bad checksum!"); - count = 0; + post("dssi~: DX7 32 voice dump with bad checksum!"); + count = 0; #endif - } else { - - count = 32; - if (count > maxpatches) - count = maxpatches; - memcpy(firstpatch, raw_patch_data + 6, count * DX7_VOICE_SIZE_PACKED); - - } } else { - /* unsuccessful load */ - post("dssi~: unknown patch bank file format!"); - count = 0; + count = 32; + if (count > maxpatches) + count = maxpatches; + memcpy(firstpatch, raw_patch_data + 6, count * DX7_VOICE_SIZE_PACKED); } + } else { - free(raw_patch_data); - - if(count == 32) - value = encode_7in6((uint8_t *)&patchbuf[0].data[0], - count * DX7_VOICE_SIZE_PACKED); + /* unsuccessful load */ + post("dssi~: unknown patch bank file format!"); + count = 0; } - else if(!strcmp(x->descriptor->LADSPA_Plugin->Label, - "FluidSynth-DSSI")){ - key = malloc(6 * sizeof(char)); - strcpy(key, "load"); - value = filename; - } - else{ - post("dssi~: %s patches are not supported", - x->descriptor->LADSPA_Plugin->Label); - } - - } - if(!strcmp(msg_type, "dir") && x->descriptor->configure){ - pathlen = strlen(argv[1].a_w.w_symbol->s_name) + 2; - x->project_dir = malloc((pathlen) * sizeof(char)); - atom_string(&argv[1], x->project_dir, pathlen); - post("dssi~: project directory for instance %d has been set to: %s", instance, x->project_dir); - key = DSSI_PROJECT_DIRECTORY_KEY; - value = x->project_dir; - } - - else if(!strcmp(msg_type, "dir")) - post("dssi~: %s %s: operation not supported", msg_type, - argv[1].a_w.w_symbol->s_name); + free(raw_patch_data); - if(!strcmp(msg_type, "show") || !strcmp(msg_type, "hide")){ - instance = (int)atom_getfloatarg(1, argc, argv) - 1; - if(!strcmp(msg_type, "show")) - toggle = 1; - else - toggle = 0; + if(count == 32) + value = encode_7in6((uint8_t *)&patchbuf[0].data[0], + count * DX7_VOICE_SIZE_PACKED); - if(instance == -1){ - while(n_instances--) - dssi_show(x, n_instances, toggle); - } - else - dssi_show(x, instance, toggle); + } + else if(!strcmp(x->descriptor->LADSPA_Plugin->Label, + "FluidSynth-DSSI")){ + key = malloc(6 * sizeof(char)); + strcpy(key, "load"); + value = filename; + } + else{ + post("dssi~: %s patches are not supported", + x->descriptor->LADSPA_Plugin->Label); } } - /*Use this to send arbitrary configure message to plugin */ - else if(!strcmp(msg_type, "configure")){ - key = - (char *)malloc(key_size = (strlen(argv[1].a_w.w_symbol->s_name) + 2) * sizeof(char)); - atom_string(&argv[1], key, key_size); - if(argc >= 3){ - if (argv[2].a_type == A_FLOAT){ - val = atom_getfloatarg(2, argc, argv); - value = (char *)malloc(TYPE_STRING_SIZE * - sizeof(char)); - sprintf(value, "%.2f", val); - } - else if(argv[2].a_type == A_SYMBOL){ - value = - (char *)malloc(value_size = - (strlen(argv[2].a_w.w_symbol->s_name) + 2) * - sizeof(char)); - atom_string(&argv[2], value, value_size); - } - - } - - if(argc == 4 && argv[3].a_type == A_FLOAT) - instance = atom_getfloatarg(3, argc, argv) - 1; - else if (n_instances) - instance = -1; + if(!strcmp(msg_type, "dir") && x->descriptor->configure){ + pathlen = strlen(argv[1].a_w.w_symbol->s_name) + 2; + x->project_dir = malloc((pathlen) * sizeof(char)); + atom_string(&argv[1], x->project_dir, pathlen); + post("dssi~: project directory for instance %d has been set to: %s", instance, x->project_dir); + key = DSSI_PROJECT_DIRECTORY_KEY; + value = x->project_dir; } - if(key != NULL && value != NULL){ + else if(!strcmp(msg_type, "dir")) + post("dssi~: %s %s: operation not supported", msg_type, + argv[1].a_w.w_symbol->s_name); + + if(!strcmp(msg_type, "show") || !strcmp(msg_type, "hide")){ + instance = (int)atom_getfloatarg(1, argc, argv) - 1; + if(!strcmp(msg_type, "show")) + toggle = 1; + else + toggle = 0; + if(instance == -1){ - while(n_instances--){ - debug = dssi_tilde_send_configure( - x, key, value, n_instances); - dssi_tilde_configure_buffer(x, key, value, n_instances); - } + while(n_instances--) + dssi_show(x, n_instances, toggle); } - /*FIX: Put some error checking in here to make sure instance is valid*/ - else{ + else + dssi_show(x, instance, toggle); + } + + if(!strcmp(msg_type, "remap")) { + /* remap channel to instance */ + for(i = 0; i < x->n_instances && i < 128; i++){ + chan = (int)atom_getfloatarg(1 + i, argc, argv); + post("dssi~: remapped MIDI channel %d to %d", 1+i, chan); + x->channelMap[i+1] = chan; + } + } + + } - debug = dssi_tilde_send_configure(x, key, value, instance); - dssi_tilde_configure_buffer(x, key, value, instance); + /*Use this to send arbitrary configure message to plugin */ + else if(!strcmp(msg_type, "configure")){ + key = + (char *)malloc(key_size = (strlen(argv[1].a_w.w_symbol->s_name) + 2) * sizeof(char)); + atom_string(&argv[1], key, key_size); + if(argc >= 3){ + if (argv[2].a_type == A_FLOAT){ + val = atom_getfloatarg(2, argc, argv); + value = (char *)malloc(TYPE_STRING_SIZE * + sizeof(char)); + sprintf(value, "%.2f", val); + } + else if(argv[2].a_type == A_SYMBOL){ + value = + (char *)malloc(value_size = + (strlen(argv[2].a_w.w_symbol->s_name) + 2) * + sizeof(char)); + atom_string(&argv[2], value, value_size); + } + + } + + if(argc == 4 && argv[3].a_type == A_FLOAT) + instance = atom_getfloatarg(3, argc, argv) - 1; + else if (n_instances) + instance = -1; + } + + if(key != NULL && value != NULL){ + if(instance == -1){ + while(n_instances--){ + debug = dssi_tilde_send_configure( + x, key, value, n_instances); + dssi_tilde_configure_buffer(x, key, value, n_instances); } } + /*FIX: Put some error checking in here to make sure instance is valid*/ + else{ + + debug = dssi_tilde_send_configure(x, key, value, instance); + dssi_tilde_configure_buffer(x, key, value, instance); + } + } #if DEBUG - post("The plugin returned %s", debug); + post("The plugin returned %s", debug); #endif - free(msg_type); - free(patchbuf); - } - else - post("dssi~: plugin is not a DSSI plugin, operation not supported"); - return 0; + free(msg_type); + free(patchbuf); + return 0; } static void dssi_tilde_bang(t_dssi_tilde *x) @@ -1872,6 +1889,8 @@ static t_int *dssi_tilde_perform(t_int *w) (x->bufReadIndex + 1) % EVENT_BUFSIZE) { instance = x->midiEventBuf[x->bufReadIndex].data.note.channel; + + instance = x->channelMap[instance + 1] - 1; /*This should never happen, but check anyway*/ if(instance > x->n_instances || instance < 0){ post( @@ -2286,7 +2305,8 @@ static void *dssi_tilde_load_plugin(t_dssi_tilde *x, t_int argc, t_atom *argv){ for(i = 0;i < x->n_instances; i++) dssi_tilde_connect_ports(x, i); for(i = 0;i < x->n_instances; i++) - dssi_tilde_activate_plugin(x, (t_float)i); + dssi_tilde_activate_plugin(x, i); + if(x->is_DSSI){ for(i = 0;i < x->n_instances; i++) dssi_tilde_osc_setup(x, i); @@ -2296,6 +2316,10 @@ static void *dssi_tilde_load_plugin(t_dssi_tilde *x, t_int argc, t_atom *argv){ #endif for(i = 0;i < x->n_instances; i++) dssi_tilde_init_programs(x, i); + + for(i = 0; i < x->n_instances && i < 128; i++){ + x->channelMap[i] = i; + } } } } @@ -2317,19 +2341,29 @@ static void *dssi_tilde_load_plugin(t_dssi_tilde *x, t_int argc, t_atom *argv){ /* x->outlets = (t_float **)calloc(x->plugin_outs, sizeof(t_float *)); - */} - if(x->plugin_ins){ - x->inlets = (t_inlet **)getbytes(x->plugin_ins * sizeof(t_inlet *)); - for(i = 0;i < x->plugin_ins; i++) - x->inlets[i] = inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); - /* x->inlets = - (t_float **)calloc(x->plugin_ins, - sizeof(t_float *)); - */} - x->dsp = 1; - post("dssi~: %d instances of %s, ready.", x->n_instances, - x->plugin_label); + */ + } + else + post("dssi~: error: plugin has no outputs"); + if(x->plugin_ins){ + x->inlets = (t_inlet **)getbytes(x->plugin_ins * sizeof(t_inlet *)); + for(i = 0;i < x->plugin_ins; i++) + x->inlets[i] = inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + /* x->inlets = + (t_float **)calloc(x->plugin_ins, + sizeof(t_float *)); + */ + } + else + post("dssi~: error: plugin has no inputs"); + + x->dsp = 1; + post("dssi~: %d instances of %s, ready.", x->n_instances, + x->plugin_label); } + else + post("dssi~: error: no plugin handle"); + return (void *)x; } diff --git a/dssi/src/dssi~.h b/dssi/src/dssi~.h index fe8df35..6aadb85 100644 --- a/dssi/src/dssi~.h +++ b/dssi/src/dssi~.h @@ -53,7 +53,7 @@ #define ASCII_t 116 #define ASCII_a 97 -#define LOADGUI 1 /* FIX: depracate this */ +#define LOADGUI 0 /* FIX: depracate this */ #ifdef DEBUG #define CHECKSUM_PATCH_FILES_ON_LOAD 1 #endif @@ -128,6 +128,7 @@ typedef struct _dssi_tilde { t_dssi_instance *instances; int n_instances; unsigned long *instanceEventCounts; + unsigned char channelMap[128]; snd_seq_event_t **instanceEventBuffers; snd_seq_event_t midiEventBuf[EVENT_BUFSIZE]; -- cgit v1.2.1