csound-expression 3.0.1 → 3.1.0
raw patch · 21 files changed
+682/−83 lines, 21 filesdep ~csound-expression-typed
Dependency ranges changed: csound-expression-typed
Files
- csound-expression.cabal +16/−8
- examples/DubBass.hs +48/−0
- examples/Gui/Button.hs +34/−0
- examples/Gui/Envelope.hs +30/−0
- examples/Gui/EnvelopeAndHarmonics.hs +28/−0
- examples/Gui/Filter.hs +38/−0
- examples/Gui/Harmonics.hs +22/−0
- examples/Gui/Key.hs +22/−0
- examples/Gui/Osc.hs +64/−0
- examples/Gui/Waves.hs +23/−0
- examples/Guis.hs +0/−61
- examples/Wind.hs +1/−1
- src/Csound/Air.hs +86/−2
- src/Csound/Base.hs +1/−1
- src/Csound/Control.hs +5/−1
- src/Csound/Control/Evt.hs +23/−6
- src/Csound/Control/Gui.hs +70/−0
- src/Csound/Control/Gui/Layout.hs +18/−0
- src/Csound/Control/Gui/Props.hs +22/−0
- src/Csound/Control/Gui/Widget.hs +100/−0
- src/Csound/Control/Instr.hs +31/−3
csound-expression.cabal view
@@ -1,5 +1,5 @@ Name: csound-expression-Version: 3.0.1+Version: 3.1.0 Cabal-Version: >= 1.6 License: BSD3 License-file: LICENSE@@ -79,8 +79,17 @@ examples/Midi.hs examples/Events.hs- examples/Guis.hs+ examples/DubBass.hs + examples/Gui/Button.hs + examples/Gui/Envelope.hs + examples/Gui/Harmonics.hs + examples/Gui/Osc.hs+ examples/Gui/EnvelopeAndHarmonics.hs + examples/Gui/Filter.hs + examples/Gui/Key.hs + examples/Gui/Waves.hs+ Homepage: https://github.com/anton-k/csound-expression Bug-Reports: https://github.com/anton-k/csound-expression/issues @@ -93,7 +102,7 @@ Ghc-Options: -Wall Build-Depends: base >= 4, base < 5, process, data-default, Boolean >= 0.1.0,- csound-expression-typed, csound-expression-opcodes+ csound-expression-typed >= 0.0.1, csound-expression-opcodes Hs-Source-Dirs: src/ Exposed-Modules: Csound.Base@@ -111,11 +120,10 @@ Csound.Control.Instr Csound.Control.SE ---- Csound.Gui--- Csound.Gui.Widget--- Csound.Gui.Props--- Csound.Gui.Layout+ Csound.Control.Gui+ Csound.Control.Gui.Widget+ Csound.Control.Gui.Props+ Csound.Control.Gui.Layout -- Csound.LowLevel Other-Modules:
+ examples/DubBass.hs view
@@ -0,0 +1,48 @@+module Main where++import Csound++wobbly :: Sig -> Sig -> Sig -> Sig+wobbly spb coeff cps = a2+ where+ a1 = mean [saw (cps * 1.005), sqr (cps * 0.495)]+ idivision = 1 / (coeff * spb)+ klfo = kr $ oscLins [0, 0.5, 1, 0.5, 0] idivision++ -- filter+ ibase = cps+ imod = ibase * 9+ a2 = moogladder a1 (ibase + imod * klfo) 0.6++spb = double dspb+dspb = 0.45++instr (coeff, cps) = return $ wobbly (sig spb) (sig coeff) (sig $ cpspch cps)+++main = dac $ mix $ str (dspb * 2) $ sco instr $ melMap temp $ + [ (2, 6.04)+ , (1/3, 7.04)+ , (2, 6.04)+ , (1/1.5, 7.07)+ + , (2, 5.09)+ , (1, 6.09)+ , (1/1.5, 5.09)+ , (1/3, 6.11)++ , (1, 6.04)+ , (1/3, 7.04)+ , (2, 6.04)+ , (1/1.5, 7.07) + + , (2, 6.09)+ , (1, 7.09)+ , (1/1.5, 6.11)+ , (1/3, 6.07) + + , (2, 6.04)+ , (1/3, 7.04)+ , (2, 6.04)+ , (1/1.5, 7.07) ]+
+ examples/Gui/Button.hs view
@@ -0,0 +1,34 @@+-- | Simple buttons+module Button where++import Csound.Base++main = dac $ do + -- Let's create a toggle button with label "play".+ -- The toggle button emmits ones and zeros.+ (gbut1, evt1) <- toggle "play"++ -- Let's create a plain button with label "Hi!".+ (gbut2, evt2) <- button "Hi!"++ -- It splits the toggle button events on only ones and only zeros.+ let (evtOn, evtOff) = splitToggle evt1++ -- Let's use a standard method to create a master volume slider.+ (gvol, vol) <- masterVolume++ -- A simple instrument that plays a single note+ -- it fades in and out in 0.5 seconds.+ let instr cps _ = return $ fades 0.5 0.5 * osc cps++ -- Creates a window with our elements vertically aligned.+ panel $ ver [gvol, gbut1, gbut2]++ -- Plays a note while toggle button is 'on'.+ let sig1 = schedUntil (instr 440) evtOn evtOff+ -- Plays a note once.+ sig2 = sched (instr 330) $ withDur 0.2 $ fmap (const unit) evt2++ -- Sends the sum to the output scaled with given master volume.+ return $ vol * sum [sig1, sig2]+
+ examples/Gui/Envelope.hs view
@@ -0,0 +1,30 @@+-- | An ADSR-envelope.+module Envelope where++import Csound.Base++-- We use `vdac` to activate the virtual midi keyboard.+-- We can use just `dac` if we have a hardware midi controller.+main = vdac $ do+ -- Creates an Attack-decay-sustain-release widget.+ -- We specify a label, time bounds for attack, decay and release+ -- and init values. It returns a gui-widget and the signal of+ -- the envelope.+ (genv, env) <- linAdsr "" (AdsrBound 1 1 3) (AdsrInit 0.1 0.5 0.5 0.1)+ + -- Creates a master volume widget.+ (gvol, vol) <- masterVolume ++ -- A simple midi-instrument that plays a triangle wave with+ -- given amplitude envelope.+ let instr = onMsg $ \cps -> env * tri cps++ -- Let's place everything on window. Two elements+ -- are aligned vertically and the master volume slider is+ -- smaller than the envelope widget (we scale it by 0.25).+ panel $ ver [genv, sca 0.25 gvol]++ -- Triggers the midi instrument and scales+ -- the output by given master volume.+ return $ vol * midi instr+
+ examples/Gui/EnvelopeAndHarmonics.hs view
@@ -0,0 +1,28 @@+-- | Experiments with amplitude envelope and harmonics.+module EnvelopeAndHarmonics where++import Csound.Base++import Harmonics(harms)++main = vdac $ do+ -- Creates a bank of 15 sliders. We specify the label+ -- and initial values for slider (0 is minimum and 1 is maximum + -- for each slider.+ (gharms, ks) <- sliderBank "harmonics" (1 : replicate 14 0)+ -- We create an Adsr envelope (see example Envelope.hs)+ (gadsr, env) <- linAdsr "amplitude envelope" (AdsrBound 1 1 3) (AdsrInit 0.1 0.1 0.5 0.1)+ -- Creates a master volume slider+ (gvol, vol) <- masterVolume++ -- Places everything on window. Elements are aligned vertically.+ -- The volume slider is smaller than the ADSR-envelope. The element+ -- for harmonics is the biggest one. We alter the sizes with the function `sca`+ -- (it's short for scale)+ panel $ ver [sca 0.1 gvol, sca 0.25 gadsr, gharms]++ -- Let's create a simple instrument with the custom harmonics + -- and amplitude envelope.+ let instr = onMsg $ \cps -> env * harms ks cps+ return $ vol * midi instr+
+ examples/Gui/Filter.hs view
@@ -0,0 +1,38 @@+-- | Development of the example Osc.hs (see it first). +-- Now oscillators are equipped with filter.+module Filter where++import Csound.Base++import Osc(OscInit(..), moogOscs)++data FilterInit = FilterInit + { filterInitResonance :: Double -- the Q+ , filterInitFreq :: Double -- the minimum of the center frequency+ , filterInitRange :: Double } -- center frequency range++-- | A moog like filter with ADSR-envelope for center frequency.+moogFilter :: String -> Sig -> FilterInit -> Source (Sig -> Sig)+moogFilter name env (FilterInit initQ initCfq initRange) = source $ do+ (gq, q) <- knob "resonance" uspan initQ + (gcfq, cfq) <- knob "freq" (linSpan 0 1000) initCfq + (grange, range) <- knob "range" (linSpan 0 10000) initRange+ let cfqSig = cfq + env * range+ gui <- setTitle name $ hor [gq, gcfq, grange]+ return (gui, mlp cfqSig q)++adsrBound = AdsrBound 3 3 5+adsrInit = AdsrInit 0.1 0.1 0.5 0.2++main = vdac $ do+ (gampEnv, ampEnv) <- linAdsr "amplitude" adsrBound adsrInit+ (gfiltEnv, filtEnv) <- linAdsr "filter" adsrBound adsrInit+ (goscs, oscs) <- moogOscs [OscInit 1 1 1, OscInit 0.5 2 2, OscInit 0.125 3 3]+ (gfilt, filt) <- moogFilter "" filtEnv (FilterInit 0.6 100 3000)+ (gvol, vol) <- masterVolume ++ let instr cps = vol * ampEnv * (filt $ oscs cps)+ let gui = hor [goscs, ver [gampEnv, gfiltEnv, gfilt], sca 0.1 gvol]+ panelBy "" (Just $ Rect 50 50 900 800) gui+ return $ midi $ onMsg instr+
+ examples/Gui/Harmonics.hs view
@@ -0,0 +1,22 @@+-- Experiments with harmonics.+module Harmonics where++import Csound.Base ++-- a sum of pure tones that form a hormanic series.+harms :: [Sig] -> Sig -> Sig+harms ks cps = sum $ zipWith f ks (fmap (sig . double) [1 .. ])+ where f k n = k * osc (n * cps)++main = vdac $ do+ -- Let's create a list of coefficients.+ (g, ks) <- sliderBank "Harmonics" (1 : replicate 13 0)+ (gv, v) <- masterVolume+ -- A simple instrument that plays a harmonic series+ -- with the given coefficients.+ let instr = onMsg $ harms ks+ -- Places elements on window with vertical alignment.+ panel $ ver [g, sca 0.1 gv]+ -- Let's trigger the instrument with midi controller.+ return $ vol * midi instr+
+ examples/Gui/Key.hs view
@@ -0,0 +1,22 @@+-- | Keyboard events+module Main where++import Csound.Base++main = dac $ do + -- Creates a window that listens to keyboard events when in focus.+ keyPanel =<< box "hi"+ + -- a pure tone instrument that plays a single frequency+ let instr x = const $ return $ osc x+ -- triggers the instrument with frequency on the give event stream+ -- and holds the note wile second stream is silent.+ asig cps evtOn evtOff = schedUntil (instr cps) evtOn evtOff+ + return $ mean + [ asig 330 (charOn 's') (charOff 's') + , asig 440 (charOn 'a') (charOff 'a') ]+ +-- Due to inner limitation only one keyboard event can be +-- registered at one control cycle. So if you press +-- the two buttons at the same time only one event will be registered.
+ examples/Gui/Osc.hs view
@@ -0,0 +1,64 @@+-- | Simulates a moog-like oscillators.+-- We create a waveform from the sum of the three oscillators.+module Osc where++import Csound.Base+import Control.Monad++-- An oscillator+data OscInit = OscInit + { oscInitVol :: Double -- volume+ , oscInitRange :: Int -- integer scale for the frequency+ , oscInitWave :: Int } -- the waveform identifier+ -- (0 - osc, 1 - triangle, 2 - square, 3 - saw)++-- A single moog oscillator.+-- It takes a title and initial values. Produces the waveform function.+moogOsc1 :: String -> OscInit -> Source (Sig -> Sig)+moogOsc1 name (OscInit initVol initRange initWave) = source $ do+ -- Creates a widget to chose between four classic waveforms+ (gf, f) <- classicWaves "" initWave+ + -- A knob for a volume of the oscillator+ (gvol, vol) <- knob "vol" uspan initVol ++ -- A knob for a range of the oscillator (scaling of the frequency)+ -- It's the number of the harmonic.+ (grange, range) <- knob "range" (linSpan 1 10) (fromIntegral initRange)++ -- Creates a gui element with the given title and horizontal placement.+ gui <- setTitle name $ hor [setMaterial NoPlastic $ gf, gvol, grange]++ -- Creates a waveform+ let instr cps = vol * f (floor' range * cps) + return (gui, instr)++-- A list of moog oscillators.+moogOscs :: [OscInit] -> Source (Sig -> Sig)+moogOscs xs = source $ do+ -- Creates a list of single moog oscillators+ (gfs, fs) <- fmap unzip $ zipWithM + (\n initVal -> moogOsc1 ("osc" ++ show n) initVal) [1 ..] xs ++ -- The final waveform is the sum of all oscillators+ let instr cps = sum $ fmap ($ cps) fs + -- Alligns all elements vertically+ let gui = ver gfs++ -- Creates a source widget.+ return (gui, instr)++main = vdac $ do+ -- Let's create a stack of three oscillators:+ (g, f) <- moogOscs [(OscInit 1 1 0), (OscInit 0.2 1 1), (OscInit 0 1 2)]+ -- Creates a master volume slider.+ (gvol, vol) <- masterVolume + -- Creates an amplitude envelope (see Envelope.hs).+ (genv, env) <- linAdsr "amplitude envelope" (AdsrBound 3 3 5) (AdsrInit 0.1 0.5 0.5 0.5) + + -- Places all elements on window with given title and sizes.+ panelBy "" (Just $ Rect 50 50 600 600) (hor [ver [sca 0.25 genv, g], sca 0.1 gvol])++ -- Triggers the instrument with midi controller.+ return $ midi $ onMsg (mul (vol * env) . f)+
+ examples/Gui/Waves.hs view
@@ -0,0 +1,23 @@+-- | Plays standard waveforms: pure tone, triangle, square and sawtooth.+module Waves where++import Csound.Base++main = vdac $ do+ -- Creates a master volume slider.+ (gvol, vol) <- masterVolume++ -- We use a standard element to experiment with four classic waveforms.+ -- The return value of the widget is a function that takes a frequency+ -- and returns the signal.+ (gw, f) <- classicWaves "waves" 0++ -- Let's create a simple instrument that uses the waveform.+ let instr x = fades 0.1 0.1 * f x++ -- Places elements on window aligned horizontally.+ panel $ hor [sca 0.1 gvol, gw]++ -- Triggers the instrument with midi keyboard.+ return $ mul vol $ midi $ onMsg instr+
− examples/Guis.hs
@@ -1,61 +0,0 @@--- | Test for GUI-elements. Right now this example is a total mess and shouldn't work.--- see you in the next version...-module Main where--import qualified Data.Colour.Names as C--import Control.Applicative--import Csound.Base---unit = ValSpan (ValDiap 0 1) Linear--linenWidget :: Source Sig-linenWidget = do- (g1, r1) <- slider unit 0.5- (g2, r2) <- slider unit 0.5- let out = liftA2 fun r1 r2 - source (ver [g1, g2]) out- where fun a b = linseg [0, a', 1, idur - a' - b', 1, b', 0]- where a' = idur * ir a- b' = idur * ir b --instr :: SE Sig -> D -> SE Sig-instr env cps = do- e <- env- return $ 0.5 * e * osc (sig cps)--pureTone () = 0.7 * osc 440 - -u = fmap (props [SetSliderType Fill] . fst) $ slider unit 0.5-v = fmap fst $ knob unit 0.5-r = fmap fst $ roller unit 0.001 0.01-c1 = fmap fst $ count (ValDiap 0 10) 1 Nothing 2-c2 = fmap fst $ count (ValDiap 0 10) 1 (Just 2) 4-b = fmap (setLabel "push me" . fst) button-bb = fmap fst $ butBank 5 3-tb = fmap (setLabel "i'm the toggle" . fst) toggle -j = fmap fst $ joy unit unit (0.2, 0.2)--d = fmap (setBoxType BorderBox) $ box $ t ++ t ++ t- where t = " Hello everybody, let's look at some cool text in the box "--f = setColors C.green C.magenta--main = dac $ do- g1 <- u- g2 <- u- g3 <- u- g4 <- r- g5 <- c1 - g6 <- c2- g7 <- b- g8 <- bb- g9 <- d- g10 <- j- g11 <- tb-- let g = setTextColor C.azure $ hor [sca 3 $ sca 1.5 $ f $ ver [setLabel "Amplitude" g1, setLabel "Frequency" g2, hor [g5, g6]], setBorder ThinDown $ ver [g7, setTextColor C.azure $ ver [f $ setTextColor C.blue $ setButtonType RoundButton g11], sca 2 space ]]- - panel (ver [sca 4 g, hor [g7, g7]])
examples/Wind.hs view
@@ -68,5 +68,5 @@ ] -- | Let's repeat everything 10 times and add the sustain for 2 seconds per note.-main = totem $ mix $ sustain 2 $ loop 3 windRes+main = dac $ mix $ sustain 2 $ loop 3 windRes
src/Csound/Air.hs view
@@ -6,7 +6,7 @@ osc, oscBy, saw, isaw, pulse, sqr, tri, blosc, -- ** Unipolar- unipolar, bipolar, uosc, uoscBy, usaw, uisaw, upulse, usqr, utri, ublosc,+ unipolar, bipolar, on, uon, uosc, uoscBy, usaw, uisaw, upulse, usqr, utri, ublosc, -- * Envelopes @@ -32,12 +32,21 @@ -- | Applies filter and balances the output by the input signal. lpb, hpb, bpb, brb, blpb, bhpb, bbpb, bbrb, + -- ** Specific filters+ mlp,+ -- * Patterns mean, vibrate, randomPitch, chorus, resons, resonsBy, modes, dryWet, -- ** List functions odds, evens, + -- * Widgets+ AdsrBound(..), AdsrInit(..),+ linAdsr, expAdsr, + classicWaves,+ masterVolume, masterVolumeKnob,+ -- * Other reverbsc1 @@ -47,7 +56,9 @@ import Data.Boolean import Csound.Typed-import Csound.Typed.Opcode+import Csound.Typed.Opcode hiding (display)+import Csound.Typed.Gui+import Csound.Control.Gui(funnyRadio) import Csound.Tab(sine) @@ -104,6 +115,20 @@ ublosc :: Tab -> Sig -> Sig ublosc tb = unipolar . blosc tb +-- rescaling++-- | Rescaling of the bipolar signal (-1, 1) -> (a, b)+-- +-- > on a b biSig+on :: Sig -> Sig -> Sig -> Sig+on a b x = uon a b $ unipolar x ++-- | Rescaling of the unipolar signal (0, 1) -> (a, b)+-- +-- > on a b uniSig+uon :: Sig -> Sig -> Sig -> Sig+uon a b x = a + (b - a) * x+ -------------------------------------------------------------------------- -- envelopes @@ -284,6 +309,12 @@ bbrb :: Sig -> Sig -> Sig -> Sig bbrb = balance2 bbr +-- | Moog's low-pass filter.+--+-- > mlp centerFrequency qResonance signal+mlp :: Sig -> Sig -> Sig -> Sig+mlp cf q asig = moogladder asig cf q+ -------------------------------------------------------------------------- -- patterns @@ -441,4 +472,57 @@ reverbsc1 :: Sig -> Sig -> Sig -> Sig reverbsc1 x k co = 0.5 * (a + b) where (a, b) = ar2 $ reverbsc x x k co+++----------------------------------------------------------------------+-- Widgets++data AdsrBound = AdsrBound+ { attBound :: Double+ , decBound :: Double+ , relBound :: Double }++data AdsrInit = AdsrInit+ { attInit :: Double+ , decInit :: Double+ , susInit :: Double+ , relInit :: Double }++expEps :: Fractional a => a+expEps = 0.00001++linAdsr :: String -> AdsrBound -> AdsrInit -> Source Sig+linAdsr = genAdsr $ \a d s r -> linsegr [0, a, 1, d, s] r 0++expAdsr :: String -> AdsrBound -> AdsrInit -> Source Sig+expAdsr = genAdsr $ \a d s r -> expsegr [double expEps, a, 1, d, s] r (double expEps)++genAdsr :: (D -> D -> D -> D -> Sig)+ -> String -> AdsrBound -> AdsrInit -> Source Sig+genAdsr mkAdsr name b inits = source $ do+ (gatt, att) <- knob "A" (linSpan expEps $ attBound b) (attInit inits)+ (gdec, dec) <- knob "D" (linSpan expEps $ decBound b) (decInit inits)+ (gsus, sus) <- knob "S" (linSpan expEps 1) (susInit inits) + (grel, rel) <- knob "R" (linSpan expEps $ relBound b) (relInit inits)+ let val = mkAdsr (ir att) (ir dec) (ir sus) (ir rel)+ gui <- setTitle name $ hor [gatt, gdec, gsus, grel]+ return (gui, val)++-- | A widget with four standard waveforms: pure tone, triangle, square and sawtooth.+-- The last parameter is a default waveform (it's set at init time).+classicWaves :: String -> Int -> Source (Sig -> Sig)+classicWaves name initVal = funnyRadio name + [ ("osc", osc)+ , ("tri", tri)+ , ("sqr", sqr)+ , ("saw", saw)]+ initVal++-- | Slider for master volume+masterVolume :: Source Sig+masterVolume = slider "master" uspan 0.5++-- | Knob for master volume+masterVolumeKnob :: Source Sig+masterVolumeKnob = knob "master" uspan 0.5
src/Csound/Base.hs view
@@ -35,5 +35,5 @@ import Data.Default import Data.Monoid -import Csound.Typed.Opcode+import Csound.Typed.Opcode hiding (button, display, space)
src/Csound/Control.hs view
@@ -9,11 +9,15 @@ module Csound.Control.Evt, -- * Instruments -- | Invoking the instruments- module Csound.Control.Instr+ module Csound.Control.Instr,+ -- * Gui+ -- | Interactive controllers+ module Csound.Control.Gui ) where import Csound.Control.SE import Csound.Control.Evt import Csound.Control.Instr+import Csound.Control.Gui
src/Csound/Control/Evt.hs view
@@ -5,13 +5,14 @@ -- * Core functions boolToEvt, evtToBool, sigToEvt, stepper, filterE, filterSE, accumSE, accumE, filterAccumE, filterAccumSE,- Snap, snapshot, snaps,++ Snap, snapshot, snaps, sync, syncBpm, -- * Opcodes metroE, changedE, triggerE, -- * Higher-level event functions- cycleE, iterateE, repeatE, appendE, mappendE,+ cycleE, iterateE, repeatE, appendE, mappendE, partitionE, splitToggle, oneOf, freqOf, freqAccum, randDs, randInts, randSkip, randSkipBy, range, listAt, @@ -19,23 +20,39 @@ ) where import Data.Monoid-+import Data.Default import Data.Boolean+import Data.Tuple import Csound.Typed import Csound.Typed.Opcode -- | Behaves like 'Csound.Opcode.Basic.metro', but returns an event stream.-metroE :: Sig -> Evt ()+metroE :: Sig -> Evt Unit metroE = sigToEvt . metro -- | Behaves like 'Csound.Opcode.Basic.changed', but returns an event stream.-changedE :: [Sig] -> Evt ()+changedE :: [Sig] -> Evt Unit changedE = sigToEvt . changed -- | Behaves like 'Csound.Opcode.Basic.trigger', but returns an event stream.-triggerE :: Sig -> Sig -> Sig -> Evt ()+triggerE :: Sig -> Sig -> Sig -> Evt Unit triggerE a1 a2 a3 = sigToEvt $ trigger a1 a2 a3++-- | the sync function but time is measured in beats per minute.+syncBpm :: (Default a, Tuple a) => D -> Evt a -> Evt a+syncBpm dt = sync (dt / 60)++-- | Splits event stream on two streams with predicate.+partitionE :: (a -> BoolD) -> Evt a -> (Evt a, Evt a)+partitionE p evts = (a, b)+ where + a = filterE p evts+ b = filterE (notB . p) evts++-- | Splits a toggle event stream on on-events and off-events.+splitToggle :: Evt D -> (Evt D, Evt D)+splitToggle = swap . partitionE (==* 0) ---------------------------------------------------------------------- -- higher level evt-funs
+ src/Csound/Control/Gui.hs view
@@ -0,0 +1,70 @@+-- | GUI (Graphical User Interface) elements are handy to change +-- the parameters of the sound in real time. It includes sliders, +-- knobs, rollers, buttons and other widgets. +--+-- A GUI element consists of two parts. They are view (how it looks)+-- and logic (what's going on with it). For example a slider can be+-- horizontal or vertical or green or yellow or small or big. It's the view +-- of the slider. And a slider can produce a continuous signal within the +-- given interval. It's a logic of the slider. +--+-- Let's talk about the view. The view is divided on two parts:+--+-- * where element is placed or Layout. +--+-- * all other properties or just Properties. +--+-- The layout is defined with very simple functions. There are vertical and horizontal grouping +-- of the elements. We can scale the element within the group and include an empty+-- space in the group. Everything is aligned (see "Csound.Gui.Layout"). +-- Other properties include colors, fonts (size and type), borders, specific properties +-- of the widgets (see "Csound.Gui.Props").+--+-- Let's consider the logic. The logic consists of three parts:+--+-- * what is consumed ('Csound.Gui.Output')+--+-- * what is produced ('Csound.Gui.Input')+--+-- * what's going on inside ('Csound.Gui.Inner')+--+-- A widget can react on values, produce values or do something useful. +-- There are special types of widgets:+--+-- * 'Csound.Gui.Source' - they produce values only+--+-- * 'Csound.Gui.Sink' - they consume values only+--+-- * 'Csound.Gui.Display' - something is going on inside them (for example, it can show a "hello world" message)+-- +--+-- Widgets can be simple and compound. Simple widgets are primitive elements+-- (sliders, knobs, rollers, buttons). We have a special constructors that +-- produce simple widgets (see "Csound.Gui.Widget"). Compound widgets glue together+-- several widgets. That is the view contains several elements and all of them +-- involved in the logic of the widget.+--+--+module Csound.Control.Gui (+ -- * Gui+ Gui, + Widget, Input, Output, Inner,+ Sink, Source, Display,+ widget, sink, source, display,+ + -- * Panels+ panel, panels, panelBy,+ keyPanel, keyPanels, keyPanelBy,++ -- * Re-exports+ module Csound.Control.Gui.Layout,+ module Csound.Control.Gui.Props,+ module Csound.Control.Gui.Widget+) where++import Csound.Typed.Gui++import Csound.Control.Gui.Layout+import Csound.Control.Gui.Props+import Csound.Control.Gui.Widget+
+ src/Csound/Control/Gui/Layout.hs view
@@ -0,0 +1,18 @@+-- | The functions from this module specify the geometry +-- of the GUI-elements. They tell where to render the elements.+--+-- Every element is rectangular. To know where to place the element is +-- to know the parameters of the bounding rectangle. All rectangles are+-- relative and automatically aligned. +--+-- We have two functions for grouping. They construct horizontal and vertical+-- groups of the elements. Within the group we can change the relative size +-- of the rectangles (by scaling one side of the rectangle). In place of rectangle+-- we can put an empty space. +module Csound.Control.Gui.Layout (+ hor, ver, space, sca, horSca, verSca, + padding, margin+) where++import Csound.Typed.Gui+
+ src/Csound/Control/Gui/Props.hs view
@@ -0,0 +1,22 @@+-- | Properties that specify the appearance of the GUI elements. +-- The specification is inspired by CSS. All properties+-- are set in the cascade manner. For example, if you want to change the font type+-- for all elements you should set this property only for the top-most GUI element.+-- If the property is set on the lower level it wins versus property that is set on the +-- higher level. +module Csound.Control.Gui.Props (+ -- * Properties+ props, forceProps,+ Prop(..), BorderType(..), Color,+ Rect(..), FontType(..), Emphasis(..), + Material(..), Orient(..), ++ -- * Setters+ -- | Handy short-cuts for the function @props@.+ setBorder, setLabel, setMaterial, + setColor1, setColor2, setColors, setTextColor,+ setFontSize, setFontType, setEmphasis, setOrient+) where++import Csound.Typed.Gui+
+ src/Csound/Control/Gui/Widget.hs view
@@ -0,0 +1,100 @@+-- | Primitive GUI elements.+--+-- There is a convention that constructors take only parameters that +-- specify the logic of the widget. The view is set for GUI-elements with +-- other functions.+module Csound.Control.Gui.Widget (+ -- * Common properties + ValDiap(..), ValStep, ValScaleType(..), ValSpan(..),+ linSpan, expSpan, uspan, bspan, uspanExp,+ -- * Valuators+ count, countSig, joy, + knob, KnobType(..), setKnobType,+ roller, + slider, sliderBank, SliderType(..), setSliderType,+ numeric, TextType(..), setTextType,++ -- * Other widgets+ box, BoxType(..), setBoxType,+ button, ButtonType(..), setButtonType, + toggle, butBank, toggleSig, butBankSig,+ butBank1, butBankSig1, + radioButton, matrixButton, funnyRadio, funnyMatrix,+ value, meter,+ -- * Transformers+ setTitle,+ -- * Keyboard+ KeyEvt(..), Key(..), keyIn, charOn, charOff+) where++import Data.List(transpose)+import Data.Boolean++import Csound.Typed.Gui+import Csound.Typed.Types+import Csound.Control.Evt(listAt)++--------------------------------------------------------------------+-- aux widgets++readMatrix :: Int -> Int -> [a] -> [a]+readMatrix xn yn as = transp $ take (xn * yn) $ as ++ repeat (head as)+ where + transp xs = concat $ transpose $ parts yn xn xs+ parts x y qs + | x == 0 = []+ | otherwise = a : parts (x - 1) y b+ where (a, b) = splitAt y qs++-- | A radio button. It takes a list of values with labels.+radioButton :: Arg a => String -> [(String, a)] -> Int -> Source (Evt a)+radioButton title as initVal = source $ do+ (g, ind) <- butBank1 "" 1 (length as) (0, initVal)+ gnames <- mapM box names+ let val = listAt vals ind + gui <- setTitle title $ padding 0 $ hor [sca 0.15 g, ver gnames]+ return (gui, val)+ where (names, vals) = unzip as++-- | A matrix of values.+matrixButton :: Arg a => String -> Int -> Int -> [a] -> (Int, Int) -> Source (Evt a)+matrixButton name xn yn vals initVal = source $ do+ (gui, ind) <- butBank1 name xn yn initVal+ let val = listAt allVals ind+ return (gui, val)+ where allVals = readMatrix xn yn vals++-- | Radio button that returns functions. Useful for picking a waveform or type of filter.+funnyRadio :: Tuple b => String -> [(String, a -> b)] -> Int -> Source (a -> b)+funnyRadio name as initVal = source $ do+ (gui, ind) <- radioButton name (zip names (fmap int [0 ..])) initVal+ contInd <- stepper (sig $ int initVal) $ fmap sig ind+ let instr x = guardedTuple (+ zipWith (\n f -> (contInd ==* (sig $ int n), f x)) [0 ..] funs+ ) (head funs x)+ return (gui, instr)+ where (names, funs) = unzip as++-- | Matrix of functional values.+funnyMatrix :: Tuple b => String -> Int -> Int -> [(a -> b)] -> (Int, Int) -> Source (a -> b)+funnyMatrix name xn yn funs initVal@(x0, y0) = source $ do+ (gui, ind) <- butBank1 name xn yn initVal+ contInd <- stepper flattenInitVal $ fmap sig ind+ let instr x = guardedTuple (+ zipWith (\n f -> (contInd ==* (sig $ int n), f x)) [0 ..] allFuns+ ) (head allFuns x)+ return (gui, instr)+ where + allFuns = readMatrix xn yn funs+ flattenInitVal = sig $ int $ y0 + x0 * yn+++-- | Shortcut for press 'CharKey' events.+charOn :: Char -> Evt Unit+charOn = keyIn . Press . CharKey++-- | Shortcut for release 'CharKey' events.+charOff :: Char -> Evt Unit+charOff = keyIn . Release . CharKey++
src/Csound/Control/Instr.hs view
@@ -83,8 +83,8 @@ cpsmidi, ampmidi, -- * Evt - trig, sched, schedHarp,- trig_, sched_, + trig, sched, schedHarp, schedUntil, schedToggle,+ trig_, sched_, schedUntil_, trigBy, schedBy, schedHarpBy, withDur, @@ -97,7 +97,7 @@ import Csound.Typed.Opcode import Csound.Control.Overload -import Csound.Control.Evt(metroE, repeatE)+import Csound.Control.Evt(metroE, repeatE, splitToggle) -------------------------------------------------------------------------- -- midi@@ -129,4 +129,32 @@ dur = double $ csdEventListDur notes instr _ = mix_ notes+++-- | Invokes an instrument with first event stream and +-- holds the note until the second event stream is active.+schedUntil :: (Arg a, Sigs b) => (a -> SE b) -> Evt a -> Evt c -> b+schedUntil instr onEvt offEvt = sched instr' $ withDur (-1) onEvt+ where + instr' x = do + res <- instr x+ runEvt offEvt $ const $ turnoff+ return res++-- | Invokes an instrument with toggle event stream (1 stands for on and 0 stands for off).+schedToggle :: (Sigs b) => SE b -> Evt D -> b+schedToggle res evt = schedUntil instr on off+ where + instr = const res+ (on, off) = splitToggle evt++-- | Invokes an instrument with first event stream and +-- holds the note until the second event stream is active.+schedUntil_ :: (Arg a) => (a -> SE ()) -> Evt a -> Evt c -> SE ()+schedUntil_ instr onEvt offEvt = sched_ instr' $ withDur (-1) onEvt+ where + instr' x = do + res <- instr x+ runEvt offEvt $ const $ turnoff+ return res