alsa-midi-0.3: examples/SimpleSynth.hs
module Main where
import qualified Sound.JACK as Jack
import qualified Sound.ALSA.Sequencer as Alsa
import Sound.ALSA.Sequencer (MIDIEvent)
import Foreign.C.Types (CFloat)
import Data.IORef (IORef, newIORef, writeIORef, readIORef, modifyIORef)
import Control.Concurrent
main :: IO ()
main = do
n <- newIORef Nothing
(_client, incoming) <- Alsa.receiveMIDIEvents "SimpleSynth" "inport"
forkIO $ writeNotes n incoming
performNotes n
writeNotes ::
IORef (Maybe MIDIEvent)
-> [MIDIEvent]
-> IO ()
writeNotes ref =
mapM_ (\ a -> writeIORef ref $ toMaybe (Alsa.isNoteOn a) a)
toMaybe :: Bool -> a -> Maybe a
toMaybe c x = if c then Just x else Nothing
performNotes ::
IORef (Maybe MIDIEvent) -> IO ()
performNotes ref = do
putStrLn "performNotes..."
phase <- newIORef 0
Jack.mainStereo (const (synth ref phase))
synth ::
IORef (Maybe MIDIEvent)
-> IORef CFloat
-> IO (CFloat, CFloat)
synth ref phase =
readIORef ref >>=
liftM split
(maybe
(return 0)
(\n -> do
p <- readIORef phase
modifyIORef phase (incPhase (midi2Hz (Alsa.note n)) 44100)
return $ sin (p * 2 * pi)))
split :: t -> (t, t)
split a = (a, a)
incPhase :: (RealFrac a) => a -> a -> a -> a
incPhase frq samplerate phase =
snd (properFraction (phase + frq / samplerate) `asTypeOf`
(undefined::Int,phase))
midi2Hz :: Int -> CFloat
midi2Hz mn = midiBaseFrq * semitoneFactor ^ mn
semitoneFactor :: CFloat
semitoneFactor = 2 ** (1 / 12)
midiBaseFrq :: CFloat
midiBaseFrq = 440 / semitoneFactor ^ (69::Int)