packages feed

jack-0.7.1: examples/Melody.hs

module Main where

import Common (mainWait)

import qualified Sound.JACK.MIDI as JackMidi
import qualified Sound.JACK as Jack
import qualified Sound.MIDI.Message as MIDI
import qualified Sound.MIDI.Message.Channel as Channel
import qualified Sound.MIDI.Message.Channel.Voice as Voice
import qualified Sound.MIDI.Message.Class.Construct as MidiCons

import qualified Control.Monad.Exception.Synchronous as Sync
import qualified Control.Monad.Trans.Class as Trans

import qualified Data.EventList.Absolute.TimeBody as AbsEventList
import qualified Data.EventList.Relative.TimeBody as EventList
import qualified Data.EventList.Relative.TimeMixed as EventListTM
import qualified Numeric.NonNegative.Wrapper as NonNegW
import Data.IORef (IORef, newIORef, readIORef, writeIORef, )

import qualified Foreign.C.Error as E

import System.Environment (getProgName)


scale :: [Channel.Pitch]
scale = map Channel.toPitch [60, 62, 64, 65, 67, 69, 71, 72]

eventLoop :: EventList.T NonNegW.Double MIDI.T
eventLoop =
    EventList.fromPairList $
    concatMap
        (\p ->
            let note on =
                    MidiCons.note (Channel.toChannel 0)
                        (Voice.normalVelocity, p, on)
            in  [(0, note True), (0.1, note False)])
        scale

main :: IO ()
main = do
    name <- getProgName
    Jack.handleExceptions $
        Jack.withClientDefault name $ \client ->
        Jack.withPort client "output" $ \output -> do
            rate <- fmap fromIntegral $ Trans.lift $ Jack.getSampleRate client
            stateRef <-
                Trans.lift $
                newIORef (EventList.resample rate $ EventList.cycle eventLoop)
            Jack.withProcess client (process stateRef output) $
                mainWait client name

process ::
    IORef (EventList.T NonNegW.Int MIDI.T) ->
    JackMidi.Port Jack.Output ->
    Jack.NFrames ->
    Sync.ExceptionalT E.Errno IO ()
process stateRef output nframes@(Jack.NFrames nframesInt) = do
    events <- Trans.lift $ readIORef stateRef
    let (currentEvents, futureEvents) =
            EventListTM.splitAtTime (fromIntegral nframesInt) events
    Trans.lift $ writeIORef stateRef futureEvents
    JackMidi.writeEventsToPort output nframes $
        AbsEventList.mapTime (Jack.NFrames . NonNegW.toNumber . fromIntegral) $
        EventList.toAbsoluteEventList 0 $
        fst $ EventListTM.viewTimeR currentEvents