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