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)