packages feed

align-audio-0.0.0.2: src/Main.hs

module Main where

import qualified CorrelateResample
import qualified Correlate
import qualified Option

import qualified Synthesizer.Basic.Binary as Bin
import qualified Data.StorableVector.Lazy as SVL
import qualified Sound.SoxLib as SoxLib
import Foreign.Storable (peek)

import qualified Options.Applicative as OP
import Shell.Utility.Exit (exitFailureMsg)

import Text.Printf (printf)

import Control.Monad (when)
import Control.Applicative (liftA2, (<*>))
import Data.Maybe.HT (toMaybe)
import Data.Maybe (fromMaybe)


withFirstChannel ::
   FilePath ->
   (Double -> SVL.Vector Float -> IO ()) ->
   IO ()
withFirstChannel src act =
   SoxLib.withRead SoxLib.defaultReaderInfo src $ \fmtInPtr -> do
      fmtIn <- peek fmtInPtr
      let numChan = fromMaybe 1 $ SoxLib.channels $ SoxLib.signalInfo fmtIn
      rate <-
         case SoxLib.rate $ SoxLib.signalInfo fmtIn of
            Nothing -> exitFailureMsg "no sample rate found"
            Just rate -> return rate
      act rate .
         SVL.sieve numChan . SVL.map Bin.toCanonical =<<
         SoxLib.readStorableVectorLazy fmtInPtr (SVL.ChunkSize 65536)

maybeInteger :: Double -> Maybe Integer
maybeInteger nf =
   let n = round nf
   in toMaybe (nf == fromInteger n) n

absLag :: Int -> (String, Int)
absLag n =
   if n >= 0
      then ("second", n)
      else ("first", -n)


main :: IO ()
main = SoxLib.formatWith $ do
   opt <-
      OP.execParser $
      OP.info (OP.helper <*> Option.parseFlags) Option.desc
   withFirstChannel (Option.input0 opt) $ \rate0 audio0 ->
      withFirstChannel (Option.input1 opt) $ \rate1 audio1 ->
      case liftA2 (,) (maybeInteger rate0) (maybeInteger rate1) of
         Nothing -> do
            when (rate0/=rate1) $
               exitFailureMsg "sample rates are fractional and differ"
            let (ref,lag) = absLag $ Correlate.determineLag audio0 audio1
            printf
               ("you must delay the %s source by %d samples at rate %f Hz, "
                  ++ "i.e. %.6f seconds\n")
               ref lag rate0 (fromIntegral lag / rate0)
         Just (irate0,irate1) -> do
            let dstRate = Option.rate opt
            let (ref,lag) = absLag $
                  CorrelateResample.determineLag dstRate
                     (irate0,audio0) (irate1,audio1)
            printf
               ("you must delay the %s source by %d samples at rate %d Hz, "
                  ++ "i.e. %.3f seconds\n")
               ref lag dstRate
               (fromIntegral lag / fromInteger dstRate :: Double)