1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
|
#ifndef SND_PLUGIN_H
#define SND_PLUGIN_H
enum {PLUG_OK,PLUG_STOP,PLUG_ERROR,PLUG_NO_DATA};
typedef struct {
int (*init)(snd_state *state); /* called upon plug_in invocation -- return PLUG_OK to continue */
int (*start_channel)(chan_info *channel); /* called for each (sync'd) channel of sound -- return PLUG_OK to continue */
int (*read_sample)(int val, int eoc); /* plug_in calls this to get the next sample (a 32-bit integer with 15-bit "fraction") */
int (*write_sample)(int *val); /* called to get next sample(s) from plug_in during current channel edit */
int (*end_channel)(chan_info *channel); /* called at end of current channel */
int (*quit)(snd_state *state); /* called at end of plug_in invocation */
char *edit_name; /* tell Snd how to describe this edit operation (edit history) */
char *documentation; /* describe this plug_in (user help) */
char *name; /* user-readable name for this plug_in */
char *error; /* report user-readable error description if PLUG_ERROR occurred */
} snd_plug;
/*
* when a plug-in edit operation is invoked in Snd:
* plug.init is called with the global state pointer 'state'
* if it returns PLUG_OK, we continue, else we print some message and exit the edit
* plug.edit_name is saved as the edit-history's reference to the current operation
* the 'sync' buttons are checked to collect the set of channels affected
* a loop begins through the set of channels, one channel at a time
* at any time, the operation may be aborted (by the user), setting state->stopped_explicitly to true
* plug.start_channel is called with the current channel
* if it returns PLUG_OK we continue, else we abort the current edit and try to return to the pre-edit state
* a loop begins through the current samples of the current channel
* for each sample, plug.read_sample is called with the current sample and a flag indicating the end of the channel's data
* if it returns PLUG_OK we continue, else we try to back out
* plug.write_sample is called until it returns PLUG_NO_DATA
* each returned sample (if any) is saved as the edited form of the current channel's data
* for each new sample, write_sample should return PLUG_OK
* if it returns PLUG_ERROR or PLUG_STOP, we abort the edit and try to back out
* when this channel's data is done,
* plug.end_channel is called with the channel
* if it returns PLUG_OK, the channel is considered edited, and it's edit history is updated
* when all the channels have been edited,
* plug.quit is called with the global state
*
* if any of the functions is null, it is ignored (as though it had simply returned PLUG_OK)
*
* so a simple scaling operation might be (giving just procedure body code):
* in lisp: "(scale 1.3)" -> guile procedure defined and loaded via dynamic links setting scl to 1.3 and calling the scaler plug
* plug.init = NULL;
* plug.quit = NULL;
* plug.start_channel = NULL;
* plug.end_channel = NULL;
* plug.read_sample = {my_samp = val*scl; my_samples = 1; return(PLUG_OK);}
* plug.write_sample = {if (my_samples) {(*val) = my_samp; my_samples = 0; return(PLUG_OK);} else return(PLUG_NO_DATA);}
* plug.name = "scaler";
* plug.documentation = "scales by arg";
* plug.edit_name = "(scaler 1.3)";
*
* The guile functions call-plug, call-plug-selection, and describe-plug take the plug argument.
* for a more complex example, see anoi.c
*/
/* some macros to access various Snd data structures from the channel pointer or the global state pointer
* from the state pointer, all of Snd's current state is accessible. Most of the relevant structs
* are defined in snd-1.h
*
* SND_PLUGIN_SRATE(cp) -- cp: channel pointer, returns file's sampling rate (integer)
* SND_PLUGIN_FULL_FILENAME(cp) -- cp: channel pointer, returns full filename (can be used with sound-*)
* SND_PLUGIN_SHORT_FILENAME(cp) -- cp: channel pointer, returns short (no directory) filename
* SND_PLUGIN_CHANNEL(cp) -- cp: channel pointer, returns which channel this is (0-based)
* SND_PLUGIN_CHANNELS(cp) -- cp: channel pointer, returns total number of channels in this sound
* SND_PLUGIN_SAMPLES(cp) -- cp: channel pointer, returns current (edit-state dependent) number of samples in this channel
* SND_PLUGIN_CURSOR(cp) -- cp: channel pointer, returns current cursor location (sample number)
* SND_PLUGIN_ABORT_REQUESTED(ss) -- ss: state pointer, returns 1 if user typed C-g to interrupt computation
* SND_PLUGIN_INDEX(cp) -- cp: channel pointer, returns index of associated sound (for use with gh_eval_str)
*/
#define SND_PLUGIN_SRATE(cp) (((file_info *)((snd_info *)(cp->sound))->hdr)->srate)
#define SND_PLUGIN_FULL_FILENAME(cp) (((snd_info *)(cp->sound))->fullname)
#define SND_PLUGIN_SHORT_FILENAME(cp) (((snd_info *)(cp->sound))->shortname)
#define SND_PLUGIN_CHANNEL(cp) (cp->chan)
#define SND_PLUGIN_CHANNELS(cp) (((snd_info *)(cp->sound))->nchans)
#define SND_PLUGIN_SAMPLES(cp) (cp->samples[cp->edit_ctr])
#define SND_PLUGIN_CURSOR(cp) (cp->cursor)
#define SND_PLUGIN_ABORT_REQUESTED(ss) (ss->stopped_explicitly)
#define SND_PLUGIN_INDEX (cp) (((snd_info *)(cp->sound))->index)
/* to affect Snd's internal state, it is much safer to use gh_eval_str; anything passed
* here is treated just as if you had typed it to Snd's lisp listener or M-X;
* for example, to fire up the recorder dialog from a plug-in,
* gh_eval_str("(recorder-dialog)");
* or to open "oboe.snd"
* gh_eval_str("(open-sound \"oboe.snd\")");
* and SND_SRATE(cp) could be also (assuming there's only one sound open)
* gh_scm2int(gh_eval_str("(srate)"));
*/
#if 0
/* transform plug-ins */
/* (not implemented yet) */
typedef struct {
int (*transform)(int size, float *data); /* called if plug-in transform is being displayed */
char *documentation;
char *name;
char *error;
/* also will need
* transform size returned,
* graphing info (xlabel xscl yscl spectrum-label)
* perhaps interpretation info for mouse drag through data (and peak-finding actions)
* save-state/options decision (if transform requested with unknown current type, revert to fourier?)
* since subsequent load may have new order, this needs to be symbolic (i.e. we need the variable name or the load form itself)
* something like "(begin (dynamic-link...) (dynamic-call...) (set! my-transform (add-transform ...)))"
* returned value should be index (like current fourier-transform var)
* perhaps macros for snd_state transform-related settings
*/
} snd_transform_plug;
/* tied into Snd via add-plug-transform */
/* also control-panel additions?
*/
#endif
#endif
|