{- |
This module calls the 'soxi' command
which is available since 'sox' version 14.

We have to call 'soxi' for every option.
However we hide this in our interface,
such that we could do more efficiently,
if 'soxi' supports multiple outputs in future.
-}
module Sound.Sox.Information (
   T(Cons),
   simple,
   format,
   sampleRate,
   numberOfChannels,
   length,
   bitsPerSample,
   get,
   exampleMulti,
   exampleSingle,
   ) where

import qualified Control.Monad.Trans.Reader as MR
import Control.Applicative (Applicative, pure, liftA3, (<*>), )
import Data.String.HT (trim, )
import Text.Read.HT (maybeRead, )

import qualified System.Process as Proc
import qualified System.IO as IO
import Control.Exception (bracket, )
-- import System.IO.Error (ioError, userError, )
-- import System.Exit (ExitCode, )

import Prelude hiding (length, )


newtype T a =
   Cons (MR.ReaderT FilePath IO a)

instance Functor T where
   {-# INLINE fmap #-}
   fmap :: forall a b. (a -> b) -> T a -> T b
fmap a -> b
f (Cons ReaderT String IO a
m) = forall a. ReaderT String IO a -> T a
Cons (forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> b
f ReaderT String IO a
m)

instance Applicative T where
   {-# INLINE pure #-}
   {-# INLINE (<*>) #-}
   pure :: forall a. a -> T a
pure = forall a. ReaderT String IO a -> T a
Cons forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a. Applicative f => a -> f a
pure
   (Cons ReaderT String IO (a -> b)
f) <*> :: forall a b. T (a -> b) -> T a -> T b
<*> (Cons ReaderT String IO a
x) = forall a. ReaderT String IO a -> T a
Cons (ReaderT String IO (a -> b)
f forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> ReaderT String IO a
x)


simple :: Read a => (String -> Maybe a) -> String -> T a
simple :: forall a. Read a => (String -> Maybe a) -> String -> T a
simple String -> Maybe a
rd String
option =
   forall a. ReaderT String IO a -> T a
Cons forall a b. (a -> b) -> a -> b
$ forall r (m :: * -> *) a. (r -> m a) -> ReaderT r m a
MR.ReaderT forall a b. (a -> b) -> a -> b
$ \String
fileName ->
      forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracket
         (String
-> [String]
-> Maybe String
-> Maybe [(String, String)]
-> IO (Handle, Handle, Handle, ProcessHandle)
Proc.runInteractiveProcess String
"soxi"
             (String
option forall a. a -> [a] -> [a]
: String
fileName forall a. a -> [a] -> [a]
: [])
             forall a. Maybe a
Nothing forall a. Maybe a
Nothing)
         (\(Handle
input,Handle
output,Handle
err,ProcessHandle
proc) ->
             forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Handle -> IO ()
IO.hClose [Handle
input, Handle
output, Handle
err] forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>>
             ProcessHandle -> IO ()
Proc.terminateProcess ProcessHandle
proc)
         (\(Handle
_,Handle
output,Handle
_,ProcessHandle
_) ->
            forall b a. b -> (a -> b) -> Maybe a -> b
maybe
               (forall a. IOError -> IO a
ioError (String -> IOError
userError String
"soxi returned rubbish"))
               forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
.
            String -> Maybe a
rd forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<<
            Handle -> IO String
IO.hGetContents Handle
output)

format :: T String
format :: T String
format = forall a. Read a => (String -> Maybe a) -> String -> T a
simple forall a. a -> Maybe a
Just String
"-t"

simpleRead :: String -> T Int
simpleRead :: String -> T Int
simpleRead = forall a. Read a => (String -> Maybe a) -> String -> T a
simple (forall a. Read a => String -> Maybe a
maybeRead forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String
trim)

sampleRate :: T Int
sampleRate :: T Int
sampleRate = String -> T Int
simpleRead String
"-r"

numberOfChannels :: T Int
numberOfChannels :: T Int
numberOfChannels = String -> T Int
simpleRead String
"-c"

length :: T Int
length :: T Int
length = String -> T Int
simpleRead String
"-s"

bitsPerSample :: T Int
bitsPerSample :: T Int
bitsPerSample = String -> T Int
simpleRead String
"-b"



get :: T a -> FilePath -> IO a
get :: forall a. T a -> String -> IO a
get (Cons ReaderT String IO a
act) = forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
MR.runReaderT ReaderT String IO a
act

exampleMulti :: IO (String, Int, Int)
exampleMulti :: IO (String, Int, Int)
exampleMulti =
   forall a. T a -> String -> IO a
get (forall (f :: * -> *) a b c d.
Applicative f =>
(a -> b -> c -> d) -> f a -> f b -> f c -> f d
liftA3 (,,) T String
format T Int
sampleRate T Int
bitsPerSample) String
"test.aiff"

exampleSingle :: IO Int
exampleSingle :: IO Int
exampleSingle =
   forall a. T a -> String -> IO a
get T Int
sampleRate String
"test.aiff"