packages feed

synthesizer-alsa-0.5: src/Synthesizer/ALSA/Storable/Server/Common.hs

module Synthesizer.ALSA.Storable.Server.Common where

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

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

import qualified Synthesizer.Generic.Signal       as SigG
import qualified Synthesizer.Storable.Signal      as SigSt
import qualified Data.StorableVector.Lazy         as SVL

import qualified Sound.MIDI.Message.Channel       as ChannelMsg
import Sound.MIDI.Message.Channel (Channel, )

import qualified Data.EventList.Relative.TimeBody  as EventList

import qualified Sound.Sox.Frame         as SoxFrame
import qualified System.Exit as Exit

import Control.Category ((.), )

import qualified Algebra.RealField as RealField
import qualified Algebra.Field     as Field
import qualified Algebra.Ring      as Ring
import qualified Algebra.ToInteger as ToInteger
import qualified Algebra.Additive  as Additive

import NumericPrelude.Numeric (zero, round, )
import Prelude hiding (Real, round, break, id, (.), )


channel :: Channel
channel = ChannelMsg.toChannel 0

sampleRate :: Num a => a
-- sampleRate = 24000
-- sampleRate = 48000
sampleRate = 44100

latency :: Int
latency = 0
-- latency = 256
-- latency = 1000

chunkSize :: SVL.ChunkSize
chunkSize = Play.defaultChunkSize

lazySize :: SigG.LazySize
lazySize =
   let (SVL.ChunkSize size) = chunkSize
   in  SigG.LazySize size

periodTime :: Field.C t => t
periodTime =
   let (SVL.ChunkSize size) = chunkSize
   in  ToInteger.fromIntegral size Field./ Ring.fromInteger sampleRate

device :: Play.Device
device = Play.defaultDevice

clientName :: MIDIEv.ClientName
clientName = MIDIEv.ClientName "Haskell-Synthesizer"


type Real = Float


{-# INLINE withMIDIEvents #-}
withMIDIEvents ::
   (Double -> Double -> a -> IO b) ->
   (EventList.T StrictTime [Event.T] -> a) -> IO b
withMIDIEvents action proc =
   let rate = sampleRate
       per  = periodTime
   in  MIDIEv.withMIDIEvents clientName per rate $
       action per rate . proc


{-# INLINE play #-}
play ::
   (RealField.C t, Additive.C y, ALSA.SampleFmt y) =>
   t -> t -> SigSt.T y -> IO ()
play period rate =
   Play.auto (Play.makeSink device period (round rate)) .
   SigSt.append (SigSt.replicate chunkSize latency zero)
--   FiltG.delayPosLazySize chunkSize latency
--   FiltG.delayPos latency

-- ToDo: do not record the empty chunk that is inserted for latency
{-# INLINE playAndRecord #-}
playAndRecord ::
   (RealField.C t, Additive.C y, ALSA.SampleFmt y, SoxFrame.C y) =>
   FilePath -> t -> t -> SigSt.T y -> IO Exit.ExitCode
playAndRecord fileName period rate =
   let intRate = round rate
   in  Play.autoAndRecord fileName
          (ALSA.SoundFmt {ALSA.sampleFreq = intRate})
          (Play.makeSink device period intRate) .
       SigSt.append (SigSt.replicate chunkSize latency zero)