packages feed

essence-of-live-coding-pulse 0.2.0.1 → 0.2.1

raw patch · 3 files changed

+88/−62 lines, 3 filesdep ~essence-of-live-coding

Dependency ranges changed: essence-of-live-coding

Files

essence-of-live-coding-pulse.cabal view
@@ -1,5 +1,5 @@ name:                essence-of-live-coding-pulse-version:             0.2.0.1+version:             0.2.1 synopsis: General purpose live coding framework - pulse backend description:   essence-of-live-coding is a general purpose and type safe live coding framework.@@ -28,10 +28,9 @@ library   exposed-modules:     LiveCoding.Pulse-    LiveCoding.Pulse.GHCi   build-depends:       base >= 4.11 && < 5-    , essence-of-live-coding >= 0.2.0.1+    , essence-of-live-coding >= 0.2.1     , transformers >= 0.5     , pulse-simple >= 0.1     , foreign-store >= 0.2
src/LiveCoding/Pulse.hs view
@@ -6,9 +6,12 @@ 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@@ -16,63 +19,110 @@ -- essence-of-live-coding import LiveCoding -type PulseCell = Cell IO () Float+type PulseT m = WriterT (Sum Float) m -playPulseCell :: PulseCell -> IO (MVar PulseCell)-playPulseCell pulseCell = do-  var <- newMVar pulseCell-  pulseClient <- simpleNew-    Nothing-    "example"-    Play-    Nothing-    "this is an example application"-    (SampleSpec (F32 LittleEndian) 44100 1)-    Nothing-    Nothing-  forkIO $ forever $ do-    cell <- takeMVar var-    (samples, cell') <- steps cell $ replicate 1024 ()-    simpleWrite pulseClient samples-    putMVar var cell'-  return var+type PulseCell m a b = Cell (PulseT m) a b --- TODO Generalisable-updatePulse :: MVar PulseCell -> PulseCell -> IO ()-updatePulse var newCell = do-  oldCell <- takeMVar var-  putMVar var $ hotCodeSwapCell newCell oldCell+-- | Compose with this cell to play a sound sample.+addSample :: Monad m => PulseCell m Float ()+addSample = arr Sum >>> arrM tell --- Returns the sum between -1 and 1+-- | 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+        (_, 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+ 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 -osc :: (Data a, RealFloat a, MonadFix m) => Cell (ReaderT a m) () 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 <- wrapSum -< f / 44100+  phase <- wrapIntegral -< f   returnA -< sin $ 2 * pi * phase -osc' :: (Data a, RealFloat a, MonadFix m) => Cell m a a+-- | 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@@ -88,9 +138,15 @@   | 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)
− src/LiveCoding/Pulse/GHCi.hs
@@ -1,29 +0,0 @@-module LiveCoding.Pulse.GHCi where---- base-import Control.Concurrent---- foreign-store-import Foreign.Store---- essence-of-live-coding-import LiveCoding.LiveProgram---- essence-of-live-coding-pulse-import LiveCoding.Pulse--livepulse "" = livepulse "pulseCell"-livepulse pulseCell = return $ unlines-  [ "var <- playPulseCell " ++ pulseCell-  , "savePulse var"-  ]-livereloadpulse "" = livereloadpulse "pulseCell"-livereloadpulse pulseCell  = return $ unlines-  [ ":reload"-  , "var <- loadPulse"-  , "updatePulse var " ++ pulseCell-  ]-loadPulse :: IO (MVar PulseCell)-loadPulse = readStore (Store 0)-savePulse :: MVar PulseCell -> IO ()-savePulse var = writeStore (Store 0) var