{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-}
-- | Mix multiple directories of rendered chunks into a single audio file.
module Synth.MixDown where
import qualified Control.Monad.Trans.Resource as Resource
import qualified Data.List as List
import qualified System.Directory as Directory
import qualified System.Environment as Environment
import qualified System.Exit as Exit
import           System.FilePath ((</>))
import qualified System.IO as IO

import qualified Util.Audio.Audio as Audio
import qualified Util.Audio.File as Audio.File
import qualified Synth.Shared.Config as Config

import           Global


main :: IO ()
main :: IO ()
main = do
    ([Char]
out, [[Char]]
dirs) <- IO [[Char]]
Environment.getArgs forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
        [Char]
out : [[Char]]
dirs
            | [Char]
".wav" forall a. Eq a => [a] -> [a] -> Bool
`List.isSuffixOf` [Char]
out -> forall (m :: * -> *) a. Monad m => a -> m a
return ([Char]
out, [[Char]]
dirs)
            | Bool
otherwise -> forall a. [Char] -> IO a
usage [Char]
"out arg should be .wav!"
        [] -> forall a. [Char] -> IO a
usage [Char]
""
    forall (m :: * -> *). Monad m => m Bool -> m () -> m ()
unlessM (forall (m :: * -> *) a. Monad m => (a -> m Bool) -> [a] -> m Bool
allM [Char] -> IO Bool
Directory.doesDirectoryExist [[Char]]
dirs) forall a b. (a -> b) -> a -> b
$
        forall a. [Char] -> IO a
usage [Char]
"non-directory arg"
    [AudioIO SamplingRate 2]
streams <- forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM [Char] -> IO (AudioIO SamplingRate 2)
streamDir [[Char]]
dirs
    forall (m :: * -> *) a. MonadUnliftIO m => ResourceT m a -> m a
Resource.runResourceT forall a b. (a -> b) -> a -> b
$ forall (rate :: Nat) (chan :: Nat).
(KnownNat rate, KnownNat chan) =>
Format -> [Char] -> AudioIO rate chan -> ResourceT IO ()
Audio.File.write Format
Audio.File.wavFormat [Char]
out forall a b. (a -> b) -> a -> b
$
        forall (m :: * -> *) (rate :: Nat) (chan :: Nat).
Monad m =>
[Audio m rate chan] -> Audio m rate chan
Audio.mix [AudioIO SamplingRate 2]
streams

usage :: String -> IO a
usage :: forall a. [Char] -> IO a
usage [Char]
msg = do
    forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Char]
msg) forall a b. (a -> b) -> a -> b
$
        Handle -> [Char] -> IO ()
IO.hPutStrLn Handle
IO.stderr forall a b. (a -> b) -> a -> b
$ [Char]
"error: " forall a. Semigroup a => a -> a -> a
<> [Char]
msg
    Handle -> [Char] -> IO ()
IO.hPutStrLn Handle
IO.stderr forall a b. (a -> b) -> a -> b
$ [Char]
"usage: mixdown out.wav [ dir1 dir2 ... ]"
    forall a. IO a
Exit.exitFailure

streamDir :: FilePath -> IO (Audio.AudioIO Config.SamplingRate 2)
streamDir :: [Char] -> IO (AudioIO SamplingRate 2)
streamDir [Char]
dir = do
    [[Char]]
chunks <- forall a. Ord a => [a] -> [a]
List.sort forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. (a -> Bool) -> [a] -> [a]
filter ([Char]
".wav" `List.isSuffixOf`) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$>
        [Char] -> IO [[Char]]
Directory.listDirectory [Char]
dir
    forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (forall (t :: * -> *) a. Foldable t => t a -> Bool
null [[Char]]
chunks) forall a b. (a -> b) -> a -> b
$
        Handle -> [Char] -> IO ()
IO.hPutStrLn Handle
IO.stderr forall a b. (a -> b) -> a -> b
$ [Char]
"WARNING: no *.wav in " forall a. Semigroup a => a -> a -> a
<> [Char]
dir
    forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall (rate :: Nat) (chan :: Nat).
(KnownNat rate, KnownNat chan) =>
Frames -> [[Char]] -> AudioIO rate chan
Audio.File.readCheckpoints Frames
Config.chunkSize (forall a b. (a -> b) -> [a] -> [b]
map ([Char]
dir</>) [[Char]]
chunks)