{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE Rank2Types #-}
module Synthesizer.Dimensional.RateAmplitude.Play (
   auto,
   timeVoltage,
   timeVoltageMonoDoubleToInt16,
   timeVoltageStereoDoubleToInt16,
   renderTimeVoltageMonoDoubleToInt16,
   renderTimeVoltageStereoDoubleToInt16,
  ) where

import qualified Sound.Sox.Play as Play
import qualified Sound.Sox.Option.Format as SoxOpt
import qualified Sound.Sox.Frame as Frame
import qualified Synthesizer.Basic.Binary as BinSmp
import qualified Data.StorableVector.Lazy.Builder as Builder
import Foreign.Storable (Storable, )

import qualified Synthesizer.Dimensional.Process as Proc

import qualified Synthesizer.Dimensional.Amplitude.Signal as SigA
import qualified Synthesizer.Dimensional.RateAmplitude.Signal as SigRA
import qualified Synthesizer.Dimensional.RateWrapper as SigP

import qualified Synthesizer.Storable.Signal as SigSt
import qualified Synthesizer.State.Signal as Sig

import qualified Synthesizer.Frame.Stereo as Stereo

import qualified Algebra.DimensionTerm as Dim
import qualified Number.DimensionTerm  as DN

import qualified Algebra.ToInteger      as ToInteger
-- import qualified Algebra.Transcendental as Trans
import qualified Algebra.Module         as Module
import qualified Algebra.RealField      as RealField
import qualified Algebra.Field          as Field
-- import qualified Algebra.Ring           as Ring

import System.Exit(ExitCode)

import NumericPrelude
import PreludeBase


{-# INLINE auto #-}
auto ::
    (Bounded int, ToInteger.C int, Storable int, Frame.C int, BinSmp.C yv,
     Dim.C u, RealField.C t,
     Dim.C v, Module.C y yv, Field.C y) =>
   DN.T (Dim.Recip u) t ->
   DN.T v y ->
   (int -> Builder.Builder int) ->
   SigP.T u t (SigA.S v y) yv ->
--   SigP.T u t (SigA.D v y SigS.S) yv ->
   IO ExitCode
auto freqUnit amp put sig =
   let opts =
          SoxOpt.numberOfChannels (BinSmp.numberOfSignalChannels sig)
       sampleRate =
          DN.divToScalar (SigP.sampleRate sig) freqUnit
   in  Play.extended SigSt.hPut opts SoxOpt.none
          (round sampleRate)
          (Builder.toLazyStorableVector SigSt.defaultChunkSize $
           Sig.monoidConcatMap (BinSmp.outputFromCanonical put) $
           SigA.vectorSamples (flip DN.divToScalar amp) sig)


{-# INLINE timeVoltage #-}
timeVoltage ::
    (Bounded int, ToInteger.C int, Storable int, Frame.C int, BinSmp.C yv,
     RealField.C t,
     Module.C y yv, Field.C y) =>
   (int -> Builder.Builder int) ->
   SigP.T Dim.Time t (SigA.S Dim.Voltage y) yv ->
--   SigP.T Dim.Time t (SigA.D Dim.Voltage y SigS.S) yv ->
   IO ExitCode
timeVoltage =
   auto (DN.frequency one) (DN.voltage one)


{-# INLINE timeVoltageMonoDoubleToInt16 #-}
timeVoltageMonoDoubleToInt16 ::
   SigP.T Dim.Time Double (SigA.S Dim.Voltage Double) Double ->
   IO ExitCode
timeVoltageMonoDoubleToInt16 sig =
   let rate = DN.toNumberWithDimension Dim.frequency (SigP.sampleRate sig)
   in  Play.simple SigSt.hPut SoxOpt.none (round rate)
          (SigP.signal (SigRA.toStorableInt16Mono sig))


{-# INLINE timeVoltageStereoDoubleToInt16 #-}
timeVoltageStereoDoubleToInt16 ::
   SigP.T Dim.Time Double (SigA.S Dim.Voltage Double) (Stereo.T Double) ->
--   SigP.T Dim.Time t (SigA.D Dim.Voltage y SigS.S) yv ->
   IO ExitCode
timeVoltageStereoDoubleToInt16 sig =
   let rate = DN.toNumberWithDimension Dim.frequency (SigP.sampleRate sig)
   in  Play.simple SigSt.hPut SoxOpt.none (round rate)
          (SigP.signal (SigRA.toStorableInt16Stereo sig))


{-# INLINE renderTimeVoltageMonoDoubleToInt16 #-}
renderTimeVoltageMonoDoubleToInt16 ::
   DN.T Dim.Frequency Double ->
   (forall s. Proc.T s Dim.Time Double (SigA.R s Dim.Voltage Double Double)) ->
   IO ExitCode
renderTimeVoltageMonoDoubleToInt16 rate sig =
   timeVoltageMonoDoubleToInt16 (SigP.runProcess rate sig)

{-# INLINE renderTimeVoltageStereoDoubleToInt16 #-}
renderTimeVoltageStereoDoubleToInt16 ::
   DN.T Dim.Frequency Double ->
   (forall s. Proc.T s Dim.Time Double (SigA.R s Dim.Voltage Double (Stereo.T Double))) ->
   IO ExitCode
renderTimeVoltageStereoDoubleToInt16 rate sig =
   timeVoltageStereoDoubleToInt16 (SigP.runProcess rate sig)