| 1 |
|
|---|
| 2 |
Sweep Plugin Writer's Guide |
|---|
| 3 |
--------------------------- |
|---|
| 4 |
|
|---|
| 5 |
Documents Sweep Plugin API Version 1.0.0 |
|---|
| 6 |
Last modified Oct 22 2000 |
|---|
| 7 |
Copyright (C) 2000 Conrad Parker <conrad@vergenet.net> |
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 |
1. Introduction |
|---|
| 11 |
=============== |
|---|
| 12 |
|
|---|
| 13 |
Sweep plugins are able to modify all aspects of a sample file, including |
|---|
| 14 |
the audio data, the sampling format used, and which regions of the sample |
|---|
| 15 |
are currently selected. Sweep provides some convenience functions to make |
|---|
| 16 |
plugin writing easy. |
|---|
| 17 |
|
|---|
| 18 |
If you intend to write plugins which only modify audio data, you are |
|---|
| 19 |
encouraged to use the Linux Audio Developer's Simple Plugin API |
|---|
| 20 |
(LADSPA) [1] instead of writing a native Sweep plugin. |
|---|
| 21 |
|
|---|
| 22 |
Other types of plugins include those which simply modify the selection |
|---|
| 23 |
data, and those which provide conversions between different data formats. |
|---|
| 24 |
|
|---|
| 25 |
Plugins do not need to provide their own graphical user interface |
|---|
| 26 |
code. This API provides an abstraction by which plugins specify |
|---|
| 27 |
numeric and string parameters. Sweep creates and presents to the user |
|---|
| 28 |
a graphical dialog window for setting these parameters. |
|---|
| 29 |
|
|---|
| 30 |
This document explains the programming interface for writing native Sweep |
|---|
| 31 |
plugins and how to set up a build environment for your plugin code. |
|---|
| 32 |
|
|---|
| 33 |
|
|---|
| 34 |
2. Getting started |
|---|
| 35 |
================== |
|---|
| 36 |
|
|---|
| 37 |
A sweep plugin is a shared library which contains one or more |
|---|
| 38 |
procedures. These procedures can modify any of the data associated |
|---|
| 39 |
with a particular sound wave. Procedures take as input a set of |
|---|
| 40 |
parameters. They must specify the types of these parameters, and can |
|---|
| 41 |
optionally provide some constraints (such as an acceptable numeric |
|---|
| 42 |
range, or a list of values to choose from) and hints (such as to |
|---|
| 43 |
interpret a number logarithmically). The constraints are interpreted |
|---|
| 44 |
by Sweep as hard limits, ie. it will not pass values to the plugin |
|---|
| 45 |
which fall outside of the constraints. Both constraints and hints are |
|---|
| 46 |
used to construct a useful dialog box to the user for setting |
|---|
| 47 |
parameters. |
|---|
| 48 |
|
|---|
| 49 |
Procedures can optionally supply a function to suggest appropriate |
|---|
| 50 |
values for the parameters as needed, provide Help and Copyright |
|---|
| 51 |
information, and of course must supply a function to perform the |
|---|
| 52 |
desired operation. |
|---|
| 53 |
|
|---|
| 54 |
It is a requirement that all operations which modify a sound wave or |
|---|
| 55 |
its associated data also provide a means for undoing and redoing |
|---|
| 56 |
themselves. Sweep provides some standard mechanisms for efficiently |
|---|
| 57 |
undoing and redoing some common types of operation. |
|---|
| 58 |
|
|---|
| 59 |
This section describes the data structures used by Sweep and its |
|---|
| 60 |
plugins, the mechanisms provided for performing common operations, and |
|---|
| 61 |
information on building the resulting plugin libraries. |
|---|
| 62 |
|
|---|
| 63 |
|
|---|
| 64 |
2.1 Sample code |
|---|
| 65 |
--------------- |
|---|
| 66 |
|
|---|
| 67 |
You will most likely find it useful to examine the existing plugins in |
|---|
| 68 |
the Sweep source archive. These are in the plugins/ directory. In |
|---|
| 69 |
particular, the "example" plugin contains many of the features discussed |
|---|
| 70 |
in this document. |
|---|
| 71 |
|
|---|
| 72 |
|
|---|
| 73 |
2.2 Files to include |
|---|
| 74 |
-------------------- |
|---|
| 75 |
|
|---|
| 76 |
<sweep/sweep.h> |
|---|
| 77 |
Includes all required sweep header files. |
|---|
| 78 |
|
|---|
| 79 |
Sweep plugins have access to all functions and datatypes defined in |
|---|
| 80 |
GLib [2]. <glib.h> is automatically included in <sweep/sweep_types.h>, |
|---|
| 81 |
so there is no need to explicitly include it once <sweep/sweep.h> has |
|---|
| 82 |
been included. In particular the GList data structure is used |
|---|
| 83 |
extensively by this API. |
|---|
| 84 |
|
|---|
| 85 |
|
|---|
| 86 |
2.3 Internationalisation |
|---|
| 87 |
------------------------ |
|---|
| 88 |
<sweep/sweep_i18n.h> |
|---|
| 89 |
|
|---|
| 90 |
Sweep includes support for translatable strings via GNU gettext [3]. It |
|---|
| 91 |
uses the same macros that the Gnome project defines to mark out strings |
|---|
| 92 |
which can be translated. |
|---|
| 93 |
|
|---|
| 94 |
When writing a plugin, you will provide some text strings describing the |
|---|
| 95 |
plugin's procedures, the names of parameters used, and short descriptions |
|---|
| 96 |
of the paramters meanings. In order to enable translators to provide |
|---|
| 97 |
complete translations of Sweep and its plugins in other languages, you |
|---|
| 98 |
must specifically mark strings which should be translated. |
|---|
| 99 |
|
|---|
| 100 |
The usual way to do this is to place the macro _(...) around any strings |
|---|
| 101 |
used within the code. However, when the string appears outside of a |
|---|
| 102 |
function, you must use the macro N_(...) instead. N(...) marks a string |
|---|
| 103 |
for later translation; _(...) marks a string for immediate translation, |
|---|
| 104 |
and can only be used where a function call is permitted. |
|---|
| 105 |
|
|---|
| 106 |
For more detailed information on the use of these macros, see the |
|---|
| 107 |
relevent section in "GTK+/Gnome Application Development" [4]. |
|---|
| 108 |
|
|---|
| 109 |
If you would like to add a translation for Sweep into a new language, |
|---|
| 110 |
all you need to do is provide translations for the text strings used in |
|---|
| 111 |
Sweep. Please see the documentation for GNU gettext [3] for more |
|---|
| 112 |
information on how to create the required file. |
|---|
| 113 |
|
|---|
| 114 |
|
|---|
| 115 |
2.4 Basic types |
|---|
| 116 |
--------------- |
|---|
| 117 |
<sweep/sweep_types.h> |
|---|
| 118 |
|
|---|
| 119 |
Sweep provides some basic types which are useful for audio |
|---|
| 120 |
handling. It is important to use these types where appropriate, as |
|---|
| 121 |
this allows the sourcecode to be reconfigured for different |
|---|
| 122 |
environments. |
|---|
| 123 |
|
|---|
| 124 |
Individual sample values of audio data are represented by the types |
|---|
| 125 |
sw_audio_t and sw_audio_intermediate_t. These are floating point |
|---|
| 126 |
values ranging between SW_AUDIO_T_MIN and SW_AUDIO_T_MAX. For most |
|---|
| 127 |
data storage and moving operations you should use sw_audio_t, and |
|---|
| 128 |
where more precision is required (eg. when mixing sounds together) use |
|---|
| 129 |
sw_audio_intermediate_t. |
|---|
| 130 |
|
|---|
| 131 |
Blocks of audio data are stored in consecutive frames, where a frame |
|---|
| 132 |
is a collection of the data values corresponding to the same instant |
|---|
| 133 |
of time in each channel. That is, multichannel audio data is |
|---|
| 134 |
interleaved, for example stereo data is stored: |
|---|
| 135 |
|
|---|
| 136 |
(LR)(LR)(LR)(LR)...(LR) |
|---|
| 137 |
|
|---|
| 138 |
where L and R are sample values of type sw_audio_t, and (LR) is a |
|---|
| 139 |
single frame of data. |
|---|
| 140 |
|
|---|
| 141 |
Frame counts are given as sw_framecount_t. Please use this type |
|---|
| 142 |
wherever a count or offset in terms of frames is required. |
|---|
| 143 |
|
|---|
| 144 |
Time is represented by sw_time_t. Time is defined in a floating point |
|---|
| 145 |
format in units of seconds. |
|---|
| 146 |
|
|---|
| 147 |
|
|---|
| 148 |
2.5 Complex types |
|---|
| 149 |
----------------- |
|---|
| 150 |
<sweep/sweep_types.h> |
|---|
| 151 |
|
|---|
| 152 |
struct _sw_sample { |
|---|
| 153 |
sw_sounddata * sounddata; |
|---|
| 154 |
... |
|---|
| 155 |
}; |
|---|
| 156 |
<sweep/sweep_sample.h> |
|---|
| 157 |
|
|---|
| 158 |
|
|---|
| 159 |
struct _sw_sounddata { |
|---|
| 160 |
sw_format * format; |
|---|
| 161 |
sw_framecount_t nr_frames; /* nr frames */ |
|---|
| 162 |
|
|---|
| 163 |
gpointer data; |
|---|
| 164 |
|
|---|
| 165 |
GList * sels; /* selection: list of sw_sels */ |
|---|
| 166 |
... |
|---|
| 167 |
}; |
|---|
| 168 |
<sweep/sweep_sounddata.h> |
|---|
| 169 |
|
|---|
| 170 |
struct _sw_format { |
|---|
| 171 |
gint channels; /* nr channels per frame */ |
|---|
| 172 |
gint rate; /* sampling rate (Hz) */ |
|---|
| 173 |
}; |
|---|
| 174 |
|
|---|
| 175 |
The selection component of a sounddata object is a GList of sw_sel |
|---|
| 176 |
objects. An sw_sel describes a region in a selection. Units are frame |
|---|
| 177 |
offsets from start of sample. |
|---|
| 178 |
|
|---|
| 179 |
struct _sw_sel { |
|---|
| 180 |
sw_framecount_t sel_start; |
|---|
| 181 |
sw_framecount_t sel_end; |
|---|
| 182 |
}; |
|---|
| 183 |
|
|---|
| 184 |
|
|---|
| 185 |
2.6 Types for parameters |
|---|
| 186 |
------------------------ |
|---|
| 187 |
<sweep/sweep_types.h> |
|---|
| 188 |
|
|---|
| 189 |
The parameters to pass to functions can be any of a few simple types |
|---|
| 190 |
as defined in <sweep/sweep_types.h>: SWEEP_TYPE_BOOL, SWEEP_TYPE_INT, |
|---|
| 191 |
SWEEP_TYPE_FLOAT, SWEEP_TYPE_STRING. |
|---|
| 192 |
|
|---|
| 193 |
A parameter object is a union which can take any of these values. A |
|---|
| 194 |
parameter set is defined as a pointer to a sequence of parameters. |
|---|
| 195 |
|
|---|
| 196 |
/* |
|---|
| 197 |
* Instances of Parameter Sets |
|---|
| 198 |
*/ |
|---|
| 199 |
typedef union _sw_param sw_param; |
|---|
| 200 |
typedef sw_param * sw_param_set; |
|---|
| 201 |
|
|---|
| 202 |
union _sw_param { |
|---|
| 203 |
sw_bool b; |
|---|
| 204 |
sw_int i; |
|---|
| 205 |
sw_float f; |
|---|
| 206 |
sw_string s; |
|---|
| 207 |
}; |
|---|
| 208 |
|
|---|
| 209 |
|
|---|
| 210 |
2.7 Parameter Specifications |
|---|
| 211 |
---------------------------- |
|---|
| 212 |
<sweep/sweep_types.h> |
|---|
| 213 |
|
|---|
| 214 |
Information about a parameter accepted by a procedure is stored in a |
|---|
| 215 |
parameter specification object, as defined in <sweep/sweep_types.h> |
|---|
| 216 |
|
|---|
| 217 |
/* |
|---|
| 218 |
* sw_param_spec: specification for a parameter. |
|---|
| 219 |
*/ |
|---|
| 220 |
struct _sw_param_spec { |
|---|
| 221 |
/* A short name for this parameter */ |
|---|
| 222 |
gchar * name; |
|---|
| 223 |
|
|---|
| 224 |
/* A longer description of the parameter's purpose and usage */ |
|---|
| 225 |
gchar * desc; |
|---|
| 226 |
|
|---|
| 227 |
/* The type of the parameter */ |
|---|
| 228 |
sw_param_type type; |
|---|
| 229 |
|
|---|
| 230 |
/* Constraints */ |
|---|
| 231 |
sw_constraint_type constraint_type; |
|---|
| 232 |
sw_constraint constraint; |
|---|
| 233 |
|
|---|
| 234 |
/* Hints */ |
|---|
| 235 |
sw_hints hints; |
|---|
| 236 |
}; |
|---|
| 237 |
|
|---|
| 238 |
Please remember to mark the name and description fields for |
|---|
| 239 |
translation. |
|---|
| 240 |
|
|---|
| 241 |
|
|---|
| 242 |
2.7.1 Constraints |
|---|
| 243 |
----------------- |
|---|
| 244 |
<sweep/sweep_types.h> |
|---|
| 245 |
|
|---|
| 246 |
There are two ways of defining constraints on parameter values |
|---|
| 247 |
accepted by the procedure; additionally, you can specify that the |
|---|
| 248 |
values for a parameter are completely unconstrained. |
|---|
| 249 |
|
|---|
| 250 |
SW_PARAM_CONSTRAINED_NOT |
|---|
| 251 |
|
|---|
| 252 |
SW_PARAM_CONSTRAINED_LIST indicates that the parameter is constrained |
|---|
| 253 |
to values given in a param list. NB: the length of this list is given |
|---|
| 254 |
by the value of the first parameter, interpreted as an integer. |
|---|
| 255 |
ie. this length = constraint->param_list[0].i |
|---|
| 256 |
|
|---|
| 257 |
SW_PARAM_CONSTRAINED_RANGE indicates that the parameter is constrained |
|---|
| 258 |
to a given range. |
|---|
| 259 |
|
|---|
| 260 |
/* |
|---|
| 261 |
* sw_param_range: a range of acceptable parameter values. |
|---|
| 262 |
* |
|---|
| 263 |
* NB: this is a hard limit. Values <lower and values >upper |
|---|
| 264 |
* need not be expected by plugins. |
|---|
| 265 |
* |
|---|
| 266 |
* The first parameter is a mask consisting of a bitwise or of between |
|---|
| 267 |
* zero and three of SW_RANGE_LOWER_BOUND_VALID, |
|---|
| 268 |
* SW_RANGE_UPPER_BOUND_VALID, and SW_RANGE_STEP_VALID. |
|---|
| 269 |
* |
|---|
| 270 |
* The three following parameters are interpreted as the type of the |
|---|
| 271 |
* parameter they constrain. The 'step' parameter is never valid for |
|---|
| 272 |
* string parameters. |
|---|
| 273 |
*/ |
|---|
| 274 |
struct _sw_param_range { |
|---|
| 275 |
int valid_mask; |
|---|
| 276 |
sw_param lower; |
|---|
| 277 |
sw_param upper; |
|---|
| 278 |
sw_param step; |
|---|
| 279 |
}; |
|---|
| 280 |
|
|---|
| 281 |
|
|---|
| 282 |
2.7.2 Hints |
|---|
| 283 |
----------- |
|---|
| 284 |
<sweep/sweep_types.h> |
|---|
| 285 |
|
|---|
| 286 |
A few hints exist which Sweep can use to provide different graphical |
|---|
| 287 |
input methods as appropriate. |
|---|
| 288 |
|
|---|
| 289 |
SW_PARAM_HINT_LOGARITHMIC indicates that the parameter should be |
|---|
| 290 |
interpreted as logarithmic. |
|---|
| 291 |
|
|---|
| 292 |
SW_PARAM_HINT_TIME indicates that the parameter should be interpreted |
|---|
| 293 |
as a time |
|---|
| 294 |
|
|---|
| 295 |
SW_PARAM_HINT_FILENAME indicates that the parameter should be |
|---|
| 296 |
interpreted as a valid filename on the user's system. |
|---|
| 297 |
|
|---|
| 298 |
|
|---|
| 299 |
2.8 Procedures |
|---|
| 300 |
-------------- |
|---|
| 301 |
<sweep/sweep_types.h> |
|---|
| 302 |
|
|---|
| 303 |
A procedure object ties together all the aspects of a particular |
|---|
| 304 |
operation, including some textual data, the specifications for |
|---|
| 305 |
parameters, and functions to suggest useful parameters and to do the |
|---|
| 306 |
work required. |
|---|
| 307 |
|
|---|
| 308 |
When the user initiates a procedure, the following occurs: |
|---|
| 309 |
|
|---|
| 310 |
1. A new, empty, parameter set is created large enough to hold |
|---|
| 311 |
the parameters required for this proc |
|---|
| 312 |
2. The parameter set is passed to the suggest() function (if |
|---|
| 313 |
that is defined) along with the sample and custom data. This allows |
|---|
| 314 |
the suggest() function to provide some useful values based on the |
|---|
| 315 |
characteristics of the sample. The suggest() function modifies the |
|---|
| 316 |
values in the parameter set and returns. |
|---|
| 317 |
3. A graphical dialog is created to allow the user to edit the |
|---|
| 318 |
parameter values. It is initialised with the values given by the |
|---|
| 319 |
suggests() function. |
|---|
| 320 |
4. The user changes the parameter values as they wish, and upon |
|---|
| 321 |
pressing "OK" the apply() function is called with these adjusted |
|---|
| 322 |
parameter values. |
|---|
| 323 |
|
|---|
| 324 |
The sw_procedure object has the following structure: |
|---|
| 325 |
|
|---|
| 326 |
|
|---|
| 327 |
typedef struct _sw_procedure sw_procedure; |
|---|
| 328 |
|
|---|
| 329 |
struct _sw_procedure { |
|---|
| 330 |
gchar * name; |
|---|
| 331 |
gchar * description; |
|---|
| 332 |
gchar * author; |
|---|
| 333 |
gchar * copyright; |
|---|
| 334 |
gchar * url; |
|---|
| 335 |
|
|---|
| 336 |
gchar * identifier; |
|---|
| 337 |
|
|---|
| 338 |
/* Key bindings */ |
|---|
| 339 |
guint accel_key; |
|---|
| 340 |
GdkModifierType accel_mods; |
|---|
| 341 |
|
|---|
| 342 |
gint nr_params; |
|---|
| 343 |
sw_param_spec * param_specs; |
|---|
| 344 |
|
|---|
| 345 |
/* suggest sets suggested values for the members of pset, |
|---|
| 346 |
* possibly using the sample. |
|---|
| 347 |
* |
|---|
| 348 |
* If nr_params is 0 then this function will not be called. |
|---|
| 349 |
* If this function is NULL then default values (zero,FALSE,"") |
|---|
| 350 |
* will be used. |
|---|
| 351 |
*/ |
|---|
| 352 |
void (*suggest) (sw_sample * sample, sw_param_set pset, |
|---|
| 353 |
gpointer custom_data); |
|---|
| 354 |
|
|---|
| 355 |
/* This is the function that actually does the work! |
|---|
| 356 |
* |
|---|
| 357 |
* apply applies the parameter set pset to a sample |
|---|
| 358 |
* |
|---|
| 359 |
* If nr_params is 0 then this function will be passed a NULL pset. |
|---|
| 360 |
*/ |
|---|
| 361 |
sw_op_instance * (*apply) (sw_sample * sample, |
|---|
| 362 |
sw_param_set pset, gpointer custom_data); |
|---|
| 363 |
|
|---|
| 364 |
/* custom data to pass to the suggest and apply functions */ |
|---|
| 365 |
gpointer custom_data; |
|---|
| 366 |
}; |
|---|
| 367 |
|
|---|
| 368 |
|
|---|
| 369 |
The name field should be a short descriptive name, as appropriate for |
|---|
| 370 |
labelling the menu item to invoke this procedure from Sweep. The |
|---|
| 371 |
description should be some somewhat longer ASCII text describing what |
|---|
| 372 |
this procedure does. Please remember to mark the name and description |
|---|
| 373 |
fields for translation. |
|---|
| 374 |
|
|---|
| 375 |
The author field should be a comma separated list of names of authors |
|---|
| 376 |
of this procedure, each optionally providing an email address in angle |
|---|
| 377 |
brackets. The copyright field should be a string such as "Copyright |
|---|
| 378 |
(C) 2000 Joe Blow". The url field can optionally give the URL of a web |
|---|
| 379 |
page describing the procedure in more detail. |
|---|
| 380 |
|
|---|
| 381 |
The identifier field should be a string which will uniquely identify |
|---|
| 382 |
this plugin. You may choose to put identifying information here such |
|---|
| 383 |
as your name, domain, a suggested categorisation for the procedure, or |
|---|
| 384 |
the name of your favourite vegetable or fish. |
|---|
| 385 |
|
|---|
| 386 |
The accel_key and accel_mods fields list keybindings to initiate this |
|---|
| 387 |
procedure. The values of their types are defined in <gdk/gdkkeysyms.h>. |
|---|
| 388 |
|
|---|
| 389 |
The nr_params field gives the number of parameters this procedure |
|---|
| 390 |
takes. You must point the param_specs field to an array of nr_params |
|---|
| 391 |
sw_param_spec objects as defined in section 2.7. |
|---|
| 392 |
|
|---|
| 393 |
Procedures must provide a suggest() and an apply() function. These |
|---|
| 394 |
functions take the same arguments (sample, parameter set, custom |
|---|
| 395 |
data). The first argument is the sample which is being edited. The |
|---|
| 396 |
second is the parameter set object to use, and the third is some |
|---|
| 397 |
custom data provided by the proc. |
|---|
| 398 |
|
|---|
| 399 |
The suggest() function is used to set some meaningful parameter |
|---|
| 400 |
values. The apply() function does the actual work of the procedure. |
|---|
| 401 |
|
|---|
| 402 |
A useful means of handling suggest() is to remember the parameter |
|---|
| 403 |
values set by the last invocation of this procedure. This can simply |
|---|
| 404 |
be done by keeping these values in static global variables within the |
|---|
| 405 |
plugin file. |
|---|
| 406 |
|
|---|
| 407 |
The (sw_op_instance *) returned by the apply() function is described |
|---|
| 408 |
in section 3.1. However in the usual case of writing a selection |
|---|
| 409 |
modifier or audio filter, you do not need to understand the internals |
|---|
| 410 |
of an sw_op_instance. |
|---|
| 411 |
|
|---|
| 412 |
You may provide some custom_data to pass to the suggest() and apply() |
|---|
| 413 |
functions if required. This is often useful if many procedures share |
|---|
| 414 |
the same suggest() and apply() functions, as custom_data can then |
|---|
| 415 |
provide further information to these functions as to how they should |
|---|
| 416 |
actually operate. See the ladspameta plugin for an example of this use |
|---|
| 417 |
of custom_data. |
|---|
| 418 |
|
|---|
| 419 |
|
|---|
| 420 |
2.9 Writing a selection modifier |
|---|
| 421 |
-------------------------------- |
|---|
| 422 |
|
|---|
| 423 |
A selection modifier is an operation which only affects the sels |
|---|
| 424 |
member of a sample's sounddata. The undo and redo methods which are |
|---|
| 425 |
registered for a selection modifier do not attempt to retain any |
|---|
| 426 |
information about any other portion of the sample -- they are |
|---|
| 427 |
optimised to only retain information about changes in the selection. |
|---|
| 428 |
|
|---|
| 429 |
To create a selection modifier, you must provide a function of the |
|---|
| 430 |
form SweepModify, as defined in <sweep/sweep_types.h>: |
|---|
| 431 |
|
|---|
| 432 |
typedef void (*SweepModify) (sw_sample * sample, sw_param_set pset, |
|---|
| 433 |
gpointer custom_data); |
|---|
| 434 |
|
|---|
| 435 |
This function is expected to modify only the selection data, ie. |
|---|
| 436 |
sample->sounddata->sels. Note that this data may also be accessed by |
|---|
| 437 |
the playback thread, so it is protected by a mutex. You must surround |
|---|
| 438 |
any modifications to the sels member of a sounddata object with |
|---|
| 439 |
calls to sounddata_lock_selection() and sounddata_unlock_selection(), |
|---|
| 440 |
as defined in <sweep/sweep_selection.h>. |
|---|
| 441 |
|
|---|
| 442 |
To perform the selection modification, call the following function |
|---|
| 443 |
(also defined in <sweep/sweep_selection.h>): |
|---|
| 444 |
|
|---|
| 445 |
sw_op_instance * |
|---|
| 446 |
perform_selection_op (sw_sample * s, char * desc, SweepModify func, |
|---|
| 447 |
sw_param_set pset, gpointer custom_data); |
|---|
| 448 |
|
|---|
| 449 |
The first argument is the sample to modify, the second is some |
|---|
| 450 |
short descriptive text for the operation, 'func' is the SweepModify |
|---|
| 451 |
function, and pset and custom_data are those provided by the proc. |
|---|
| 452 |
|
|---|
| 453 |
see eg. plugins/byenergy/byenergy.c for an example of a Selection |
|---|
| 454 |
plugin. |
|---|
| 455 |
|
|---|
| 456 |
|
|---|
| 457 |
2.10 Writing an audio filter |
|---|
| 458 |
--------------------------- |
|---|
| 459 |
<sweep/sweep_filter.h> |
|---|
| 460 |
|
|---|
| 461 |
Two convenience routines exist for creating plugins which only modify |
|---|
| 462 |
audio data. Note that where possible it is preferable to use the |
|---|
| 463 |
LADSPA API instead for developing such plugins. See Appendix B for |
|---|
| 464 |
more information about building LADSPA plugins for use with Sweep. |
|---|
| 465 |
|
|---|
| 466 |
Filter plugins must only modify the audio data corresponding to |
|---|
| 467 |
selected regions of a sample. The functions provided to perform filter |
|---|
| 468 |
operations remember only information in the selected regions for the |
|---|
| 469 |
purposes of undoing and redoing. Thus any modifications done outside the |
|---|
| 470 |
selected regions will be lost after an undo. |
|---|
| 471 |
|
|---|
| 472 |
As the selection information consists of a linked list of selected |
|---|
| 473 |
regions (see section 2.3) it is necessary for a filter plugin to |
|---|
| 474 |
somehow iterate over the selection and operate only on these regions. |
|---|
| 475 |
The SweepFilterRegion mechanism handles this iteration for you, |
|---|
| 476 |
whereas the SweepFilter mechanism allows for more general filters for |
|---|
| 477 |
which you must provide the iteration over the selection regions. |
|---|
| 478 |
|
|---|
| 479 |
|
|---|
| 480 |
2.10.1 SweepFilterRegion |
|---|
| 481 |
----------------------- |
|---|
| 482 |
|
|---|
| 483 |
To make use of the SweepFilterRegion mechanism define a function of |
|---|
| 484 |
type SweepFilterRegion, which has the following prototype: |
|---|
| 485 |
|
|---|
| 486 |
typedef void (*SweepFilterRegion) (gpointer data, sw_format * format, |
|---|
| 487 |
sw_framecount_t nr_frames, |
|---|
| 488 |
sw_param_set pset, gpointer custom_data); |
|---|
| 489 |
|
|---|
| 490 |
A SweepFilterRegion function takes a pointer 'data' to a block of |
|---|
| 491 |
audio data, an (sw_format *) describing the format of the data, and a |
|---|
| 492 |
count of the number of frames to process from that point onwards. It |
|---|
| 493 |
also accepts a parameter set and some custom data. |
|---|
| 494 |
|
|---|
| 495 |
To perform a FilterRegion operation, use the following function: |
|---|
| 496 |
|
|---|
| 497 |
sw_op_instance * |
|---|
| 498 |
perform_filter_region_op (sw_sample * sample, char * desc, |
|---|
| 499 |
SweepFilterRegion func, sw_param_set pset, |
|---|
| 500 |
gpointer custom_data); |
|---|
| 501 |
|
|---|
| 502 |
see eg. plugins/reverse/reverse.c for an example of a FilterRegion |
|---|
| 503 |
|
|---|
| 504 |
The LADSPA meta-plugin is implemented as a FilterRegion. |
|---|
| 505 |
|
|---|
| 506 |
|
|---|
| 507 |
2.10.2 SweepFilter |
|---|
| 508 |
----------------- |
|---|
| 509 |
|
|---|
| 510 |
If the processing involved cannot be done independently for each |
|---|
| 511 |
selection region, it is necessary to use a SweepFilter function. Note |
|---|
| 512 |
that the plugin must then perform the iteration over the selection |
|---|
| 513 |
regions itself. These functions have the following prototype: |
|---|
| 514 |
|
|---|
| 515 |
typedef sw_sounddata * (*SweepFilter) (sw_sounddata * sounddata, |
|---|
| 516 |
sw_param_set pset, |
|---|
| 517 |
gpointer custom_data); |
|---|
| 518 |
|
|---|
| 519 |
To perform a Filter operation, use the following function: |
|---|
| 520 |
|
|---|
| 521 |
sw_op_instance * |
|---|
| 522 |
perform_filter_op (sw_sample * sample, char * desc, SweepFilter func, |
|---|
| 523 |
sw_param_set pset, gpointer custom_data); |
|---|
| 524 |
|
|---|
| 525 |
For example, the Normalise plugin (plugins/normalise/normalise.c) is |
|---|
| 526 |
implemened as a Filter. This is necessary because it must first find |
|---|
| 527 |
the greatest data value in any of the selected regions, and then in |
|---|
| 528 |
a second pass amplify each region by an amount calculated. |
|---|
| 529 |
|
|---|
| 530 |
|
|---|
| 531 |
2.11 Creating Plugin Shared Libraries |
|---|
| 532 |
------------------------------------- |
|---|
| 533 |
<sweep/sweep_types.h> |
|---|
| 534 |
|
|---|
| 535 |
The usual way to make a proc available for use with Sweep is to build |
|---|
| 536 |
it into a shared library file. When building a shared library to |
|---|
| 537 |
contain one or more procs you must define a structure of type |
|---|
| 538 |
sw_plugin with the name "plugin". This is the only name that should be |
|---|
| 539 |
exposed in the library's symbol table; declare all other functions and |
|---|
| 540 |
global variables as static. If multiple procedures share common code |
|---|
| 541 |
or data then place them in the same plugin library. A plugin must not rely |
|---|
| 542 |
on any other particular plugin being loaded. |
|---|
| 543 |
|
|---|
| 544 |
typedef struct _sw_plugin sw_plugin; |
|---|
| 545 |
|
|---|
| 546 |
struct _sw_plugin { |
|---|
| 547 |
/* plugin_init () returns a list of procs */ |
|---|
| 548 |
GList * (*plugin_init) (void); |
|---|
| 549 |
|
|---|
| 550 |
/* plugin_cleanup() frees the plugin's private data structures */ |
|---|
| 551 |
void (*plugin_cleanup) (void); |
|---|
| 552 |
}; |
|---|
| 553 |
|
|---|
| 554 |
An sw_plugin structure contains two members, a plugin_init function |
|---|
| 555 |
that takes no arguments and returns a list of procs, and a void |
|---|
| 556 |
plugin_cleanup function. |
|---|
| 557 |
|
|---|
| 558 |
The initialisation is performed through a function call and can |
|---|
| 559 |
instantiate an indeterminate number of procs. This allows the creation |
|---|
| 560 |
of plugins which may not necessarily know when built how many procs |
|---|
| 561 |
they will create; for example, the LADSPA meta plugin dynamically |
|---|
| 562 |
creates a proc corresponding to every LADSPA function it can find in |
|---|
| 563 |
all the LADSPA plugins it can find. |
|---|
| 564 |
|
|---|
| 565 |
In the usual case where the procs are known at build time (ie. they |
|---|
| 566 |
consist of functions within the plugin library) the plugin_init |
|---|
| 567 |
function can simply construct a GList out of these known procs, each |
|---|
| 568 |
of which can be statically defined. This list is constructed using |
|---|
| 569 |
g_list_append(); see <glib.h> for more information on using GLists. |
|---|
| 570 |
|
|---|
| 571 |
|
|---|
| 572 |
2.12 Building plugins within the sweep source tree |
|---|
| 573 |
------------------------------------------------- |
|---|
| 574 |
|
|---|
| 575 |
Have a look at plugins/example/ |
|---|
| 576 |
|
|---|
| 577 |
Makefile.am: |
|---|
| 578 |
|
|---|
| 579 |
## Process this file with automake to produce Makefile.in |
|---|
| 580 |
|
|---|
| 581 |
INCLUDES = -I$(top_srcdir)/include |
|---|
| 582 |
|
|---|
| 583 |
libdir = $(PACKAGE_PLUGIN_DIR) |
|---|
| 584 |
|
|---|
| 585 |
lib_LTLIBRARIES = \ |
|---|
| 586 |
libexample.la |
|---|
| 587 |
|
|---|
| 588 |
libexample_la_SOURCES = example.c |
|---|
| 589 |
libexample_la_LDFLAGS = -version-info 1:0:0 |
|---|
| 590 |
|
|---|
| 591 |
The version info (1:0:0 in the example above) should consist of |
|---|
| 592 |
the numbers SWEEP_PLUGIN_API_MAJOR, SWEEP_PLUGIN_API_MINOR and |
|---|
| 593 |
SWEEP_PLUGIN_API_REVISION listed in <sweep/sweep_version.h>, colon |
|---|
| 594 |
separated. Copy the numbers in manually, do not attempt to generate |
|---|
| 595 |
them from the values defined. These numbers should only be changed |
|---|
| 596 |
when your plugin code changes to use features only available in |
|---|
| 597 |
newer versions of the plugin API. |
|---|
| 598 |
|
|---|
| 599 |
Note that the plugin API is maintained independently of the |
|---|
| 600 |
application version. See section 3.2 for more information on API |
|---|
| 601 |
versioning. |
|---|
| 602 |
|
|---|
| 603 |
|
|---|
| 604 |
3. Advanced topics |
|---|
| 605 |
================== |
|---|
| 606 |
|
|---|
| 607 |
This section explains in depth some topics which are not usually required |
|---|
| 608 |
by plugin writers. This includes the creation of new types of operation |
|---|
| 609 |
and undo/redo mechanisms and a detailed discussion of the versioning system |
|---|
| 610 |
employed. |
|---|
| 611 |
|
|---|
| 612 |
|
|---|
| 613 |
3.1 Sweep "Operations", Handling Undo and Redo |
|---|
| 614 |
---------------------------------------------- |
|---|
| 615 |
|
|---|
| 616 |
Sweep maintains a stack of recent operations for each sample, which |
|---|
| 617 |
are used to allow undoing and redoing of all operations. If you write |
|---|
| 618 |
a new operation method that modifies a sample's 'sounddata' (ie. the |
|---|
| 619 |
sampling rate, length, selections or audio data) then you must |
|---|
| 620 |
implement Undo and Redo methods for it. However if you are using one |
|---|
| 621 |
of the convenience mechanisms covered in Sections 2.7 and 2.8 then |
|---|
| 622 |
these things are already taken care of by the perform_* functions. |
|---|
| 623 |
|
|---|
| 624 |
The datatypes used are defined in <sweep/sweep_types.h> as follows: |
|---|
| 625 |
|
|---|
| 626 |
typedef void (*SweepFunction) (gpointer data); |
|---|
| 627 |
typedef void (*SweepCallback) (sw_sample * sample, gpointer data); |
|---|
| 628 |
|
|---|
| 629 |
typedef struct _sw_operation sw_operation; |
|---|
| 630 |
typedef struct _sw_op_instance sw_op_instance; |
|---|
| 631 |
|
|---|
| 632 |
struct _sw_operation { |
|---|
| 633 |
SweepCallback undo; |
|---|
| 634 |
SweepFunction purge_undo; |
|---|
| 635 |
SweepCallback redo; |
|---|
| 636 |
SweepFunction purge_redo; |
|---|
| 637 |
}; |
|---|
| 638 |
|
|---|
| 639 |
struct _sw_op_instance { |
|---|
| 640 |
char * description; |
|---|
| 641 |
sw_operation * op; |
|---|
| 642 |
gpointer undo_data; |
|---|
| 643 |
gpointer redo_data; |
|---|
| 644 |
}; |
|---|
| 645 |
|
|---|
| 646 |
Once an operation has been successfully performed, it needs to |
|---|
| 647 |
register itself with Sweep's undo handler. This involves: |
|---|
| 648 |
|
|---|
| 649 |
1. Finding or constructing an sw_operation structure. These are |
|---|
| 650 |
often statically defined. |
|---|
| 651 |
2. Creating an sw_op_instance structure. This contains a |
|---|
| 652 |
description of the operation performed, a pointer to the sw_operation, |
|---|
| 653 |
and pointers to data for use in undoing and redoing the operation |
|---|
| 654 |
performed. |
|---|
| 655 |
3. Calling register_operation() to add the sw_op_instance to |
|---|
| 656 |
the sample's stack of operations. |
|---|
| 657 |
|
|---|
| 658 |
The apply() method of a proc usually does these things, does its actual |
|---|
| 659 |
work, registers the operation then returns the sw_op_instance created. |
|---|
| 660 |
|
|---|
| 661 |
An operation given by (sw_op_instance * inst) is undone by calling |
|---|
| 662 |
|
|---|
| 663 |
inst->op->undo (sample, inst->undo_data); |
|---|
| 664 |
|
|---|
| 665 |
and redone by calling |
|---|
| 666 |
|
|---|
| 667 |
inst->op->redo (sample, inst->redo_data); |
|---|
| 668 |
|
|---|
| 669 |
When an operation instance can be discarded (eg. if only a finite number |
|---|
| 670 |
of recent operations are remembered), the undo_data and redo_data |
|---|
| 671 |
members are purged by calling |
|---|
| 672 |
|
|---|
| 673 |
inst->op->purge_undo (inst->undo_data); |
|---|
| 674 |
inst->op->purge_redo (inst->redo_data); |
|---|
| 675 |
|
|---|
| 676 |
Thus, if any memory was allocated to create undo_data or redo_data it |
|---|
| 677 |
should be freed in the purge functions. |
|---|
| 678 |
|
|---|
| 679 |
Some stock undo and redo methods exist for handling operations which |
|---|
| 680 |
need to paste new data over the selected region, or which need to splice |
|---|
| 681 |
data into the sample. These are outlined in <sweep/sweep_undo.h>. For |
|---|
| 682 |
examples of their use, see "src/edit.c" in the Sweep application source. |
|---|
| 683 |
|
|---|
| 684 |
|
|---|
| 685 |
3.2 Versioning |
|---|
| 686 |
-------------- |
|---|
| 687 |
<sweep/sweep_version.h> |
|---|
| 688 |
|
|---|
| 689 |
Sweep's plugin API is strictly versioned. Note that the plugin API's |
|---|
| 690 |
versions are maintained completely independently of the application |
|---|
| 691 |
version. You can find out the application and plugin API versions of |
|---|
| 692 |
your installed copy of Sweep by issuing the command "sweep --version". |
|---|
| 693 |
|
|---|
| 694 |
Versioning of the plugin API tracks the interface provided by |
|---|
| 695 |
Sweep. This is done because the application is actively linking itself |
|---|
| 696 |
against the plugin libraries, and must look for plugins which |
|---|
| 697 |
implement versions it understands. |
|---|
| 698 |
|
|---|
| 699 |
Versions are given in the form Major.Minor.Revision (M.m.R). The API |
|---|
| 700 |
version implemented by a particular release of Sweep is defined in |
|---|
| 701 |
<sweep/sweep_version.h> by the values SWEEP_PLUGIN_API_MAJOR, |
|---|
| 702 |
SWEEP_PLUGIN_API_MINOR and SWEEP_PLUGIN_API_REVISION. |
|---|
| 703 |
|
|---|
| 704 |
Note: in the following description, a "public data structure" is one |
|---|
| 705 |
defined in any of the headers included by <sweep/sweep.h>, and a |
|---|
| 706 |
"public function" is any function declared in any of the headers |
|---|
| 707 |
included by <sweep/sweep.h>. An "interface" is anything that is either |
|---|
| 708 |
a public data structure or a public function. |
|---|
| 709 |
|
|---|
| 710 |
The version information is only ever updated upon a release of |
|---|
| 711 |
Sweep. Interfaces defined in the code available through CVS between |
|---|
| 712 |
releases are subject to change. |
|---|
| 713 |
|
|---|
| 714 |
The version information V = M.m.R is updated to V' as follows: |
|---|
| 715 |
|
|---|
| 716 |
1. If any interfaces have been removed or modified by a |
|---|
| 717 |
release (ie. backwards compatability has been broken) then the Major |
|---|
| 718 |
number is incremented and Minor and Revision are set to 0. (V' = M+1.0.0) |
|---|
| 719 |
|
|---|
| 720 |
2. Otherwise, if any interfaces have been added since the last |
|---|
| 721 |
release the Minor number is incremented and Revision is set to 0. |
|---|
| 722 |
(V' = M.m+1.0) |
|---|
| 723 |
|
|---|
| 724 |
3. Otherwise, if the application code has changed at all since |
|---|
| 725 |
the last release the Revision is incremented. (V' = M.m.R+1) |
|---|
| 726 |
|
|---|
| 727 |
A plugin library 'plugin' must be built with a name of the form |
|---|
| 728 |
|
|---|
| 729 |
libplugin.so.M.m.R |
|---|
| 730 |
|
|---|
| 731 |
where M, m, and R correspond to the Major, Minor and Revision provided |
|---|
| 732 |
by the particular version of sweep you are developing against, or an |
|---|
| 733 |
earlier Minor and Revision which are known to work. The current behaviour |
|---|
| 734 |
of sweep is to attempt to load all plugins with a Major number it |
|---|
| 735 |
understands, and fail silently if the plugin makes use of interfaces |
|---|
| 736 |
added in subsequent Minor API versions. Thus it is not absolutely necessary |
|---|
| 737 |
to find the earliest API version which will work with your plugin. |
|---|
| 738 |
|
|---|
| 739 |
Additionally, symlinks should be created for the plugin as follows: |
|---|
| 740 |
|
|---|
| 741 |
libplugin.so -> libplugin.so.M |
|---|
| 742 |
libplugin.so.M -> libplugin.so.M.m |
|---|
| 743 |
libplugin.so.M.m -> libplugin.so.M.m.R |
|---|
| 744 |
|
|---|
| 745 |
These symlinks are created automatically when using libtool to build |
|---|
| 746 |
plugin libraries by including the line |
|---|
| 747 |
|
|---|
| 748 |
libplugin_la_LDFLAGS = -version-info M:m:R |
|---|
| 749 |
|
|---|
| 750 |
in the Makefile.am within the plugin's build directory (if using automake, |
|---|
| 751 |
or the flag "-version-info M:m:R" needs to be otherwise passed to libtool). |
|---|
| 752 |
Note that this flag contains colons, not dots. |
|---|
| 753 |
|
|---|
| 754 |
|
|---|
| 755 |
Appendix A. Keeping in touch with Sweep development |
|---|
| 756 |
=================================================== |
|---|
| 757 |
|
|---|
| 758 |
The Sweep homepage contains links to many relevent resources. |
|---|
| 759 |
http://sweep.sourceforge.net/ |
|---|
| 760 |
|
|---|
| 761 |
The Sweep project page on Sourceforge holds the infrastructure for such |
|---|
| 762 |
things as CVS access and bug reporting. It is available at |
|---|
| 763 |
http://sourceforge.net/projects/sweep/ |
|---|
| 764 |
|
|---|
| 765 |
If you have any questions or comments about Sweep please send them to |
|---|
| 766 |
the sweep-devel mailing list <sweep-devel@lists.sourceforge.net>. |
|---|
| 767 |
|
|---|
| 768 |
If in doubt, contact the main author: Conrad Parker <conrad@vergenet.net> |
|---|
| 769 |
|
|---|
| 770 |
|
|---|
| 771 |
Appendix B. Building LADSPA plugins for use with Sweep |
|---|
| 772 |
====================================================== |
|---|
| 773 |
|
|---|
| 774 |
If you intend to create a plugin which only modifies audio data (such |
|---|
| 775 |
as an effects plugin), it is recommended that you use the LADSPA API |
|---|
| 776 |
[1] if possible. This ensures that the plugin you create is usable by |
|---|
| 777 |
a wide variety of free audio programs, rather than only being usable |
|---|
| 778 |
by Sweep. |
|---|
| 779 |
|
|---|
| 780 |
If writing a LADSPA plugin please write a mono filter unless there is |
|---|
| 781 |
some kind of algorithmic interaction between the channels such as |
|---|
| 782 |
panning. Sweep, and most other LADSPA hosts, use multiple instances or |
|---|
| 783 |
invocations of the LADSPA plugins as required so that any number of |
|---|
| 784 |
channels can be catered for with a mono plugin. |
|---|
| 785 |
|
|---|
| 786 |
In fact Sweep will quite happily use a stereo LADSPA filter on a mono |
|---|
| 787 |
sample (it will generate an empty second channel and ignore its |
|---|
| 788 |
output) but it is inefficient to rely on this behaviour. |
|---|
| 789 |
|
|---|
| 790 |
To clarify: LADSPA defines a standardised interface for audio plugins. |
|---|
| 791 |
When creating LADSPA plugins, do not reference any data structures |
|---|
| 792 |
used internally by Sweep. You do not provide any undo or redo methods; |
|---|
| 793 |
Sweep will construct these as required when performing an operation |
|---|
| 794 |
from a LADSPA plugin. |
|---|
| 795 |
|
|---|
| 796 |
|
|---|
| 797 |
Acknowledgements |
|---|
| 798 |
================ |
|---|
| 799 |
|
|---|
| 800 |
This API was constructed after careful studying of plugin APIs from |
|---|
| 801 |
many other free software projects, including SANE, the Gimp, LADSPA, |
|---|
| 802 |
and xmms. |
|---|
| 803 |
|
|---|
| 804 |
|
|---|
| 805 |
References |
|---|
| 806 |
========== |
|---|
| 807 |
|
|---|
| 808 |
[1] Linux Audio Developer's Simple Plugin API |
|---|
| 809 |
http://www.ladspa.org/ |
|---|
| 810 |
|
|---|
| 811 |
[2] GLib Reference Manual |
|---|
| 812 |
http://developer.gnome.org/doc/API/glib/index.html |
|---|
| 813 |
|
|---|
| 814 |
[3] GNU gettext |
|---|
| 815 |
http://www.gnu.org/software/gettext/ |
|---|
| 816 |
|
|---|
| 817 |
[4] GTK+/Gnome Application Development, Section 5.2 "Internationalization" |
|---|
| 818 |
Havoc Pennington, New Riders Publishing 1999 |
|---|
| 819 |
http://developer.gnome.org/doc/GGAD/sec-i18n.html |
|---|