YampaSynth-0.0.1: src/MidiSynth.hs
{-# LANGUAGE Arrows #-}
module MidiSynth (
midiSynth
, midiToEventSource
) where
import qualified SynthParams as SP
import SynthBasics
import Data.Audio
import Codec.Midi hiding (Time)
import FRP.Yampa
import qualified Data.Map as Map
type MidiSynth = SF (Event [Message]) (Sample, Event ())
type Col a = Map.Map (Channel,Key) a
eventToList :: Event [a] -> [a]
eventToList me = if isEvent me then fromEvent me else []
synthParamsToMonoSynth :: SP.SynthParams -> MidiSynth
synthParamsToMonoSynth params = proc me -> do
e <- iEdge False -< any msgPred (eventToList me)
s <- oscSampleBased params -< (0,e)
(cv,end) <- envGen 0 adsr (Just 2) -< e
sample <- amp 0.125 (SP.velocity params) -< (s,cv)
-- cv <- oscSine 1.5 >>> arr( + 1) -< 0
-- s <- moogVCF 48000 (19000) 0.0 -< (s,0)
returnA -< (sample,end)
where
adsr = [(0.001,1.0),(0.001,0.80),(0.24,0.0)]
msgPred msg = (isNoteOff msg || isNoteOn msg && velocity msg == 0)
&& (key msg == SP.key params)
&& (channel msg == SP.channel params)
midiToEventSource :: Midi -> SF a (Event [Message])
midiToEventSource m = afterEachCat
$! toRealTime (timeDiv m')
$! head
$! tracks m'
where
m' = toSingleTrack m
data SynthState = SynthState {
channelState :: Map.Map Channel (Bank,Preset)
-- , synthParams :: Map.Map (Bank, Preset, Key, Channel, Velocity)
-- SP.SynthParams
}
defaultSynthState :: SynthState
defaultSynthState = SynthState {
channelState = Map.singleton 9 (128,0)
-- , synthParams = Map.empty
}
getChannelState :: Channel -> SynthState -> (Bank,Preset)
getChannelState c s = Map.findWithDefault (0,0) c (channelState s)
updateSynthState :: Message -> SynthState -> SynthState
updateSynthState (ProgramChange c p) s = s {channelState =
Map.insertWith (\_ (b,_) -> (b,p)) c (0,p) (channelState s)}
updateSynthState (ControlChange c 0 b) s = s {channelState = -- Bank Select
Map.insertWith (\_ (_,p) -> (b,p)) c (b,0) (channelState s)}
updateSynthState _ s = s
triggerChange :: SynthState ->
SF (Event [Message], Col (Sample, Event ()))
(Event ([(Channel, Key, Velocity)], [(Channel,Key)], SynthState))
triggerChange initState = proc (me, ses) -> do
let msgList = eventToList me
onMsgs = filter (\msg -> isNoteOn msg && velocity msg > 0) msgList
ons = map (\m -> (channel m, key m, velocity m)) onMsgs
newState = foldr updateSynthState initState msgList
offs = Map.keys $ Map.filter (isEvent . snd) ses
e <- iEdge False -< not (null ons)
|| not (null offs)
|| (channelState newState /= channelState initState)
returnA -< tag e (ons,offs,newState)
performChange ::
SP.SynthParamsGen
-> Col MidiSynth
-> ([(Channel, Key, Velocity)], [(Channel,Key)], SynthState)
-> SF (Event [Message]) (Col (Sample, Event ()))
performChange paramsGen sfs (ons,offs,synthState) =
pSwitchB sfs2 (noEvent --> triggerChange synthState) (performChange paramsGen)
where
sfs1 = foldr (Map.delete) sfs offs
sfs2 = foldr (\(c,k,v) col -> insertSynth c k v col) sfs1 ons
cmbSynths :: MidiSynth -> MidiSynth -> MidiSynth
cmbSynths newSF oldSF = noEvent >-- proc midiEvent -> do
s1 <- switch sf (\_ -> constant 0) -< ()
(s2,e2) <- newSF -< midiEvent
returnA -< (s1 + s2, e2)
where
sf = proc () -> do
(cv,e) <- envGen 1.0 [(0.001, 0.0)] Nothing -< noEvent
(s,_) <- oldSF -< noEvent
returnA -< (s * cv,e)
insertSynth :: Channel -> Key -> Velocity -> Col MidiSynth -> Col MidiSynth
insertSynth c k v col = case paramsGen b p c k v of
(Just params) -> Map.insertWith (cmbSynths) (c,k)
(synthParamsToMonoSynth params) col
Nothing -> col
where (b,p) = getChannelState c synthState
midiSynth :: SP.SynthParamsGen -> MidiSynth
midiSynth paramsGen = proc me -> do
ses <- pSwitchB Map.empty (triggerChange defaultSynthState)
(performChange paramsGen) -< me
end <- iEdge False -< any isTrackEnd (eventToList me)
-- end <- after 15.0 () -< ()
-- smpl <- mixer -< Map.elems $ Map.map fst ses
let smpl = Map.fold (\(s,_) s' -> s + s') 0 ses
returnA -< (smpl, end)