/* * drive.c * * Beispielimplementation eines LADSPA 1.1-Plugins. * * Das hier vorgestellte Plugin ist ein simpler 'Waveshaper', im Klang * ähnlich der subtilen harmonischen Verzerrung und Kompression, wie sie durch * analoge Bandaufnahmen oder Röhren-Endstufen erzeugt wird. Der eigentlich * Algorithmus ist bewusst einfach gehalten, um das Augenmerk auf die * LADSPA-Architektur gerichtet zu halten. * * Zum Kompilieren: * gcc -Wall -nostartfiles -shared -lm -o drive.so drive.c * * (C) 2004, 2005 Tim Goetze * All rights reversed: this code is in the public domain. */ #include #include #include "ladspa.h" /* Die folgende Struktur definiert den Zustand des Plugins: vier 'Ports', * also Anschlüsse, sowie ein 'Gain'-Wert, der das Mischverhältnis angibt * für den Fall, dass das Ausgangssignal mit einem anderen gemischt wird. */ typedef struct { LADSPA_Data * ports[4]; LADSPA_Data adding_gain; } Drive; /* Die 'instantiate'-Methode legt eine Instanz des Plugins an und initialisiert * diese. In diesem Fall eines sehr simplen Klangeffekts ist hierfür nichts * außer der Reservierung des Speichers für die 'Drive'-Struktur nötig. * * Die Sample-Frequenz, bei der das Plugin betrieben wird, wird nur einmal beim * Aufruf dieser Methode durch den Host übergeben. Es empfiehlt sich also, * diese für die Instanz zu speichern, falls der Wert Auswirkungen auf den * DSP-Algorithmus hat (was hier nicht der Fall ist). */ static LADSPA_Handle drive_instantiate ( const struct _LADSPA_Descriptor * Descriptor, unsigned long SampleRate) { Drive * d = (Drive *) calloc (1, sizeof (Drive)); return (LADSPA_Handle) d; } /* 'connect_port' wird vom Host aufgerufen, um dem Plugin mitzuteilen, an * welcher Speicherstelle Daten für die 'Ports' übergeben werden, und zwar * gleichermaßen für Audio- und Kontroll-Parameter, Eingänge wie Ausgänge. * * Üblicherweise reicht es aus, die Speicheradresse für die spätere Nutzung * zu speichern. */ static void drive_connect_port ( LADSPA_Handle Instance, unsigned long Port, LADSPA_Data * DataLocation) { Drive * d = (Drive *) Instance; d->ports[Port] = DataLocation; } /* Die 'run'-Methode des Plugins verrichtet die eigentliche Arbeit; der Host * ruft diese auf, nachdem für alle Ports eine Speicheradresse an 'connect_port' * übergeben wurde und die 'activate'-Methode das Plugin auf die Benutzung * vorbereitet hat. * * Für die Nutzung von Plugins in Echtzeit ist zu beachten, dass in 'run' * möglichst wenige Betriebsystem-Calls getätigt werden sollten. Insbesondere * malloc() und free() müssen vermieden werden, da diese die Ausführung des * Programms für bis zu einige Millisekunden blockieren können; die Auswirkung * wäre eine hörbare Unterbrechung des Audiostroms. * * Plugins, die keine Echtzeit-Operation gewährleisten können, sollten das dem * Host mitteilen, indem sie das 'LADSPA_PROPERTY_HARD_RT_CAPABLE'-Flag * ungesetzt lassen (siehe unten). * * Zum DSP-Algorithmus ist anzumerken, dass eine brauchbarere Version Eingangs- * signale jenseits von 0 dB (also größer 1 oder kleiner -1) auf 0 dB limitieren * sollte; solche Algorithmen sollten allerdings wegen der Gefahr von Aliasing * in Oversampling betrieben werden, worauf hier verzichtet wird, um das Ganze * einfach zu halten. */ static void drive_run ( LADSPA_Handle Instance, unsigned long SampleCount) { Drive * d = (Drive *) Instance; LADSPA_Data * src = d->ports[0]; LADSPA_Data drive = 0.25f * fabs (*(d->ports[1])); LADSPA_Data gain = pow (10, 0.05 * *(d->ports[2])); LADSPA_Data * dest = d->ports[3]; LADSPA_Data x; unsigned long i; /* Ein Wert von 1 würde zu Division durch 0 führen, lieber defensiv mit dem * Parameter umgehen. */ if (drive == 1) drive = .9999; /* Korrektur für die Lautstärkensenkung durch den Algorithmus */ gain *= 1.0f / (1.0f - drive); for (i = 0; i < SampleCount; ++i) { x = src[i]; dest[i] = gain * (x - drive * fabsf (x) * x * x * x); } } /* 'run_adding' verrichtet die gleiche Arbeit wie 'run', mit dem Unterschied, * dass der Host möchte, dass das resultierende Audiosignal am Ausgang den * dafür bereitgestellten Puffer nicht einfach überschreibt, sondern mit einem * dort bereits vorliegenden Signal gemischt wird. * * Das Mischverhältnis legt der Host durch Aufruf der 'set_run_adding_gain'- * Methode fest. */ static void drive_run_adding ( LADSPA_Handle Instance, unsigned long SampleCount) { Drive * d = (Drive *) Instance; LADSPA_Data * src = d->ports[0]; LADSPA_Data drive = 0.25f * fabs (*(d->ports[1])); LADSPA_Data gain = pow (10, 0.05 * *(d->ports[2])); LADSPA_Data * dest = d->ports[3]; LADSPA_Data x; unsigned long i; if (drive == 1) drive = .9999; /* Hier verarbeitet das Plugin den 'adding_gain' */ gain *= d->adding_gain / (1.0f - drive); for (i = 0; i < SampleCount; ++i) { x = src[i]; /* '+=' anstatt '=' sorgt für das gewünschte Mischen */ dest[i] += gain * (x - drive * fabsf (x) * x * x * x); } } /* Hier wird das Signalmischverhältnis für 'run_adding' festgelegt. */ static void drive_set_run_adding_gain ( LADSPA_Handle Instance, LADSPA_Data adding_gain) { Drive * d = (Drive *) Instance; d->adding_gain = adding_gain; } /* Wenn der Host die Plugininstanz nicht mehr benötigt, sorgt 'cleanup' für * die Freigabe aller durch die Instanz benötigten Ressourcen. */ static void drive_cleanup ( LADSPA_Handle Instance) { free ((void *) Instance); } /* LADSPA v1.1 Port-Beschreibungen *******************************************/ /* Es lohnt sich, diese Strukturen soweit als möglich zu bündeln, um unnötige * Arbeit beim Initialisieren der Plugin-Bibliothek einzusparen, und um das * Ganze lesbar zu gestalten. * * Für jeden Port: der Name. */ static const char * const drive_port_names [] = { "input", "drive", "gain (dB)", "output" }; /* Für jeden Port: Flags, die anzeigen, ob es ein Ein- oder Ausgang ist, und * ob Kontroll- oder Audiodaten darüber fließen. */ static int drive_port_descriptors [] = { LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, /* "input" */ LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, /* "drive" */ LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, /* "gain" */ LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO /* "output" */ }; /* Für jeden Port eine Struktur, die den Wertebereich und grob die Standard- * Einstellung beschreibt. */ static const LADSPA_PortRangeHint drive_port_range_hints [] = { /* "input" - Angaben nicht strikt nötig, da ein Audioport */ {0, -1, 1}, /* "drive" */ { /* Flags */ LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_DEFAULT_LOW, /* Unter-, Obergrenze */ 0, 1 }, /* "gain" */ { /* Flags */ LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_DEFAULT_0, /* Unter-, Obergrenze */ -36, 36 }, /* "output" */ {0, -1, 1} }; /* Diese Struktur fasst alle Informationen zusammen, die das Plugin für den * Host beschreiben: ID, Namen, Autor, Portbeschreibungen, Methoden etc. */ static LADSPA_Descriptor drive_descriptor = { /* Diese UniqueID ist nur für Testzwecke, für Distribution sollte eine * eigene angefordert werden. */ .UniqueID = 1, .Label = "Drive", .Properties = LADSPA_PROPERTY_HARD_RT_CAPABLE, .Name = "4th Order Waveshaper", .Maker = "Dein Name Hier ", .Copyright = "Public Domain, 2005", .PortCount = 4, .PortDescriptors = drive_port_descriptors, .PortNames = drive_port_names, .PortRangeHints = drive_port_range_hints, .ImplementationData = 0, .instantiate = drive_instantiate, .connect_port = drive_connect_port, .activate = 0, .run = drive_run, .run_adding = drive_run_adding, .set_run_adding_gain = drive_set_run_adding_gain, .deactivate = 0, .cleanup = drive_cleanup, }; /*****************************************************************************/ /* Nachdem der Host die Programmbibliothek geladen hat, ruft er diese Methode * auf, die ihm die Pluginbeschreibungen zur Verfügung stellt. Üblicherweise * zählt der Host den 'index'-Parameter von 0 solange herauf, bis diese Methode * einen NULL-Zeiger übergibt. */ const LADSPA_Descriptor * ladspa_descriptor (unsigned long index) { /* Nur ein Plugin bei Index 0. */ if (index == 0) return &drive_descriptor; return NULL; }