module Synthesizer.MIDI.CausalIO.ControllerSet (
T,
fromChannel,
slice, PCS.Controller(..),
controllerLinear,
controllerExponential,
pitchBend,
channelPressure,
bendWheelPressure,
) where
import qualified Synthesizer.CausalIO.Process as PIO
import qualified Synthesizer.MIDI.CausalIO.Process as MIO
import qualified Synthesizer.MIDI.PiecewiseConstant.ControllerSet as PCS
import qualified Synthesizer.MIDI.EventList as MIDIEv
import qualified Synthesizer.MIDI.Value.BendModulation as BM
import qualified Synthesizer.MIDI.Value.BendWheelPressure as BWP
import qualified Synthesizer.MIDI.Value as MV
import qualified Synthesizer.PiecewiseConstant.Signal as PC
import qualified Sound.MIDI.Message.Channel.Voice as VoiceMsg
import qualified Sound.MIDI.Message.Class.Check as Check
import qualified Data.EventList.Relative.TimeTime as EventListTT
import qualified Data.EventList.Relative.BodyTime as EventListBT
import qualified Data.EventList.Relative.MixedTime as EventListMT
import qualified Algebra.Transcendental as Trans
import qualified Algebra.Field as Field
import qualified Algebra.RealRing as RealRing
import qualified Control.Monad.Trans.State as MS
import qualified Data.Accessor.Basic as Acc
import qualified Data.Map as Map
import Data.Traversable (traverse, )
import Data.Foldable (traverse_, )
import Control.Arrow (Arrow, arr, )
import Control.Category ((.), )
import qualified Data.Maybe as Maybe
import Data.Maybe.HT (toMaybe, )
import NumericPrelude.Numeric
import NumericPrelude.Base hiding ((.), )
import Prelude ()
mapInsertMany ::
(Ord key) =>
[(key,a)] -> Map.Map key a -> Map.Map key a
mapInsertMany assignments inits =
foldl (flip (uncurry Map.insert)) inits assignments
fromChannel ::
(Check.C event) =>
MIDIEv.Channel ->
PIO.T
(EventListTT.T MIDIEv.StrictTime [event])
(PCS.T PCS.Controller Int)
fromChannel chan =
(PIO.traverse Map.empty $ \evs0 -> do
initial <- MS.get
fmap (PCS.Cons initial) $
traverse (\ys -> MS.modify (mapInsertMany ys) >> return ys) evs0)
.
MIO.mapMaybe (PCS.maybeController chan)
type T arrow y =
arrow
(PCS.T PCS.Controller Int)
(EventListBT.T PC.ShortStrictTime y)
slice ::
(Arrow arrow) =>
PCS.Controller ->
(Int -> y) ->
y ->
T arrow y
slice c f deflt =
arr $ \(PCS.Cons initial stream) ->
let yin = maybe deflt f $ Map.lookup c initial
in PC.subdivideLongStrict $
EventListMT.consBody yin $
flip MS.evalState yin $
traverse
(\ys -> traverse_ (MS.put . f) ys >> MS.get) $
fmap
(Maybe.mapMaybe
(\(ci,a) -> toMaybe (c==ci) a))
stream
controllerLinear ::
(Field.C y, Arrow arrow) =>
MIDIEv.Controller ->
(y,y) -> y ->
T arrow y
controllerLinear ctrl bnd initial =
slice (PCS.Controller ctrl) (MV.controllerLinear bnd) initial
controllerExponential ::
(Trans.C y, Arrow arrow) =>
MIDIEv.Controller ->
(y,y) -> y ->
T arrow y
controllerExponential ctrl bnd initial =
slice (PCS.Controller ctrl) (MV.controllerExponential bnd) initial
pitchBend ::
(Trans.C y, Arrow arrow) =>
y -> y ->
T arrow y
pitchBend range center =
slice PCS.PitchBend (MV.pitchBend range center) center
channelPressure ::
(Trans.C y, Arrow arrow) =>
y -> y ->
T arrow y
channelPressure maxVal initial =
slice PCS.Pressure (MV.controllerLinear (zero,maxVal)) initial
bendWheelPressure ::
(RealRing.C y, Trans.C y, Arrow arrow) =>
Int -> y -> y ->
T arrow (BM.T y)
bendWheelPressure pitchRange wheelDepth pressDepth =
arr $ \(PCS.Cons initial stream) ->
let set key field =
maybe id (Acc.set field) $
Map.lookup key initial
yin =
set PCS.PitchBend BWP.bend $
set (PCS.Controller VoiceMsg.modulation) BWP.wheel $
set PCS.Pressure BWP.pressure $
BWP.deflt
in PC.subdivideLongStrict $
fmap (BM.fromBendWheelPressure pitchRange wheelDepth pressDepth) $
EventListMT.consBody yin $
flip MS.evalState yin $
traverse (\ys0 -> traverse_ MS.put ys0 >> MS.get) $
fmap Maybe.catMaybes $
flip MS.evalState BWP.deflt $
traverse (traverse PCS.checkBendWheelPressure) stream