SF integration complete, pushing.
authorGuerric Chupin <guerric.chupin@ensta-paristech.fr>
Thu, 19 May 2016 15:50:18 +0000 (16:50 +0100)
committerGuerric Chupin <guerric.chupin@ensta-paristech.fr>
Thu, 19 May 2016 15:50:18 +0000 (16:50 +0100)
.gitignore
Reactogon/ClientState.hs
Reactogon/Note.hs
Reactogon/Reactimation.hs
Reactogon/Reactogon.hs
initJack.sh [new file with mode: 0755]
tmp/Melody.hs [deleted file]

index e28092288a3fda31cc4c0085231ac504263e6023..be6af621440454e3bcdcb1688f84d3c8c8c731ff 100644 (file)
@@ -14,3 +14,5 @@ javaReactogon/
 little_things/
 manuals/
 midi_linux.md
+*.eventlog
+tmp/
\ No newline at end of file
index a38e740000563974d5d8c13097c20a8fd80d7fc0..f1b1e0cbf48ab084c8c4761e790fa60ac7c55c45 100644 (file)
@@ -2,7 +2,9 @@ module ClientState where
 
 import Sound.JACK ( NFrames
                   )
+import FRP.Yampa
 
 data ClientState = ClientState { rate :: Int
                                , buffSize :: NFrames
+                               , clientClock :: Time
                                }
index d5b271daec11a4b1f588303288fbbc7fa8cf9b29..0c5433fa1e7f30837319dc6f5e5fcfae049f95b6 100644 (file)
@@ -2,30 +2,31 @@ module Note where
 
 import MIDI
 
-isOn :: Note -> Bool
-isOn (NoteOn _ _ _) = True
-isOn _ = False
+isNoteOn :: Message -> Bool
+isNoteOn (NoteOn _ _ _) = True
+isNoteOn _ = False
 
-isOff :: Note -> Bool
-isOff = not . isOn
+isNoteOff :: Message -> Bool
+isNoteOff (NoteOff _ _ _) = True
+isNoteOff _ = False
 
-changePitch :: (Pitch -> Pitch) -> Note -> Note
+changePitch :: (Pitch -> Pitch) -> Message-> Message
 changePitch f (NoteOn  c p v) = NoteOn  c (f p) v
 changePitch f (NoteOff c p v) = NoteOff c (f p) v
 
-changeVelocity :: (Velocity -> Velocity) -> Note -> Note
+changeVelocity :: (Velocity -> Velocity) -> Message-> Message
 changeVelocity f (NoteOn  c p v) = NoteOn  c p (f v)
 changeVelocity f (NoteOff c p v) = NoteOff c p (f v)
 
-switchOnOff :: Note -> Note
+switchOnOff :: Message-> Message
 switchOnOff (NoteOn  c p v) = NoteOff c p v
 switchOnOff (NoteOff c p v) = NoteOn  c p v
 
-perfectFifth :: Note -> Note
+perfectFifth :: Message-> Message
 perfectFifth = changePitch (toPitch . (+7) . fromPitch)
 
-majorThird :: Note -> Note
+majorThird :: Message-> Message
 majorThird = changePitch (toPitch . (+4) . fromPitch)
 
-minorThird :: Note -> Note
+minorThird :: Message-> Message
 minorThird = changePitch (toPitch . (+3) . fromPitch)
index fbe875df865719536c8e465f92a02b695a8629df..99d816dd04a334bcfade1a78e61854045cb0003d 100644 (file)
@@ -2,32 +2,48 @@
 
 module Reactimation where
 
-import Data.Map (Map)
+import Data.Map ( Map
+                , empty
+                )
 import qualified Data.Map as M
 import FRP.Yampa
 import Control.Concurrent.MVar
+import Sound.JACK ( NFrames(NFrames)
+                  )
 
 import MIDI
-import Arpeggiated
+import ClientState
+--import Arpeggiated
 
-mainReact :: IO ()
-mainReact = reactimate (initialize inRef) (sensing synthRef) actuation mainSF
+mainReact :: MVar EventQueue
+          -> MVar EventQueue
+          -> MVar ClientState
+          -> IO ()
+mainReact inRef outRef clientRef =
+  reactimate (initialize inRef) (sensing clientRef inRef) (actuation outRef) mainSF
 
 initialize :: MVar EventQueue -> IO EventQueue
-initialize = readMVar
+initialize inRef = readMVar inRef
 
-sensing :: MVar (SynthState)
-        -> MVar (Map Time a)
+sensing :: MVar ClientState
+        -> MVar EventQueue
         -> Bool
-        -> IO (DTime, Maybe (Map Time a))
-sensing synthRef inRef _ = do
-  input <- readMVar inref
-  synth <- readMVar synthRef
-  let dt = (fromIntegral $ rate synth)/(fromIntegral $ outBuffSize synth)
+        -> IO (DTime, Maybe EventQueue)
+sensing clientRef inRef _ = do
+  client <- readMVar clientRef
+  input <- takeMVar inRef
+  let (NFrames buff) = buffSize client
+      dt = (fromIntegral $ rate client)/(fromIntegral buff)
   return (dt, Just input)
 
+actuation :: MVar EventQueue
+          -> Bool
+          -> EventQueue
+          -> IO Bool
+actuation outRef _ output = do
+  out <- takeMVar outRef
+  putMVar outRef $ M.union output out
+  return True
 
-actuation = undefined
-
-mainSF :: (Message a) => SF (Map Time a) (Map Time a)
+mainSF :: SF EventQueue EventQueue
 mainSF = identity
index 6f66009efa7885f52b89a6945efe2a5263dc0500..a0eaadc8f786f53aea065b2e45a29173a1256649 100644 (file)
@@ -3,7 +3,7 @@ module Main where
 import Auxiliary
 import MIDI
 import ClientState
---import Reactimation
+import Reactimation
 
 import qualified Sound.JACK as Jack
 import qualified Sound.JACK.MIDI as JMIDI
@@ -61,56 +61,60 @@ main = do
     Jack.withPort client inPortName $ \input -> do
     clientState <- Trans.lift $ newEmptyMVar
     Jack.withProcess client
-      (jackLoop client clientState outState input output) $
+      (jackLoop client clientState inState outState input output) $
       Jack.withActivation client $ do
-      --frpid <- Trans.lift $ forkIO mainReact
+      frpid <- Trans.lift $ forkIO $ mainReact inState outState clientState
       Jack.connect client (reactogonName ++ ":" ++ outPortName) fsPortName
       Trans.lift $ putStrLn $ "Started " ++ reactogonName
       Trans.lift $ Jack.waitForBreak
 
 jackLoop :: Jack.Client
          -> MVar ClientState -- ^ MVar containing the client state (rate and buff size)
+         -> MVar EventQueue -- ^ MVar containing incoming events
          -> MVar EventQueue -- ^ MVar containing exiting events
          -> JMIDI.Port Jack.Input -- ^ Jack input port
          -> JMIDI.Port Jack.Output -- ^ Jack output port
          -> Jack.NFrames -- ^ Buffer size for the ports
          -> Sync.ExceptionalT E.Errno IO ()
-jackLoop client clientState outRef input output nframes@(Jack.NFrames nframesInt) = do
-    rate <- Trans.lift $ Jack.getSampleRate client
-    isEmptyState <- Trans.lift $ isEmptyMVar clientState
-    let updateClient c v = if isEmptyState then putMVar c v else void $ swapMVar c v
-    Trans.lift $ updateClient clientState $ ClientState { rate = rate
-                                             , buffSize = nframes
-                                             }
-    outEvents <- Trans.lift $ takeMVar outRef
-    lframe <- Trans.lift $ Jack.lastFrameTime client
-    inEventsT <- JMIDI.readEventsFromPort input nframes
-    let rateD = fromIntegral rate
-        (Jack.NFrames lframeInt) = lframe
-        currentTime = fromIntegral lframeInt / rateD
-        inEvents :: EventQueue
-        inEvents = M.mapMaybe fromRawMessage $
-          M.fromList $ map (\(Jack.NFrames n,e) -> (currentTime + fromIntegral n/rateD, e)) $
-          EventListAbs.toPairList inEventsT
-        playableEvents = M.filterWithKey
-          (\t _ -> t - currentTime > - fromIntegral nframesInt / rateD) $
-          M.union inEvents outEvents
-        (processableEvents, futureEvents) = breakMap currentTime playableEvents
-        processableEvents' = M.toList processableEvents
-    Trans.lift $ print currentTime
-    Trans.lift $ putMVar outRef futureEvents
-    {-if null processableEvents
-      then Trans.lift $ putStrLn "No events in queue."
-      else Trans.lift $ putStrLn "Event!"-}
-    let smartSub x y = if x < y then y - x else x - y
-        (firstTime,_) = head processableEvents'
-    Trans.lift $ print $
-      map ((* rateD) . smartSub firstTime . fst) processableEvents'
-    JMIDI.writeEventsToPort output nframes $
-      EventListAbs.fromPairList $
-      map (\(t,e) -> (Jack.NFrames $ floor $ rateD * smartSub t currentTime
-                     , toRawMessage e)) $
-      M.toList processableEvents
+jackLoop client clientState inRef outRef
+         input output nframes@(Jack.NFrames nframesInt) = do
+  rate <- Trans.lift $ Jack.getSampleRate client
+  lframe <- Trans.lift $ Jack.lastFrameTime client
+  isEmptyState <- Trans.lift $ isEmptyMVar clientState
+  let updateClient = if isEmptyState
+                     then putMVar
+                     else \c v -> void $ swapMVar c v
+      rateD = fromIntegral rate
+      (Jack.NFrames lframeInt) = lframe
+      currentTime = fromIntegral lframeInt / rateD
+  Trans.lift $ updateClient clientState $ ClientState { rate = rate
+                                                      , buffSize = nframes
+                                                      , clientClock = currentTime
+                                                      }
+  outEvents <- Trans.lift $ takeMVar outRef
+  inEventsT <- JMIDI.readEventsFromPort input nframes
+  let inEvents :: EventQueue
+      inEvents = M.mapMaybe fromRawMessage $
+        M.fromList $
+        map (\(Jack.NFrames n,e) -> (currentTime + fromIntegral n/rateD, e)) $
+        EventListAbs.toPairList inEventsT
+  Trans.lift $ swapMVar inRef inEvents
+  let playableEvents = M.filterWithKey
+        (\t _ -> t - currentTime > - fromIntegral nframesInt / rateD) $
+        M.union inEvents outEvents
+      (processableEvents, futureEvents) = breakMap currentTime playableEvents
+      processableEvents' = M.toList processableEvents
+  Trans.lift $ print currentTime
+  Trans.lift $ putMVar outRef futureEvents
+  let smartSub x y = if x < y then y - x else x - y
+      (firstTime,_) = head processableEvents'
+  Trans.lift $ print $
+    map ((* rateD) . smartSub firstTime . fst) processableEvents'
+  JMIDI.writeEventsToPort output nframes $
+    EventListAbs.fromPairList $
+    map (\(t,e) -> (Jack.NFrames $ floor $ rateD * smartSub t currentTime
+                   , toRawMessage e)) $
+    M.toList processableEvents
 
 {-
     else JMIDI.writeEventsToPort output nframes $
diff --git a/initJack.sh b/initJack.sh
new file mode 100755 (executable)
index 0000000..464310c
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+mkdir -p ~/.log
+
+jackd -d alsa --device hw:0 --rate 44100 --period 1024 &> ~/.log/jack.log &
+
+sleep 5
+
+qjackctl &
+
+fluidsynth -a jack -m jack /usr/share/soundfonts/FluidR3_GM2-2.sf2 \
+    &> ~/.log/fluidsynth.log
diff --git a/tmp/Melody.hs b/tmp/Melody.hs
deleted file mode 100644 (file)
index f792957..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-module Main where
-
-import qualified Sound.JACK.MIDI as JackMidi
-import qualified Sound.JACK as Jack
-import qualified Sound.MIDI.Message as MIDI
-import qualified Sound.MIDI.Message.Channel as Channel
-import qualified Sound.MIDI.Message.Channel.Voice as Voice
-import qualified Sound.MIDI.Message.Class.Construct as MidiCons
-
-import qualified Control.Monad.Exception.Synchronous as Sync
-import qualified Control.Monad.Trans.Class as Trans
-
-import qualified Data.EventList.Absolute.TimeBody as AbsEventList
-import qualified Data.EventList.Relative.TimeBody as EventList
-import qualified Data.EventList.Relative.TimeMixed as EventListTM
-import qualified Numeric.NonNegative.Wrapper as NonNegW
-import Data.IORef ( IORef
-                  , newIORef
-                  , readIORef
-                  , writeIORef
-                  )
-
-import qualified Foreign.C.Error as E
-
-import System.Environment (getProgName)
-
-import Debug.Trace
-
-scale :: [Channel.Pitch]
-scale = map Channel.toPitch [60, 62, 64, 65, 67, 69, 71, 72]
-
--- events :: EventList.T Jack.NFrames MIDI.T
-eventLoop :: EventList.T NonNegW.Double MIDI.T
-eventLoop =
-    EventList.fromPairList $
-    concatMap
-        (\p ->
-            let note on =
-                    MidiCons.note (Channel.toChannel 0)
-                        (Voice.normalVelocity, p, on)
-            in  [(0, note True), (0.1, note False)])
-        scale
-
-{-
-eventList :: Jack.NFrames -> EventList.T Jack.NFrames MIDI.T
-eventList nframes = EventList.fromPairList $
-  zip (map ((mappend nframes) . Jack.NFrames) [22050,44100..] ) $
-  [MIDI.Channel $ Channel.Cons
-    { Channel.messageChannel = Channel.toChannel 0
-    , Channel.messageBody =
-        Channel.Voice $ Voice.NoteOn (Voice.toPitch s) (Voice.toVelocity 100)
-    } | s <- scale]
--}
-
-main :: IO ()
-main = do
-    name <- getProgName
-    stateRef <- newIORef (EventList.cycle eventLoop)
-    Jack.handleExceptions $
-        Jack.withClientDefault name $ \client ->
-        Jack.withPort client "output" $ \output -> do
-        Jack.withProcess client (process client stateRef output) $
-            Jack.withActivation client $ do
-                Jack.connect client "Melody:output" "fluidsynth:midi"
-                Trans.lift $ putStrLn $ "started " ++ name ++ "..."
-                Trans.lift $ Jack.waitForBreak
-
-process ::
-    Jack.Client ->
-    IORef (EventList.T NonNegW.Double MIDI.T) ->
-    JackMidi.Port Jack.Output ->
-    Jack.NFrames ->
-    Sync.ExceptionalT E.Errno IO ()
-process client stateRef output nframes@(Jack.NFrames nframesInt) = do
-    rate <- fmap fromIntegral $ Trans.lift $ Jack.getSampleRate client
-    events <- Trans.lift $ readIORef stateRef
-    let (currentEvents, futureEvents) =
-            EventListTM.splitAtTime (fromIntegral nframesInt / rate) events
-    Trans.lift $ writeIORef stateRef futureEvents
-    JackMidi.writeEventsToPort output nframes $
-        AbsEventList.mapTime (Jack.NFrames . NonNegW.toNumber) $
-        EventList.toAbsoluteEventList 0 $
-        EventList.resample rate $
-        fst $ EventListTM.viewTimeR currentEvents