Snd is a highly customizable, extensible program.
I've tried to bring out to the extension language nearly every portion
of Snd, both the signal-processing functions and
much of the user interface. You can, for example,
add your own menu choices, editing operations,
or graphing alternatives.
Nearly everything in Snd can be set in an initialization
file, loaded at any time from a text file of program code, or specified in a saved state file.
It can also be set
via inter-process communication or from stdin
from any other program (CLM and Emacs in particular),
embedded in a keyboard macro, or typed in the
listener.
The syntax used throughout this documentation is Scheme (a form of lisp) as implemented by Guile or Gauche.
You can also use Ruby or Forth, but need to make various minor changes.
I'm slowly adding parallel Forth and Ruby examples.
The easiest way to get acquainted
with this aspect of Snd is to open the listener
(via the View:Open listener menu option), and type
experiments in its window. Its prompt is ">". So,
say we've opened the listener (my typing is
in this color and Snd's responses
are in this color):
Another quick way to check out the extension language is to go to the
Preferences dialog (in the Options menu), choose some items, then
save them. The saved file (~/.snd_prefs_guile for example) is a text file, a program in the current
extension language, that initializes Snd to use whatever items you chose.
Snd is organized as a list of sounds, each with a list of channels,
each channel containing lists of edits, marks, mixes, etc.
There are other objects such as colors, vcts (an optimization
of vectors), and regions; the currently active region is
called the selection. I originally presented all the
functions and variables in an enormous alphabetical
list, but that finally became unmanageable. In the following
sections, each of the basic entities is treated in a separate
section with cross-references where needed. The index
provides alphabetical entry.
Most of Snd's behavior can be customized. For example,
when a sound is saved, some people want to be warned if
a pre-existing sound is about to be destroyed; others (Snd's
author included) grumble "just do it". There are two ways
this kind of situation is handled in Snd; through global variables and hooks.
A hook is a list of callbacks invoked whenever its associated
event happens. When Snd exits, for example, any functions found
on the before-exit-hook list are evaluated; if any of them returns #t,
Snd does not exit.
Now when Snd is told to exit, it checks before-exit-hook, runs
unsaved-edits?, and if the latter returns #t, if prints
a worried message in the minibuffer, and refuses to
exit. Similar hooks customize actions such as closing
a sound (close-hook), clicking a mark (mark-click-hook),
pressing a key (key-press-hook), and so on.
The global variables handle various customizations that aren't callback-oriented.
For example,
as panes (sounds) come and go, Snd's overall size may change (this is
partly determined by the window manager, but is also
up to Snd); many people find this distracting — they would rather that the
overall window stick to one size. The Snd variable associated
with this is "auto-resize"; it can be accessed as:
The variables are presented as a special kind of function, rather than as bare variables, mainly
to ensure that Snd's response to the assignment is immediate.
The statement (set! (auto-resize) #f) can be placed in your ~/.snd initialization file
to make it the default setting for your version of Snd, or placed
in a separate file of Scheme code and loaded at any time via the load
function.
| | |
ask-before-overwrite |
#f |
|
ask-before-overwrite determines whether Snd asks before overwriting an existing file:
(set! (ask-before-overwrite) #t)
|
|
audio-input-device |
mus-audio-default |
|
This is the recorder's input device: (set! (audio-input-device) mus-audio-microphone).
|
|
audio-output-device |
mus-audio-default |
|
This is the audio output device for the play button.
|
|
auto-resize |
#t |
|
auto-resize determines whether the Snd window should be resized
when a sound is opened or closed.
|
|
auto-update |
#f |
|
auto-update determines whether Snd should update a file automatically
if it (the file) changes on disk due to some other process.
If Snd's view of a sound doesn't match the on-disk version of the sound,
a bomb icon warns you that there are two conflicting versions of the sound.
|
|
auto-update-interval |
60 |
|
This is the time (in seconds) between background checks for a changed file on
disk (see auto-update). If auto-update-interval is 0.0, the auto-update background process
is turned off.
If the file alternation monitor is running (the default if you have libfam or libgamin), auto-update-interval is ignored
since in that case the check happens instantly.
|
|
clipping |
#f |
|
If clipping is #t, output values are clipped to fit the current sndlib sample
representation's notion of -1.0 to just less than 1.0. The default (#f)
can cause wrap-around (if writing integer sound data) which makes the out-of-range values very obvious.
To control this action more closely, use clip-hook.
To get completely confused, see mus-clipping and mus-file-clipping:
this has become as messed up as the sampling rate settings!
|
|
cursor-location-offset |
0.05 |
|
cursor-location-offset is the offset in samples between Snd's notion of the location of the tracking cursor
(with-tracking-cursor in Snd jargon) and the actual (DAC-relative) location.
Since, in general, Snd can't tell how many samples of buffering there are between itself
and the speakers (audio cards have varying amounts), its notion of where to place the
tracking cursor can be wrong by an almost arbitrary amount. If you have some idea
of the buffering amount, you can correct this error via cursor-location-offset.
|
|
cursor-update-interval |
0.05 |
|
This is the time in seconds between
cursor redisplays if playing a sound with with-tracking-cursor #t.
If this number is too small, you may clicks during playback.
|
|
dac-combines-channels |
#t |
|
If dac-combines-channels is #t, and the current sound has more channels than are
supported by the available audio hardware, Snd mixes the extra channels into the available channels during audio output.
This provides a way to hear 4-channel sounds when you only have a stereo audio card.
If dac-combines-channels is #f, extra channels are not played.
|
|
dac-size |
256 |
|
dac-size is the audio output buffer size; it is not always meaningful. See play-with-envs in enved.scm or
play-sound in play.scm. When you change the control panel settings during playback, the snappiness of the
response is set, to some extent, by the dac-size. The default of 256 gives a stair-case effect in many
cases, whereas 2048 is smoother. This also affects the resampling smoothness of playback while dragging the
mark play triangle. Some audio choices, ALSA in particular, may ignore dac-size.
|
|
default-output-chans |
1 |
|
default-output-chans is the default number of channels when a new or temporary file is created,
or a save dialog is opened.
|
|
default-output-data-format |
mus-bfloat |
|
default-output-data-format is the default data format when a new or temporary file is created,
or a save dialog is opened. (The default, mus-bfloat, is from sndlib, standing for 32-bit big-endian floating point data).
Use mus-out-format for fastest IO.
The available output data formats are (b=big-endian, l=little, u=unsigned, short=16 bits, byte=8 bits, int = 32 bits):
mus-bshort mus-lshort mus-mulaw mus-alaw mus-byte mus-ubyte mus-bfloat
mus-lfloat mus-bint mus-lint mus-b24int mus-l24int mus-bdouble mus-ldouble
mus-ubshort mus-ulshort
|
|
default-output-header-type |
mus-next |
|
This is the default header type when a new file is created,
or a save dialog is opened. (The default, mus-next, stands for the NeXT/Sun sound file header).
The available output header-types are:
mus-next mus-aifc mus-riff mus-rf64 mus-nist mus-raw mus-ircam mus-aiff
mus-soundfont mus-bicsf mus-voc mus-svx mus-caff
|
|
default-output-srate |
44100 |
|
This is the default sampling rate when a new or temporary file is created,
or a save dialog is opened.
|
|
eps-bottom-margin |
0.0 |
|
eps-bottom-margin is the bottom margin used in snd.eps, created by the File:Print dialog, or the graph->ps function.
PostScript units are 1/72 of an inch (a "point" in printer jargon);
an inch is 2.54 cm:
Scheme:
(define (inches-to-ps inches)
(* inches 72))
(define (cm-to-ps cm)
(* cm (/ 72.0 2.54)))
|
|
Ruby:
def inches_to_ps(inches)
inches * 72
end
def cm_to_ps(cm)
cm * 72.0 / 2.54
end
|
|
Forth:
: inches-to-ps { inches }
inches 72 f*
;
: cm-to-ps { cm }
cm 2.54 f/ 72 f*
;
|
|
In the resulting .eps file, you'll find a concat statement near the
top of the file; the first and fourth numbers are scale factors on
the entire graph, the fifth is the left margin, and the sixth is the
bottom margin.
|
|
eps-file |
"snd.eps" |
|
This is the default name of the Postscript file produced by the File:Print dialog, or the graph->ps function.
|
|
eps-left-margin |
0.0 |
|
eps-left-margin is the left margin used in snd.eps, created by the File:Print dialog, or the graph->ps function.
|
|
eps-size |
1.0 |
|
eps-size is the scaler used to set the overall picture size in snd.eps,
created by the File:Print dialog, or the graph->ps function,
|
|
graph-cursor |
XC_crosshair (34) |
|
graph-cursor is the kind of cursor displayed following the mouse in the data graph.
It can be any of the cursors provided by X or Gtk+: (set! (graph-cursor) 22).
The X/Motif cursors are declared in /usr/include/X11/cursorfont.h or some such file;
gtk versions are in gdkcursor.h. Some useful choices are:
| Motif | Gtk+ | value |
| XC_arrow | GDK_ARROW | 2 |
| XC_center_ptr | GDK_CENTER_PTR | 22 |
| XC_cross | GDK_CROSS | 30 |
| XC_crosshair | GDK_CROSSHAIR | 34 |
| XC_left_ptr | GDK_LEFT_PTR | 68 |
| XC_plus | GDK_PLUS | 90 |
| XC_right_ptr | GDK_RIGHT_PTR | 94 |
| XC_tcross | GDK_TCROSS | 130 |
| XC_xterm | GDK_XTERM | 152 |
|
|
|
html-dir |
"." |
|
html-dir is the directory to search for documentation if an HTML reader is in use.
See the function html in index.scm.
|
|
html-program |
"firefox" |
|
This is the program to use to read HTML files.
On the Mac, you need to give the full path to the executable image: "/Applications/Safari.app/Contents/MacOS/Safari".
See the function html in index.scm.
|
|
just-sounds |
#f |
|
In Motif and in Gtk versions 2.3 or later,
if just-sounds is #t,
the file lists displayed by the file selection dialogs are filtered to show just
sound files (see add-sound-file-extension).
|
|
ladspa-dir |
#f |
|
LADSPA is a way of managing plug-ins in Linux. I consider it very old-fashioned, but
there are a bunch of ladspa libraries, and they're easy to load. so...
ladspa-dir is the
name of the directory to search for LADSPA plugin libraries (it can override or replace LADSPA_PATH).
See Snd and LADSPA.
|
|
log-freq-start |
32.0 |
|
log-freq-start is the start (lowest) frequency used in the log freq display (ffts). Since the log display emphasizes the lower
frequencies, but the lowest are all inaudible, it seemed more informative to squash the lowest 30Hz or so
into a single point (0 Hz) on the log freq graphs; otherwise the audible data starts about 1/4 of the way
down the x axis, wasting valuable screen space! But it also seemed a bother to have to set/reset the
spectro-start variable every time you wanted to flip between log and linear
displays. log-freq-start to the rescue? For other ideas along these lines, see display-bark-fft.
|
|
max-regions |
16 |
|
This sets the maximum size of the region list, the number of regions that are accessible.
|
|
max-virtual-ptrees |
3 |
|
This sets the maximum number of parse-trees (see ptree-channel) that the
virtual editor will allow in any virtual edit (once the maximum is reached, the operation is handled
locally, not via a virtual edit).
|
|
minibuffer-history-length |
8 |
|
This sets the maximum length of the minibuffer and listener M-p/M-n history lists.
|
|
open-file-dialog-directory |
"." |
|
open-file-dialog-directory is the name of the initial open file dialog directory (normally ".").
|
|
optimization |
0 |
|
optimization affects optimization levels in Scheme.
If non-zero, it causes Snd to try to optimize simple lambda forms passed to the searches and so forth.
This depends partly on the optargs module, and
applies only to Guile or Gauche.
The actual values of the optimization switch are:
0: no optimization (use the standard Scheme parser/evaluator)
1: optimize simple stuff (if complex result possible, give up)
2: assume nothing will return a complex number
3: if an undefined variable is encountered, try to guess its type
4: make questionable assumptions about variable types
5: make dangerous assumptions about variable locations (for set!)
6: try to splice in user-defined functions
Currently, the optimizer is able to speed up Scheme code by factors between
8 and 20; see snd-run.c for what is implemented, what the major limitations are, and so on.
If you set the optimization-hook to print out whatever its argument is, you can
find out what the optimizer found confusing:
(add-hook! optimization-hook (lambda (n) (display (format #f "opt: ~A~%" n))))
See also max-virtual-ptrees.
|
|
print-length |
12 |
|
For objects such as vcts, print-length sets the number of elements printed.
>(set! (print-length) 3)
3
>(make-vct 10 .1)
#<vct[len=10]: 0.100 0.100 0.100 ...>
|
|
run-safety |
0 |
|
If run-safety is not 0, code is added by the run macro to perform various
error checks, much as in CL/CLM with *clm-safety*. (The name *clm-safety* is defined in ws.scm as
a synonym for run-safety within with-sound).
|
|
save-dir |
#f |
|
save-dir is the name of the directory for saved-state files.
These files are written when you call save-state or
choose the Options:Save session menu item. If any of the current sounds has
an edit that requires saved data, it is written as a separate sound file, and
that file is reloaded automatically when you restart the saved session. To keep such
files safe, or at least separate from others, you can set up separate
directory for them.
(set! (save-dir) "/tmp").
|
|
save-state-file |
"saved-snd.scm" |
|
This is the saved state file name.
|
|
selection-creates-region |
#t |
|
If selection-creates-region is #t, a region is created whenever a selection is made. If you're editing very large sounds
and using selections, the region temp files can use up a lot of disk space (and the time to write
them); if you're not using regions anyway, this switch can turn them off.
|
|
show-backtrace |
#f |
|
If show-backtrace is #t, any error will display a backtrace automatically.
|
|
show-indices |
#f |
|
If show-indices is #t, each sound's name is preceded by its index in the sound pane.
|
|
show-selection-transform |
#f |
|
If show-selection-transform is #t, Snd displays the transform of the current active selection, if any.
The sonogram and spectrogram displays ignore this flag because they assume their time axis
matches that of the time domain graph.
|
|
sinc-width |
10 |
|
sinc-width is the width in samples of the sampling rate conversion sinc interpolation.
The higher this number, the better the src low-pass filter, but the slower
src runs. If you use too low a setting, you can sometimes hear high
frequency whistles leaking through. To hear these on purpose, make
a sine wave at (say) 55 Hz, then (src-sound '(0 3 1 1)) with sinc-width at 4.
|
|
snd-version |
"1-Jun-06" etc |
|
This is a string giving the current Snd version, normally a date. version is a Guile function; in Gauche it's gauche-version.
|
|
temp-dir |
#f |
|
temp-dir is the directory to use for temporary files; if it is #f, Snd uses whatever the system default is, usually "/tmp" or "/var/tmp".
See also snd-tempnam.
|
|
trap-segfault |
#t |
|
If trap-segfault is #t, Snd tries to catch segfaults and continue anyway. This normally gives you
a chance to save your current work, but please also send bil@ccrma.stanford.edu a bug report!
|
|
window-height |
0 |
|
window-height is the current Snd window height in pixels.
This is the same as
Scheme: (cadr (widget-size (cadr (main-widgets))))
Ruby: widget_size(main_widgets.cadr).cadr
Forth: main-widgets cadr widget-size cadr
except at startup when the window-height function and friends defer the assignment until after the main widgets
have been created. If Snd becomes confused about screen size, it can make its main window so large that
you can't get at any of the decorations for resizing the window; in this emergency you can
(set! (window-height) 300) or some such number.
|
|
window-width |
0 |
|
This is the current Snd window width in pixels.
|
|
window-x |
-1 |
|
This is the current Snd window left side position in pixels (-1 means unset).
This is (usually) the same as
(car (widget-position (cadr (main-widgets))))
|
|
window-y |
-1 |
|
This is the current Snd window upper side position in pixels (X numbering starts at 0 at the top, -1 means unset).
|
|
with-background-processes |
#t |
|
with-background-processes determines whether Snd should use background (idle time) processes for ffts and so forth.
It is intended primarily for auto-testing.
|
|
with-file-monitor |
#t |
|
If with-file-monitor is #t (the default), the file alteration monitor is active. There are still bugs
in this library that can cause Snd to hang — I haven't tracked down what the problem is yet; in the meantime,
set this switch to #f to disable the monitor. (One such bug was fixed in gamin 1.8.0).
|
|
with-relative-panes |
#t |
|
If with-relative-panes is #t in the Motif
version of Snd, a multichannel sound tries to retain the relative channel graph sizes
when the outer sash (the overall sound size sash) changes.
Mono sounds and the listener are not affected (perhaps they should be?).
|
|
zoom-focus-style |
zoom-focus-active |
|
This determines what a zoom action focuses (centers) on. The choices are
zoom-focus-left, zoom-focus-right, zoom-focus-active, zoom-focus-middle,
or a function of 6 arguments. The function should return the new window left edge as a float.
Its arguments are the current sound index, channel number, zoom slider value (0.0 to 1.0), time domain window left and right
edges in seconds, and the current total x axis size (seconds) corresponding to a slider value
of 1.0.
(set! (zoom-focus-style) (lambda (snd chn zx x0 x1 range) (- x1 (* zx range))))
mimics zoom-focus-right. zoom-focus-active tries to focus on some object in the view: the cursor, a mix or mark, etc.
See also Zoom options.
|
When some user-interface action takes place, code is called that responds to that action;
these functions are sometimes called callbacks; the variable that holds a list of such
callbacks is known as a hook.
A hook provides
a way to customize user-interface
actions.
The hook itself is list of functions. The function add-hook! adds a function to a hook's
list, remove-hook! removes a function, and reset-hook! clears out the list.
For example, the hook that is checked when you click the sound's name in the minibuffer is
name-click-hook. We can cause that action to print "hi":
If there is more than one function attached to a hook, some of the hooks
"or" the functions together (marked [or] below); that is they
run through the list of functions, and if any function returns something other than #f, the
hook invocation eventually returns the last such non-#f value. A few hooks are "cascade"
hooks; that is, each function gets the result of the previous function, and
the final function's value is returned.
In the other
cases ("progn", the name coming from Common Lisp), the result returned by the hook is the result of the last function in the list.
Whatever the hook combination choice, all the functions on the hook list are run
on each invocation. There are a variety of hook-related functions in hooks.scm.
There are several basic actions that involve a bunch of hooks. Here is a schematic view of
some of these sequences.
You can find out what's on a given hook with the following (which is mostly adding carriage returns to the
printout from hook->list):
These hooks are extremely easy to add; if there's some user-interface action
you'd like to specialize in some way, send me a note.
hooks.scm has snd-hooks and reset-all-hooks, as well as other
useful hook-related functions.
In the following list of hooks, the arguments after the hook name refer to the arguments to the functions invoked by
the hook. That is, after-apply-controls-hook (snd) means that the functions on the
after-apply-controls-hook list each take one argument, a sound index.
If the argument list is followed by some
indication such as "[or]", that means the various hook function values are or-d together.
after-apply-controls-hook (snd)
|
|
This hook is called when apply-controls finishes.
add-amp-controls in snd-motif.scm uses this hook to
reset any added amplitude sliders to 1.0.
|
|
after-graph-hook (snd chn)
|
|
This hook is called after a graph is updated or redisplayed; see display-samps-in-red,
draw-smpte-label in snd-motif.scm, or add-comment.
This is the hook to use when adding your own finishing touches to the display; if added earlier they risk
being erased by Snd as it redraws graphs.
|
|
after-lisp-graph-hook (snd chn)
|
|
This hook is called after a "lisp" graph is updated or redisplayed. The lisp-graph-hook functions
are called before the actual graph is displayed, so if you want to add to a graph in some way, you need to
use after-lisp-graph-hook.
display-bark-fft in dsp.scm uses it to draw the x axis labels and
ticks for various frequency scales.
|
|
after-open-hook (snd)
|
|
This hook is called just before a newly opened sound's window is displayed.
This provides a way to set various sound-specific defaults.
For example, the following causes Snd to default to locally
sync'd channels (that is, each sound's channels are sync'd
together but are independent of any other sound), united channels (all chans in one graph),
and filled graphs (not line segments or dots, etc):
See also C-x b support in examp.scm,
remember-sound-state in extensions.scm,
enved.scm, and various examples in snd-motif.scm.
|
|
after-save-as-hook (index filename from-dialog)
|
|
This hook is called after File:Save as. See emacs-style-save-as in snd7.scm
which closes the current sound and opens the newly created one to mimic Emacs.
|
|
after-save-state-hook (filename)
|
|
This hook is called after Snd has saved its state (save-state). 'filename' is the (otherwise complete) saved state
program. See ws-save-state in ws.scm
or remember-sound-state in extensions.scm. Both use this sequence:
(lambda (filename)
(let ((fd (open filename (logior O_RDWR O_APPEND)))) ; open to write at the end
(format fd "~%~%;;; save-state stuff here ~%")
...
(close fd)))
|
|
after-transform-hook (snd chn scaler)
|
|
This hook is called just after an FFT (or spectrum) is calculated.
|
|
bad-header-hook (filename) [or]
|
|
This hook is called if a file has a bogus-looking header (that is,
a header with what appear to be bad values such as a negative number of channels).
If a hook function returns #t, Snd does not try to open the file.
(add-hook! bad-header-hook (lambda (n) #t)) ; don't open bogus-looking files
If no header is found, open-raw-sound-hook is invoked instead ("raw" = "headerless").
|
|
before-close-hook (snd) [or]
|
|
This hook is called when a file is about to be closed. If a hook function returns #t, the file is not closed (see
check-for-unsaved-edits in extensions.scm).
|
|
before-exit-hook () [or]
|
|
This hook is called upon a request to exit Snd.
If a hook function returns #t, Snd does not exit. This can be used to check
for unsaved edits (see above or extensions.scm: unsaved-edits?).
|
|
before-save-as-hook (index filename selection srate header-type data-format comment) [or]
|
|
This hook is called before save-sound-as or File:Save as.
If a hook function returns something other than #f, the
save is not performed. This hook provides a way to do last minute fixups (srate conversion for example)
just before a sound is saved. The arguments to the hook function describe the requested attributes of the saved sound;
'index' is the to-be-saved sound's index; 'filename' is the output file's name; 'selection' is #t if
we're saving the selection.
(add-hook! before-save-as-hook
(lambda (index filename selection sr type format comment)
(if (not (= sr (srate index)))
(let ((chns (chans index)))
(do ((i 0 (1+ i)))
((= i chns))
(src-channel (exact->inexact (/ (srate index) sr)) 0 #f index i))
(save-sound-as filename index :header-type type :data-format format :srate sr :comment comment)
;; hook won't be invoked recursively
(do ((i 0 (1+ i)))
((= i chns))
(undo 1 index i))
#t) ; tell Snd that the sound is already saved
#f)))
|
|
|
before-save-state-hook (filename) [or]
|
|
This hook is called before Snd saves its state (save-state). 'filename' is the saved state
file. If the hook functions return #t, the save state file is opened in append mode (rather than create/truncate),
so you can write preliminary stuff via this hook, then instruct Snd not to clobber it during the save process.
(add-hook! before-save-state-hook
(lambda (name)
(with-output-to-file name
(lambda ()
(display (format #f ";this comment will be at the top of the saved state file.~%~%"))
#t))))
|
|
|
before-transform-hook (snd chn) [progn]
|
|
This hook is called just before an FFT (or spectrum) is calculated. If a hook function returns
an integer, that value is used as the starting point (sample number) of the fft. Normally,
the fft starts from the left window edge. To have it start at mid-window:
(add-hook! before-transform-hook
(lambda (snd chn) ; 0.5 * (left + right) = midpoint
(inexact->exact (round (* 0.5 (+ (right-sample snd chn) (left-sample snd chn)))))))
|
The following
somewhat brute-force code shows a way to have the fft reflect the position
of a moving mark:
|
|
clip-hook (clipping-value) [progn]
|
|
This hook is called whenever a sample is about to be clipped while writing out a sound file.
The hook function can return the new value.
|
|
close-hook (snd)
|
|
This hook is called when a file is closed (before the actual close, so the index 'snd' is still valid).
(add-hook! close-hook
(lambda (snd)
(system "sndplay wood16.wav")))
|
| |
$close_hook.add_hook!("play") do |snd|
system("aplay wood16.wav")
end
|
|
close-hook is used in autosave.scm,
the C-x b support in examp.scm,
remember-sound-state in extensions.scm,
the peak env support in peak-env.scm, and
many other places.
|
|
color-hook () [progn]
|
|
This hook is called whenever one of the variables associated with the color dialog changes.
See start-waterfall in snd-gl.scm.
|
|
dac-hook (data) [progn]
|
|
This hook is called just before data is sent to DAC; 'data' is a sound-data object.
See with-level-meters in snd-motif.scm.
|
|
draw-mark-hook (id) [progn]
|
|
This hook is called before a mark is drawn (in XOR mode except in cairo). If the hook function returns #t, the mark is not drawn.
mark-sync-color
in snd-motif.scm uses this hook to draw sync'd marks in some other color than the current mark-color.
|
|
draw-mix-hook (id old-x old-y x y) [progn]
|
|
This hook is called before a mix tag is drawn. If the hook function returns either #t or a list, the mix tag is not drawn by Snd (the
assumption is that the hook function drew something). old-x and old-y are the previous mix tag positions (in case
you're using draw-mix-hook to draw your own mix tag as in musglyphs.scm). x and y give the current position.
If the hook function returns a list, its two elements (integers) are treated as the mix's tag x and y locations
for subsequent mouse click hit detection.
|
|
drop-hook (filename) [or]
|
|
This hook is called each time Snd receives a drag-and-drop event, passing the hook functions the dropped filename.
If the hook functions return #t, the file is not opened by Snd. Normally if you drag a file icon to the menubar,
Snd opens it as if you had called open-sound. If you drag the icon to a particular channel,
Snd mixes it at the mouse location in that channel. To get Snd to
mix the dragged file even from the menubar:
(add-hook! drop-hook (lambda (filename) (mix filename) #t)) ; return #t = we already dealt with the drop
snd-motif.scm has examples that add a drop callback to an arbitrary widget, or
change an existing callback (to pass the sound index and channel number to the drop callback function, bypassing drop-hook).
|
|
during-open-hook (fd name reason)
|
|
This hook is called after file is opened, but before data has been read.
This provides an opportunity to set sndlib prescaling values:
The prescaling affects only sound data made up of floats or doubles. 'reason' is an integer indicating why this file is being opened:
0: reopen a temporarily closed file (internal to Snd — normally invisible)
1: sound-open, File:open etc — the normal path to open a sound
2: copy reader — another internal case; this happens if a sound is played and edited at the same time
3: insert sound (File:Insert etc)
4: re-read after an edit (file changed, etc — an invisible editing case)
5: open temp file after an edit (another invisible editing case)
6: mix sound (File:Mix etc)
So, to restrict the hook action to the normal case where Snd is opening a file for the first time,
check that 'reason' is 1, or perhaps 1, 3, or 6 (these read the external form of the data).
|
|
enved-hook (env pt new-x new-y reason) [cascade]
|
|
Each time a breakpoint is changed in the envelope editor, this hook
is called; if it returns a list, that list defines the new envelope,
otherwise the breakpoint is moved (but not beyond the neighboring
breakpoint), leaving other points untouched. The kind of change that triggered the hook callback
is indicated by the argument 'reason'. It can be enved-move-point, enved-delete-point,
or enved-add-point. This hook makes it possible to define attack
and decay portions in the envelope editor, or use functions such as
stretch-envelope from env.scm:
(add-hook! enved-hook
(lambda (env pt x y reason)
(if (= reason enved-move-point)
(if (and (> x 0.0) (< x (envelope-last-x env))) ; from env.scm
(let* ((old-x (list-ref env (* pt 2)))
(new-env (stretch-envelope env old-x x)))
(list-set! new-env (+ (* pt 2) 1) y)
new-env)
env)
#f)))
|
If there are several functions on the hook, each gets the envelope
result of the preceding function (if a function returns #f, the envelope
is not changed). A math-type would call this a "function composition"
method combination; a filter-type would say "cascade";
|
|
exit-hook ()
|
|
This hook is called upon exit.
It can be used
to perform cleanup activities; in peak-env.scm, for example, we save peak-env info upon exit:
(add-hook! exit-hook (lambda () (for-each save-peak-env-info (sounds))))
For more examples, see extensions.scm and autosave.scm.
Guile's exit-hook is shadowed by this variable.
|
|
graph-hook (snd chn y0 y1) [or]
|
|
This hook is called each time a graph is updated or redisplayed.
If its hook functions return #t, the display is not updated.
See examp.scm for many examples. If you want to add your own graphics to the display, use after-graph-hook.
(add-hook! graph-hook
(lambda (snd chn y0 y1)
"set the dot size depending on the number of samples being displayed"
(let ((dots (- (right-sample snd chn) (left-sample snd chn))))
(if (> dots 100)
(set! (dot-size snd chn) 1)
(if (> dots 50)
(set! (dot-size snd chn) 2)
(if (> dots 25)
(set! (dot-size snd chn) 3)
(set! (dot-size snd chn) 5))))
#f)))
|
|
|
help-hook (subject help-string) [cascade]
|
|
This hook is called from snd-help with the current help subject and default help-string.
Say we want the index.scm
procedure html called any time snd-help is called (from C-? for example):
(add-hook! help-hook (lambda (subject help) (html subject) #f))
If there is more than one hook function, each function's result is passed as input to the next function.
|
|
initial-graph-hook (snd chn dur) [or]
|
|
This hook is called the first time a given channel is displayed (when the sound is first opened).
If the hook function returns a list, the list's contents are interpreted as:
(list x0 x1 y0 y1 label ymin ymax)
(all trailing values are optional), where these numbers set the
initial x and y axis limits and the x axis label.
The default (an empty hook) is equivalent to:
(add-hook! initial-graph-hook (lambda (snd chn dur) (list 0.0 0.1 -1.0 1.0 "time" -1.0 1.0)))
The 'dur' argument is the total length in seconds of the displayed portion of the channel, so to cause the
entire sound to be displayed initially:
(add-hook! initial-graph-hook (lambda (snd chn dur) (list 0.0 dur)))
To get other the data limits (rather than the default y axis limits of -1.0 to 1.0), you can use mus-sound-maxamp,
but if that sound's maxamp isn't already known, it can require a long process of reading the file. The following hook procedure
uses the maxamp data if it is already available or the file is short:
(add-hook! initial-graph-hook
(lambda (snd chn dur)
(if (or (mus-sound-maxamp-exists? (file-name snd))
(< (frames snd chn) 10000000))
(let* ((amp-vals (mus-sound-maxamp (file-name snd)))
(max-val (max 1.0 (list-ref amp-vals (+ (* chn 2) 1)))))
;; max amp data is list: (sample value sample value ...)
(list 0.0 dur (- max-val) max-val)) ; these are the new y-axis limits
(list 0.0 dur -1.0 1.0)))) ; max amp unknown, so use defaults
|
A similar problem affects the 'dur' argument. If the file is very long, Snd starts
a background process reading the file's data to get an overall amplitude envelope,
and this envelope is what it actually displays when you zoom out to look at the entire
sound. If you set 'x1' to 'dur', you effectively get two such processes
contending for access to the data. One way around this is to save the envelope as
a "peak envelope" in Snd's nomenclature;
load peak-env.scm to make this process automatic.
|
|
key-press-hook (snd chn key state) [or]
|
|
This hook is called upon key press while the mouse is in the lisp graph (the third graph,
to the right of the time and fft graphs).
If its function returns #t, the key press is not passed to the main handler.
'state' refers to the control, meta, and shift keys.
start-enveloping in enved.scm uses this hook to add C-g and C-. support to the
channel-specific envelope editors.
|
|
lisp-graph-hook (snd chn) [progn]
|
|
This hook is called just before the lisp graph is updated or redisplayed (see display-db).
If its function returns a list of pixels (xm style), these are used in order by the list of graphs (if any), rather than Snd's default set
(this makes it possible to use different colors for the various graphs).
If it returns a function (of no arguments), that function is called rather than the standard graph routine:
For a fancy example, see display-bark-fft in dsp.scm.
|
|
listener-click-hook (textpos)
|
|
This hook is called when a click occurs in the listener; the 'textpos' argument is the position in the text
(a character number) where the click occurred.
See click-for-listener-help in draw.scm.
|
|
mark-click-hook (id) [progn]
|
|
This hook is called when a mark is clicked; return #t to squelch the default minibuffer mark identification. The following
hook function is used in with-marked-sound in ws.scm to display arbitrary info about a mark.
|
|
mark-drag-hook (id)
|
|
This hook is called when a mark is dragged.
|
|
mark-drag-triangle-hook (id x time dragged-before) [progn]
|
|
This hook is called when a mark play triangle is dragged. The smoothness of the response to the drag motion is
largely determined by dac-size.
'dragged-before' is #f when the drag starts and #t thereafter. 'x' is the mouse x location in the current
graph. 'time' is the uninterpreted (graphics toolkit) time at which the drag event was reported. 'id' is the mark id. If the hook function returns #t,
Snd takes no further action. To set up to play, then interpret the motion yourself, return #f on the first call,
and #t thereafter:
(let ((first-x 0))
(add-hook! mark-drag-triangle-hook
(lambda (id x time dragged-before)
(if (not dragged-before)
(set! first-x x)
(set! (speed-control) (/ (- x first-x) 16.0)))
dragged-before)))
|
|
|
mark-hook (id snd chn reason)
|
|
This hook is called when a mark is added, deleted, or moved (but not while moving). 'id' can be -1 (i.e. no specific mark).
'reason' can be 0: add, 1: delete, 2: move (via set! mark-sample), 3: delete all marks, 4: release (after drag).
In the "release" case, the hook is called upon button release before any edits (control-drag of mark) or sorting (simple drag),
and if the mark-sync is not 0, the hook is called on each syncd mark.
(define (snap-mark-to-beat)
;; when a mark is dragged, its end position is always on a beat
(let ((mark-release 4))
(add-hook! mark-hook
(lambda (mrk snd chn reason)
(if (= reason mark-release)
(let* ((samp (mark-sample mrk))
(bps (/ (beats-per-minute snd chn) 60.0))
(sr (srate snd))
(beat (floor (/ (* samp bps) sr)))
(lower (inexact->exact (/ (* beat sr) bps)))
(higher (inexact->exact (/ (* (1+ beat) sr) bps))))
(set! (mark-sample mrk)
(if (< (- samp lower) (- higher samp))
lower
higher))))))))
|
|
|
mix-click-hook (id) [progn]
|
|
This hook is called when a mix tag is clicked; return #t to omit the default action which is to print the mix id in
the minibuffer. A more informative version is mix-click-info in mix.scm.
Here's an example that sets a mix's amps to 0 if you click it (see mix-click-sets-amp
in mix.scm for a fancier version):
|
|
mix-drag-hook (id x y)
|
|
This hook is called when a mix is dragged.
A neat example is to set up an empty sound with a 1.0 in sample 0, mix in a vct containing one element of 0.5, then set
up this mix-drag-hook:
(add-hook! mix-drag-hook (lambda (id x y) (update-transform-graph)))
and turn on the FFT graph. As you drag the mix, you can see the spectral effect of that
moving value as a comb filter.
|
|
mix-release-hook (id samps) [progn]
|
|
This hook is called after a mix has been dragged by the mouse to a new position. 'id' is the mix id,
'samps' is the number of samples moved during the drag. If its function returns #t, the final position
of the mix is
hook's responsibility. See snap-mix-to-beat in mix.scm.
|
|
mouse-click-hook (snd chn button state x y axis) [or]
|
|
This hook is called upon a mouse button release or click (with various exceptions). If its function returns #t, the click is ignored by Snd.
See the current-window-location function in draw.scm. Here's a simpler example:
(define (click-to-center snd chn button state x y axis)
;; if mouse click in time domain graph, set cursor as normally, but also center the window
(if (= axis time-graph)
(let ((samp (inexact->exact (* (srate snd) (position->x x snd chn)))))
(set! (cursor snd chn) samp)
(set! (right-sample snd chn)
(- samp (inexact->exact (* .5 (- (left-sample snd chn) (right-sample snd chn))))))
(update-time-graph)
#t)
#f))
(add-hook! mouse-click-hook click-to-center)
;;; this example disables button 2 -> insert selection
(add-hook! mouse-click-hook
(lambda (snd chn button state x y axis)
(and (= axis time-graph) (= button 2))))
|
The mouse scroll wheel is sometimes reported as buttons 4 and 5; in the next example,
turning the wheel zooms the graph in or out:
(add-hook! mouse-click-hook
(lambda (snd chn button state x y axis)
(if (and (= axis time-graph)
(or (= button 4) (= button 5))) ; mouse scroll wheel
(let ((midpoint (* 0.5 (apply + (x-bounds))))
(dur (/ (frames) (srate)))
(range (if (= button 4)
(* -0.25 (apply - (x-bounds))) ; zoom in
(abs (apply - (x-bounds)))))) ; zoom out
(set! (x-bounds) (list (max 0.0 (- midpoint range))
(min dur (+ midpoint range))))))
#f))
|
Here is a Forth example:
mouse-click-hook lambda: <{ snd chn button state x y axis -- }>
axis time-graph = if
$" freq: %.3f" '( snd chn #f cursor snd chn spot-freq ) string-format
snd #f report-in-minibuffer
else
#f
then
; add-hook!
|
|
|
mouse-drag-hook (snd chn button state x y)
|
|
This hook is called when the mouse is dragged within the lisp graph (see enved.scm or rtio.scm).
|
|
mouse-enter-graph-hook (snd chn)
|
|
This hook is called when the mouse enters a channel's drawing area (graph pane).
|
|
mouse-enter-label-hook (type position label)
|
|
This hook is called when the mouse enters a file viewer or region label.
The 'type' is 1 for view files list, and 2 for regions.
The 'position' is the scrolled list position of the label.
The label itself is 'label'. We can use the finfo procedure in examp.scm
to popup file info as follows:
(add-hook! mouse-enter-label-hook
(lambda (type position name)
(if (not (= type 2))
(info-dialog name (finfo name)))))
|
See also files-popup-info in nb.scm.
|
|
mouse-enter-listener-hook (widget)
|
|
This hook is called when the mouse enters the listener pane. This hook, along with the parallel graph hook
makes it possible to set up Snd to behave internally like a window manager with pointer focus. That is, to
ensure that the pane under the mouse is the one that receives keyboard input, we can define the following
hook procedures:
I much prefer this style of operation.
|
|
mouse-enter-text-hook (widget)
|
|
This hook is called when the mouse enters a text widget (this is the third of the pointer focus hooks).
|
|
mouse-leave-graph-hook (snd chn)
|
|
This hook is called when the mouse leaves a channel's drawing area (graph pane).
|
|
mouse-leave-label-hook (type position name)
|
|
This hook is called when the mouse exits one of the labels covered by mouse-enter-label-hook.
See nb.scm.
|
|
mouse-leave-listener-hook (widget)
|
|
This hook is called when the mouse leaves the listener pane.
|
|
mouse-leave-text-hook (widget)
|
|
This hook is called when the mouse leaves a text widget.
|
|
mouse-press-hook (snd chn button state x y)
|
|
This hook is called upon a mouse button press within the lisp graph (see enved.scm). The 'x' and 'y' values are
relative to the lisp graph axis (as if the raw mouse pixel position was passed through
position->x and position->y).
|
|
mus-error-hook (error-type error-message) [or]
|
|
This hook is called upon mus-error.
If its functions return #t, Snd ignores the error (it assumes you've handled it via the hook).
This hook is used in play-sound in play.scm to flush an error message
that the Snd ALSA support code generates (or used to generate).
Both mus_error and mus_print run this hook; in the mus_print case, the 'type' is mus-no-error (0).
You can redirect mus_print output from stderr (the default) to stdout via:
(add-hook! mus-error-hook
(lambda (typ msg)
(and (= typ 0) ; it's mus_print, not mus_error
(display msg)))) ; display returns some non-#f result, I assume
|
To decode the 'error-type' argument, see mus-error-type->string.
|
|
name-click-hook (snd) [or]
|
|
This hook is called when the sound name is clicked (in the label in the minibuffer region of the sound's pane).
If the function returns #t, the usual highly informative minibuffer babbling is squelched.
|
|
new-sound-hook (filename)
|
|
This hook is called whenever a new sound file is being created. sound-let in ws.scm uses
this hook to keep track of newly created temporary sounds so that it can delete them once they are no longer needed.
|
|
new-widget-hook (widget)
|
|
This hook is called each time a dialog or a new set of channel or sound widgets is created.
This is used in misc.scm (paint-all) to
make sure all newly created widgets have the same background pixmaps.
|
|
open-hook (filename) [or]
|
|
This hook is called before a sound file is opened.
If the function returns #t, or the sound is not readable (bad header, etc) the file is not opened
and any corresponding after-open-hook functions are not called.
If it returns a string (a filename), that file is opened instead of the original one.
(add-hook! open-hook
(lambda (filename)
(if (= (mus-sound-header-type filename) mus-raw)
;; check for "OggS" first word, if found, translate to something Snd can read
(if (call-with-input-file filename
(lambda (fd)
(and (char=? (read-char fd) #\O)
(char=? (read-char fd) #\g)
(char=? (read-char fd) #\g)
(char=? (read-char fd) #\S))))
(let ((aufile (string-append filename ".au")))
(if (file-exists? aufile) (delete-file aufile))
(system (format #f "ogg123 -d au -f ~A ~A" aufile filename))
aufile) ; now open-sound will read the new .au file
#f)
#f)))
|
See also open-buffer in examp.scm.
|
|
open-raw-sound-hook (filename current-choices) [cascade]
|
|
This hook is called each time open-sound encounters a headerless file.
Its result can be a list describing the raw file's attributes (thereby bypassing the Raw File Dialog and so on):
(list chans srate data-format data-location data-length) where trailing elements can
be omitted ('data-location' defaults to 0, and 'data-length' defaults to the file length in bytes).
If there is more than one function on the hook list, functions after the first get the
on-going list result (if any) as the 'current-choices' argument (the empty list is the default).
(add-hook! open-raw-sound-hook (lambda (file choices) (list 1 44100 mus-lshort)))
Return '() to accept all the current raw header defaults; return #f to fallback on the Raw File Dialog.
The raw header defaults are stereo, 44100 Hz, big endian short data; these values can be changed in the
Raw File Dialog, by calling open-raw-sound with explicit arguments,
or via mus-header-raw-defaults.
If the hook function returns #t, the open-sound returns without opening.
|
|
|