module Synthesizer.ALSA.CausalIO.Process (
   Events,
   playFromEvents,
   Output,
   playFromEventsWithParams,
   ) where

import qualified Synthesizer.ALSA.EventList as MIDIEv

import qualified Synthesizer.ALSA.Storable.Play as Play
import Synthesizer.MIDI.EventList (StrictTime, )

import qualified Synthesizer.CausalIO.Process as PIO

import qualified Sound.ALSA.PCM as PCM
import qualified Sound.ALSA.Sequencer.Event as Event

import qualified Data.EventList.Relative.TimeTime  as EventListTT

import qualified Algebra.RealField      as RealField
import qualified Algebra.Additive       as Additive

import qualified Data.StorableVector as SV

import Control.Exception (bracket, )

import NumericPrelude.Numeric
import NumericPrelude.Base
import Prelude ()


type Events = EventListTT.T StrictTime [Event.T]

playFromEvents ::
   (RealField.C time, PCM.SampleFmt a, Additive.C a) =>
   Play.Device -> MIDIEv.ClientName -> time -> time -> PCM.SampleFreq ->
   PIO.T Events (SV.Vector a) ->
   IO ()
playFromEvents device name latency beat rate
      (PIO.Cons next create delete) =
   let sink = Play.makeSink device beat rate
       rateFloat = fromIntegral rate
   in  MIDIEv.withMIDIEventsChunked name beat rateFloat $ \getEventsList ->
       PCM.withSoundSink sink $ \to ->
{-
       Play.writeLazy sink to
          (SVL.replicate
              (SVL.chunkSize $ round (beat * rateFloat))
              (round (latency * rateFloat))
              (zero::Float))
-}
       Play.write sink to
          (SV.replicate (round (latency * rateFloat)) zero) >>
       (bracket create delete $ \state ->
        let loop getEvs0 s0 =
               case getEvs0 of
                  [] -> return ()
                  getEvents : getEvs1 -> do
                     evs <- getEvents
                     (pcm, s1) <- next evs s0
                     Play.write sink to pcm
                     loop getEvs1 s1
        in  loop getEventsList state)


type Output handle signal a =
   (IO ((PCM.Size, PCM.SampleFreq), handle),
    handle -> IO (),
    handle -> signal -> IO a)

playFromEventsWithParams ::
   Output handle signal () ->
   MIDIEv.ClientName ->
   ((PCM.Size, PCM.SampleFreq) -> PIO.T Events signal) ->
   IO ()
playFromEventsWithParams (open, close, write) name process =
   bracket open (close . snd) $ \(p@(period,rate),h) ->
      let rateFloat = fromIntegral rate :: Double
          beat = fromIntegral period / rateFloat
      in  MIDIEv.withMIDIEventsChunked name beat rateFloat $ \getEventsList ->
             case process p of
                PIO.Cons next create delete -> do
{-
                   write
                      (SV.replicate (round (latency * rateFloat)) zero)
-}
                   bracket create delete $ \state ->
                      let loop getEvs0 s0 =
                             case getEvs0 of
                                [] -> return ()
                                getEvents : getEvs1 -> do
                                   evs <- getEvents
                                   (chunk, s1) <- next evs s0
                                   write h chunk
                                   loop getEvs1 s1
                      in  loop getEventsList state