content: {-# 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