{-# LANGUAGE Arrows #-} module LiveCoding.Pulse where -- base import Control.Arrow as X import Control.Concurrent import Control.Monad (forever) import Control.Monad.Fix import Data.Monoid (getSum, Sum(Sum)) -- transformers import Control.Monad.Trans.Class (MonadTrans(lift)) import Control.Monad.Trans.Reader import Control.Monad.Trans.Writer.Strict -- pulse-simple import Sound.Pulse.Simple -- essence-of-live-coding import LiveCoding type PulseT m = WriterT (Sum Float) m type PulseCell m a b = Cell (PulseT m) a b -- | Compose with this cell to play a sound sample. addSample :: Monad m => PulseCell m Float () addSample = arr Sum >>> arrM tell -- | Globally fix the sample rate to 48000 samples per second. sampleRate :: Num a => a sampleRate = 48000 {- | Create a pulse server backend handle. Currently, this is always mono, but with a future release of @pulse-simple@, this might be configurable. -} pulseHandle :: Handle IO Simple pulseHandle = Handle { create = simpleNew Nothing "example" Play Nothing "this is an example application" (SampleSpec (F32 LittleEndian) sampleRate 1) Nothing Nothing , destroy = simpleFree } {- | Run a 'PulseCell' with a started pulse backend. Currently, this is synchronous and blocking, i.e. the resulting cell will block until the backend buffer is nearly empty. This performs several steps of your cell at a time, replicating the input so many times. -} pulseWrapC :: Int -- ^ Specifies how many steps of your 'PulseCell' should be performed in one step of 'pulseWrapC'. -> PulseCell IO a b -- ^ Your cell that produces samples. -> Cell (HandlingStateT IO) a [b] pulseWrapC bufferSize cell = proc a -> do simple <- handling pulseHandle -< () samplesAndBs <- resampleList $ liftCell $ runWriterC cell -< replicate bufferSize a let (samples, bs) = unzip samplesAndBs samples' = getSum <$> samples arrM $ lift . uncurry simpleWrite -< samples' `seq` bs `seq` (simple, samples') returnA -< bs {- | Returns the sum of all incoming values, and wraps it between -1 and 1. This is to prevent floating number imprecision when the sum gets too large. -} wrapSum :: (Monad m, Data a, RealFloat a) => Cell m a a wrapSum = Cell { cellState = 0 , cellStep = \accum a -> let (_, accum') = properFraction $ accum + a in return (accum', accum') } -- | Like 'wrapSum', but as an integral, assuming the PulseAudio 'sampleRate'. wrapIntegral :: (Monad m, Data a, RealFloat a) => Cell m a a wrapIntegral = arr (/ sampleRate) >>> wrapSum -- | A sawtooth, or triangle wave, generator, -- outputting a sawtooth wave with the given input as frequency. sawtooth :: (Monad m, Data a, RealFloat a) => Cell m a a sawtooth = wrapIntegral modSum :: (Monad m, Data a, Integral a) => a -> Cell m a a modSum denominator = Cell { cellState = 0 , cellStep = \accum a -> let accum' = (accum + a) `mod` denominator in return (accum', accum') } clamp :: (Ord a, Num a) => a -> a -> a -> a clamp lower upper a = min upper $ max lower a -- | A sine oscillator. -- Supply the frequency via the 'ReaderT' environment. -- See 'osc'' and 'oscAt'. osc :: (Data a, RealFloat a, Monad m) => Cell (ReaderT a m) () a osc = proc _ -> do f <- constM ask -< () phase <- wrapIntegral -< f returnA -< sin $ 2 * pi * phase -- | A sine oscillator, at a fixed frequency. oscAt :: (Data a, RealFloat a, Monad m) => a -> Cell m () a oscAt = flip runReaderC osc -- | A sine oscillator, at a frequency that can be specified live. osc' :: (Data a, RealFloat a, Monad m) => Cell m a a osc' = proc a -> do runReaderC' osc -< (a, ()) {- | A basic musical note (western traditional notation, german nomenclature). Assumes equal temperament and removes enharmonic equivalents, i.e. there is only Dis (= D sharp) but not Eb (= E flat). -} data Note = A | Bb | B | C | Cis | D | Dis | E | F | Fis | G | Gis deriving (Enum, Show) -- | Calculate the frequency of a note, -- with 'A' corresponding to 220 Hz. f :: Note -> Float f note = 220 * (2 ** (fromIntegral (fromEnum note) / 12)) -- | Transpose a frequency an octave higher, i.e. multiply by 2. o :: Float -> Float o = (* 2) -- | Transpose a frequency an octave lower, i.e. divide by 2. oB :: Float -> Float oB = (/ 2)