Csound-expression Reference
by Anton Kholomiov
Quick start types and functions.
Basic types
In the library we have several basic types:
Sig  -- audio and control signals
D    -- constant numbers
Tab  -- functional tables
SE   -- Side-effects
Spec -- spectrums (used in pvs opcodes)Rendering the audio
dac   -- send audio to speakers
dacBy -- supply options (rates, drivers, midi-devices)
vdac  -- dac with virtual midi-keyboard.
writeSnd -- render audio to file offline
writeSndBy -- supply options (rates, drivers, midi-devices)
setRates  -- sets the sample rate and the block size
setJack   -- sets the jack nameExamples:
> let opt = setRates 48000 128 <> setJack "sine-wave"
> dacBy opt (osc 220)We use the operator <> to combine options. See the standard class Data.Monoid for more information.
Sound design tools
Audio waves
Pure sine, sawtooth, square, triangle, and pulse with adjustable width:
osc, saw, sqr, tri :: Sig -> Sig
pw :: Sig -> Sig -> Sig
pw bandwidth frequency = ...Unipolar waves (useful for LFOs): uosc, usaw, usqr, utri.
Examples:
> dac $ mul 0.5 $ tri $ 220 * (1 + 0.08 * uosc 3)
> dac $ mul 0.25 $ pw (0.5 * uosc 0.12) 220 + pw (0.2 + 0.3 * uosc 0.2) 220Envelope generators
linseg, expseg :: [D] -> SigJust like in Csound but arguments are passed in the list, and the last value is held:
> linseg [0, 0.2, 1, 1.3, 0.5, 1.5, 0]So the zero is held. It is not going to drop down to infinity.
[[[Actually linseg in Csound already behaves in the same way so this point is perhaps inaccurate. line and expseg will however continue along their defined trajectory.]]]Linear adsr and exponential adsr envelope generators:
leg, xeg :: D -> D -> D -> D -> SigAttack-sustain-release envelope:
fades :: D -> D -> Sig
fades fadeInTime fadeOutTime = ...Examples:
> dac $ osc $ 220 * (1 + 0.5 * linseg [0, 2, 1, 2, 0.5, 1, 0.5, 1, 0])
> let env = leg 0.02 0.1 0 0
> dac $ mul env $ sqr $ 220 * env
> vdac $ midi $ onMsg $ mul (fades 0.1 0.5) . oscFilters
Moog-like low-pass filter:
mlp :: Sig -> Sig -> Sig -> Sig
mlp centerFreq resonance asig = aoutNote that the order of arguments is reversed with respect to the ordering used in Csound. The reason for this is that in Haskell it is convenient to use less arguments as first arguments. Because in Haskell we have partial application. With partial application if we apply a single argument to the function of to arguments, it will not lead to a type error. It creates a function of one argument. The first argument is bound to a passed value and the second, therefore, is free to be used.
Here is an example:
> :t lp
mlp :: Sig -> Sig -> Sig -> Sig
> :t (lp 1500)
(mlp 1500) :: Sig -> Sig -> Sig
> :t (mlp 1500 0.4)
(mlp 1500 0.4) :: Sig -> Sig
> :t (mlp 1500 0.4 $ saw 200)
(mlp 1500 0.4 $ saw 200) :: SigWe gradually reduce the number of arguments in the expression by passing more arguments to the function mlp. The order of arguments is the same for other filters.
Ordinary filters, low, high, band pass and band reject filters:
lp, hp, bp, br :: Sig -> Sig -> Sig -> SigAdd the prefix z for zero-delay filters:
zlp, zhp, zbp, zbr :: Sig -> Sig -> Sig -> SigLadder filters (moog-like and zero delay):
ladder, zladder :: Sig -> Sig -> Sig -> SigButterworth filters:
blp, bhp :: Sig -> Sig -> Sig
blp centerFreq ain = aout
bbp, bbr :: Sig -> Sig -> Sig -> Sig
bbp centerFreq reson ain = aoutExamples:
> dac $ mlp (3500 * uosc 1) 0.1 $ saw 220
> dac $ mlp (3500 * uosc (linseg [1, 2, 4, 1, 2, 0.5, 8, 0.5, 2, 4, 0])) 0.1 $ saw 220Creation of functional tables
Play oscillator with given table:
oscBy :: Tab -> Sig -> SigHarmonic series
sines :: [Double] -> TabHarmonic series with exact frequencies:
type PartialNumber = Double
type PartialStrength = Double
sines2 :: [(PartialNumber, PartialStrength)] -> TabLinear and exponential curves:
lins, exps :: [Double] -> TabSet the table size and add a guard point:
setSize :: Int -> Tab -> Tab
guardPoint :: Tab -> TabSkip normalization:
skipNorm :: Tab -> TabExamples
> dac $ mul (uosc 0.5 * usqr 4) $ oscBy (sines [1, 0.5, 0, 0, 0.25]) 220Midi
Creates audio signal out, instrument definition and user midi input.
midi :: Sigs a => (Msg -> SE a) -> SE aThe Msg is the midi message. We can read amplitude and frequency with ampCps function:
ampCps :: Msg -> (D, D)The useful function onMsg converts a function that takes a frequency signal, a constant, or a pair of amplitude and frequency components to the function that is defined on messages. It often goes hand-in-hand with the function midi:
> vdac $ midi $ onMsg oscWe can add envelopes to remove clicks and pops:
> let synt cps = mul (fades 0.01 0.5) $ osc cps
> vdac $ mul 0.5 $ midi $ onMsg syntReverbs
Reverbs: smallRoom2, smallHall2, largeHall2, magicCave2:
> let x = mul (uosc 0.5 * usqr 4) $ oscBy (sines [1, 0.5, 0, 0, 0.25]) 220
> dac $ mixAt 0.25 largeHall2 x
> let synt = midi $ onMsg $ mul (fades 0.01 0.7) . tri
> vdac $ mul 0.25 $ mixAt 0.25 magicCave2 syntDelays
type MaxDelayTime = D
type Feedback = Sig
type Balance = Sig
echo :: MaxDelayTime -> Feedback -> Sig -> SE Sig
pingPong :: DelayTime -> Feedback -> Balance -> Sig2 -> SE Sig2Example:
> let synt = midi $ onMsg $ mul (fades 0.01 0.7) . tri
> vdac $ mul 0.25 $ mixAt 0.25 largeHall2 $ mixAt 0.65 (echo 0.5 0.8) syntMagic functions
There are certain magic functions that are defined using arguments of many types.
Volume control
We can scale the amplitude of something that produces signals. It can be a single signal or tuples of signals or signals wrapped in the SE,  or produced with a UI-widget.
mul :: Audible a => Sig -> a -> aTransformation of signals
The at function converts something audible with a signal-like function, and mixAt converts using a dry-wet ratio. It is the first argument that ranges from 0 (all dry) to 1 (all wet).
at    :: Audible a => (Sig -> Sig) -> a -> a
mixAt :: Audible a => Sig -> (Sig -> Sig) -> a -> aPatches
Play a patch with midi:
atMidi :: Sigs a => Patch a -> SE aPlay a single note:
atNote :: Sigs a => Patch a -> (D, D) -> SE a
atNote patch (amplitude, frequency) = ...