module ShakeRules where
import Development.Shake
import Development.Shake.FilePath
import Control.Monad (guard, when)
import Text.Printf (printf)
import qualified Data.List as List
data Configuration =
Configuration {
useBackground :: Bool,
frameRate :: Integer,
resolution :: Integer,
antialias :: [String],
pages :: [String]
}
defaultConfig :: Configuration
defaultConfig =
Configuration {
useBackground = True,
frameRate = 50,
resolution = 108,
antialias = ["-dTextAlphaBits=2", "-dGraphicsAlphaBits=2"],
pages = []
}
previewConfig :: Configuration
previewConfig =
Configuration {
useBackground = False,
frameRate = 25,
resolution = 54,
antialias = [],
pages = []
}
tmpRoot :: FilePath
tmpRoot = "/tmp"
tmpFramesDir :: String -> FilePath
tmpFramesDir stem = tmpRoot </> stem <> "-frames"
{-
tif24nc and ppmraw are much faster than png16m,
but occupy more temporary space.
-}
framePattern :: FilePath; frameFormat :: String
(framePattern, frameFormat) =
case id '_' of
'p' -> ("%04d.png", "png16m")
't' -> ("%04d.tif", "tif24nc")
_ -> ("%04d.ppm", "ppmraw")
require :: FilePath -> Action FilePath
require path = do
need [path]
return path
whenAvailable :: FilePath -> (FilePath -> Action ()) -> Action ()
whenAvailable path act = do
available <- doesFileExist path
when available $ act path
chooseAudioSource :: FilePath -> [String] -> Action FilePath
chooseAudioSource stem audioFormats = do
let audioSrcs = map (stem <.>) audioFormats
available <- traverse doesFileExist audioSrcs
case filter fst $ zip available audioSrcs of
(_,audioSrc):_ -> return audioSrc
_ -> fail "No audio source available"
configured :: Configuration -> FilePath -> Rules ()
configured config prefix = do
prefix <> "*.pdf" %> \dst -> do
let base = takeBaseName dst
midi <- require $ base <.> "mid"
let background = base <.> "jpeg"
backgroundAvailable <-
if useBackground config
then doesFileExist background
else return False
let optionsFile = base <.> "options"
optionsAvailable <- doesFileExist optionsFile
options <-
if optionsAvailable
then readFile' optionsFile
else return ""
let titleFile = base <.> "title"
titleAvailable <- doesFileExist titleFile
title <-
if titleAvailable
then fmap (\str -> ["--title", str]) $ readFile' titleFile
else return []
cmd_ "boomwhacker" title options "--rate" (show $ frameRate config)
(guard backgroundAvailable >> ["--background", background])
[midi, dst]
tmpRoot </> prefix <> "*.frames" %> \dst -> do
let stem =
maybe
(error "cannot strip temporary directory prefix")
(dropExtension . dropWhile (pathSeparator==))
(List.stripPrefix tmpRoot dst)
pdf <- require $ stem <.> "pdf"
let framesDir = tmpFramesDir stem
cmd_ "mkdir" "-p" [framesDir] -- avoid failure of 'rm -r'
cmd_ "rm" "-r" [framesDir]
cmd_ "mkdir" "-p" [framesDir]
cmd_ "gs-parallel" "-j6"
(pages config) pdf (framesDir </> framePattern) "--"
"-dNOPAUSE" "-dBATCH"
("-sDEVICE="++frameFormat) (antialias config)
[printf "-dDEVICEHEIGHT=%d" (resolution config * 10) :: String]
[printf "-r%d" (resolution config) :: String]
cmd_ "touch" [dst]
prefix <> "*.flv" %> \dst -> do
let stem = dropExtension dst
need [tmpRoot </> stem <.> "frames"]
let framesDir = tmpFramesDir stem
audioSrc <- chooseAudioSource (takeBaseName dst) ["mp3", "aac", "wav"]
cmd_ "ffmpeg" "-r" (show $ frameRate config) "-f" "image2"
"-i" [framesDir </> framePattern]
"-i" [audioSrc]
"-vcodec" "flashsv" "-acodec" "copy" "-y" [dst]
prefix <> "*.mp4" %> \dst -> do
let stem = dropExtension dst
need [tmpRoot </> stem <.> "frames"]
let framesDir = tmpFramesDir stem
audioSrc <-
chooseAudioSource (takeBaseName dst) ["mp3", "aac", "flac", "wav"]
cmd_ "ffmpeg" "-r" (show $ frameRate config) "-f" "image2"
"-i" [framesDir </> framePattern]
"-i" [audioSrc]
"-b:v" "8000k" "-acodec" "copy" "-y" [dst]
generic :: Rules ()
generic = do
"//*.inspect-csv" %> \dst -> do
let Just stem = stripExtension "inspect-csv" dst
src <- require $ stem <.> "mid"
cmd_ "midicsv" [src, dst]
"//*.mid" %> \dst ->
whenAvailable (dst -<.> "csv") $ \src -> do
cmd_ "csvmidi" [src, dst]
"//*.csv" %> \dst ->
whenAvailable (dst -<.> "fods") $ \src -> do
Stdout csvUtf8 <- cmd "ods2csv" "--sheetnumber" "1" [src]
cmd_ "iconv" "-f" "utf8" "-t" "latin1"
(StdinBS csvUtf8) (FileStdout dst)
"//*.wav" %> \dst ->
whenAvailable (dst -<.> "mid") $ \src -> do
cmd_ "timidity" "--preserve-silence" "-A300" "-Ow" [src]