{-# LANGUAGE Arrows, ExistentialQuantification, FlexibleContexts, ScopedTypeVariables #-}



module Euterpea.IO.Audio.Basics (

  outA, 

  integral,

  countDown, countUp,

  upsample,

  pchToHz, apToHz

) where



import Euterpea.Music

import Euterpea.IO.Audio.Types

import Control.Arrow

import Control.Arrow.Operations

import Control.Arrow.ArrowP





outA :: (Arrow a) => a b b

outA = arr id



integral :: forall a p. (ArrowCircuit a, Clock p) => ArrowP a p Double Double

integral = 

    let dt = 1 / rate (undefined :: p)

    in proc x -> do

      rec let i' = i + x * dt

          i <- delay 0 -< i'

      outA -< i



countDown :: ArrowCircuit a => Int -> a () Int

countDown x = proc _ -> do

    rec i <- delay x -< i - 1

    outA -< i



countUp :: ArrowCircuit a => a () Int

countUp = proc _ -> do

    rec i <- delay 0 -< i + 1

    outA -< i





upsample :: forall a b c p1 p2. (ArrowChoice a, ArrowCircuit a, Clock p1, Clock p2, AudioSample c) 

         => ArrowP a p1 b c -> ArrowP a p2 b c

upsample f = g 

   where g = proc x -> do 

               rec

                 cc <- delay 0 -< if cc >= r-1 then 0 else cc+1

                 y <- if cc == 0 then ArrowP (strip f) -< x 

                                 else delay zero       -< y

               outA -< y

         r = if outRate < inRate 

             then error "Cannot upsample a signal of higher rate to lower rate" 

             else outRate / inRate

         inRate  = rate (undefined :: p1)

         outRate = rate (undefined :: p2)



-- Some useful auxiliary functions.



-- | Converting an AbsPitch to hertz (cycles per second):

apToHz :: Floating a => AbsPitch -> a

apToHz ap = 440 * 2 ** (fromIntegral (ap - absPitch (A,4)) / 12)



-- | Converting from a Pitch value to Hz:

pchToHz :: Floating a => Pitch -> a

pchToHz = apToHz . absPitch