#lang scribble/doc @(require scribble/manual) @title{@bold{RSound}: An Adequate Sound Engine for Racket} @author[(author+email "John Clements" "clements@racket-lang.org")] @defmodule[rsound]{This collection provides a means to represent, read, write, play, and manipulate sounds. It uses the 'portaudio' library, which appears to run on Linux, Mac, and Windows. The package contains binary versions of the mac & windows portaudio libraries. This is because Windows and Mac users are less likely to be able to install their own versions of the library; naturally, this is a less-than-perfect solution. Sound playing happens on a separate racket thread and custodian. This means that re-running the program or interrupting with a "Kill" will not halt the sound. It represents all sounds internally as stereo 16-bit PCM, with all the attendant advantages (speed, mostly) and disadvantages (clipping). Does it work on your machine? Try this example: @racketblock[ (require rsound rsound/util) (play-rsound ding) ] } @section{Sound Control} These procedures start and stop playing sounds and loops. @defproc[(rsound-play (rsound rsound?)) void?]{ Plays an rsound. Interrupts an already-playing sound, if there is one.} @defproc[(rsound-loop (rsound rsound?)) void?]{ Plays an rsound repeatedly. Continues looping until interrupted by another sound command.} @defproc[(change-loop (rsound rsound?)) void?]{ When the current sound or loop finishes, starts looping this one instead.} @defproc[(stop-playing) void]{ Stop the currently playing sound.} @section{Sound I/O} These procedures read and write rsounds from/to disk. The RSound library reads and writes WAV files only; this means fewer FFI dependencies (the reading & writing is done in racket), and works on all platforms. @defproc[(rsound-read (path path-string?)) rsound?]{ Reads a WAV file from the given path, returns it as an rsound. It currently has lots of restrictions (it insists on 16-bit PCM encoding, for instance), but deals with a number of common bizzarre conventions that certain WAV files have (PAD chunks, extra blank bytes at the end of the fmt chunk, etc.), and tries to fail relatively gracefully on files it can't handle. Reading in a large sound can result in a very large value (~10 Megabytes per minute); for larger sounds, consider reading in only a part of the file, using @racket[rsound-read/clip].} @defproc[(rsound-read/clip (path path-string?) (start nonnegative-integer?) (finish nonnegative-integer?)) rsound?]{ Reads a portion of a WAV file from a given path, starting at frame @racket[start] and ending at frame @racket[finish]. It currently has lots of restrictions (it insists on 16-bit PCM encoding, for instance), but deals with a number of common bizzarre conventions that certain WAV files have (PAD chunks, extra blank bytes at the end of the fmt chunk, etc.), and tries to fail relatively gracefully on files it can't handle.} @defproc[(read-rsound-frames (path path-string?)) nonnegative-integer?]{ Returns the number of frames in the sound indicated by the path. It parses the header only, and is therefore much faster than reading in the whole sound. The file must be encoded as a WAV file readable with @racket[rsound-read].} @defproc[(read-rsound-sample-rate (path path-string?)) number?]{ Returns the sample-rate of the sound indicated by the path. It parses the header only, and is therefore much faster than reading in the whole sound. The file must be encoded as a WAV file readable with @racket[rsound-read].} @defproc[(rsound-write (rsound rsound?) (path path-string?)) void?]{ Writes an rsound to a WAV file, using stereo 16-bit PCM encoding. It overwrites an existing file at the given path, if one exists.} @section{Rsound Manipulation} These procedures allow the creation, analysis, and manipulation of rsounds. @defstruct[rsound ([data s16vector?] [frames nonnegative-integer?] [sample-rate nonnegative-number?])]{ Represents a sound.} @defproc[(make-silence [frames nonnegative-integer?] [sample-rate nonnegative-number?]) rsound?]{ Returns an rsound of length @racket[frames] containing silence. This procedure is relatively fast.} @defproc[(rsound-nth-sample/left (rsound rsound?) (frame nonnegative-integer?)) nonnegative-integer?]{ Returns the @racket[n]th sample from the left channel of the rsound, represented as a number in the range @racket[#x-8000] to @racket[#x7fff].} @defproc[(rsound-nth-sample/right (rsound rsound?) (frame nonnegative-integer?)) nonnegative-integer?]{ Returns the @racket[n]th sample from the right channel of the rsound, represented as a number in the range @racket[#x-8000] to @racket[#x7fff].} @defproc[(rsound-clip (rsound rsound?) (start nonnegative-integer?) (finish nonnegative-integer?)) rsound?]{ Returns a new rsound containing the frames in @racket[rsound] from the @racket[start]th to the @racket[finish]th - 1. This procedure copies the required portion of the sound.} @defproc[(rsound-append* (rsounds (listof rsound?))) rsound?]{ Returns a new rsound containing the given @racket[rsounds], appended sequentially. This procedure is relatively fast. All of the given rsounds must have the same sample-rate.} @defproc[(rsound-overlay* (assembly-list (listof (list/c rsound? nonnegative-integer?)))) rsound?]{ Returns a new rsound containing all of the given rsounds. Each sound begins at the frame number indicated by its associated offset. The rsound will be exactly the length required to contain all of the given sounds. So, suppose we have two rsounds: one called 'a', of length 20000, and one called 'b', of length 10000. Evaluating @racketblock[ (rsound-overlay* (list (list a 5000) (list b 0) (list b 11000)))] ... would produce a sound of 21000 frames, where each instance of 'b' overlaps with the central instance of 'a'. } @defproc[(fun->mono-rsound (frames nonnegative-integer?) (sample-rate nonnegative-integer?) (fun (-> nonnegative-integer? inexact-number?))) rsound?]{ Builds a sound of length @racket[frames] and sample-rate @racket[sample-rate] by calling @racket[fun] with integers from 0 up to @racket[frames]-1. The result should be an inexact number in the range @racket[-1.0] to @racket[1.0]. Values outside this range are clipped. Both channels are identical. } @section{Visualizing Rsounds} @defmodule[rsound/draw] @defproc[(rsound-draw [rsound rsound?] [#:title title string?] [#:width width nonnegative-integer? 800] [#:height height nonnegative-integer? 200]) void?]{ Displays a new window containing a visual representation of the sound as a waveform.} @section{RSound Utilities} @defmodule[rsound/util] not-yet-documented: @racket[(provide twopi s16max sine-wave sawtooth-wave square-wave fader dc-signal signal-*s signal-+s make-tone make-squaretone ding make-ding split-in-4 times vectors->rsound echo1 )] @section{Reporting Bugs} For Heaven's sake, report lots of bugs!