tidal-midi-0.9: Sound/Tidal/MIDI/Stream.hs
{-|
Entry functions for interacting with MIDI devices through Tidal.
-}
module Sound.Tidal.MIDI.Stream (midiStream, midiBackend, midiState, midiSetters, midiDevices, displayOutputDevices) where
-- generics
import Control.Concurrent
import Control.Concurrent.MVar ()
import qualified Data.Map as Map
-- Tidal specific
import Sound.Tidal.Stream as S
import Sound.Tidal.Time
import Sound.Tidal.Transition (transition)
-- MIDI specific
import Sound.Tidal.MIDI.Control
import Sound.Tidal.MIDI.Output
{-|
Create a handle for all currently used 'Output's indexed by their device name.
We use this to cache once opened devices.
This will be passed to _every_ initialization of a virtual stream to a MIDI device
and is necessary since, 'PortMidi' only allows a single connection to a device.
-}
midiDevices :: IO (MVar MidiDeviceMap)
midiDevices = newMVar $ Map.fromList []
{-|
Connect to a MIDI device with a given name and channel,
using a 'ControllerShape' to allow customized interaction
with specific MIDI synths.
Needs a 'MidiDeviceMap' to operate, create on using 'midiDevices'!
Usage:
@
(m1, mt1) <- midiSetters devices "My Synth Controller Device name" 1 synthController getNow
@
To find the correct name for your device see 'displayOutputDevices'
-}
midiSetters :: MVar MidiDeviceMap -- ^ A list of MIDI output devices
-> String -- ^ The name of the output device to connect
-> Int -- ^ The MIDI Channel to use
-> ControllerShape -- ^ The definition of params to be usable
-> IO Time -- ^ a method to get the current time
-> IO (ParamPattern -> IO (), (Time -> [ParamPattern] -> ParamPattern) -> ParamPattern -> IO ())
midiSetters d n c s getNow = do
ds <- midiState d n c s
return (setter ds, transition getNow ds)
{-|
Creates a single virtual stream to a MIDI device using a specific 'ControllerShape'
Needs a 'MidiDeviceMap' to operate, create one using 'midiDevices'!
-}
midiStream :: MVar MidiDeviceMap -> String -> Int -> ControllerShape -> IO (ParamPattern -> IO ())
midiStream d n c s = do
backend <- midiBackend d n c s
stream backend (toShape s)
{-|
Creates a single virtual state for a MIDI device using a specific 'ControllerShape'
This state can be used to either create a 'Sound.Tidal.Stream.setter' or a 'Sound.Tidal.Transition.transition' from it.
Needs a 'MidiDeviceMap' to operate, create one using 'midiDevices'!
-}
midiState :: MVar MidiDeviceMap -> String -> Int -> ControllerShape -> IO (MVar (ParamPattern, [ParamPattern]))
midiState d n c s = do
backend <- midiBackend d n c s
S.state backend (toShape s)
{-|
Opens a connection to a MIDI device and wraps it in a 'Sound.Tidal.Stream.Backend' implementation.
Needs a 'MidiDeviceMap' to operate, create one using 'midiDevices'!
-}
midiBackend :: MVar MidiDeviceMap -> String -> Int -> ControllerShape -> IO (Backend a)
midiBackend d n c cs = do
(s, o) <- makeConnection d n c cs
return $ Backend s (flushBackend o)