File: snd-plugin.h

package info (click to toggle)
snd 3.4-4
  • links: PTS
  • area: main
  • in suites: potato
  • size: 5,148 kB
  • ctags: 12,594
  • sloc: ansic: 86,516; lisp: 3,480; sh: 1,507; makefile: 119
file content (122 lines) | stat: -rw-r--r-- 6,870 bytes parent folder | download
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