packages feed

jammittools 0.5.3.2 → 0.5.4

raw patch · 10 files changed

+240/−118 lines, 10 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

Files

+ CHANGELOG.md view
@@ -0,0 +1,105 @@+## v0.5.4
+
+  * Program will always also look for songs in the folder it's in
+  * Add option and script to export all backing tracks without combining them
+
+## v0.5.3.2
+
+Mac-only release, fixes some issues on Sierra
+
+## v0.5.3
+
+Added support for "Bass 2", "Vocals" (instead of "Vocal"),
+and "Vocal 1"/"Vocal 2" (instead of "Vocal"/"B Vocals").
+
+## v0.5.2
+
+Added the ability to export the metronome click track.
+Available via the `-m` option, or via the `-x` or `-b` full backup options
+(including the `easy-export` script on Windows/Mac).
+
+## v0.5.1
+
+Added a `-b` option for backing up an entire library of songs.
+
+## v0.5.0.3
+
+Fixed some bugs in both audio and image decoding.
+
+## v0.5.0.1
+
+Fixed a bug in reading the `beats.plist` file
+(not actually used in the executable).
+
+## v0.5
+
+  * Fixed a bug where `"Song Title "` wasn't considered the same as `"Song Title"`
+  * Much internal cleanup, smaller executable size
+  * Library: added some more Jammit types (beats, ghost beats, sections)
+
+## v0.4.1.1
+
+Fixes for GHC 7.10 (no new functionality).
+
+## v0.4.1
+
+Uses [`conduit-audio`](http://hackage.haskell.org/package/conduit-audio),
+and exposes the audio file as an
+[`AudioStream`](http://hackage.haskell.org/package/conduit-audio-0.1/docs/Data-Conduit-Audio.html#t:AudioSource).
+
+## v0.4
+
+  * Doesn't require ImageMagick or SoX anymore!
+    Thanks to [JuicyPixels](https://hackage.haskell.org/package/JuicyPixels),
+    [HPDF](https://hackage.haskell.org/package/HPDF),
+    [conduit](https://hackage.haskell.org/package/conduit),
+    and audio code ported from [aifc2wav](http://sed.free.fr/aifc2wav.html).
+  * Prints better instructions.
+  * Prints what it is doing during each step of the `-x dir` export option.
+  * Error if the search parameters don't narrow down the library
+    to exactly one song.
+
+## v0.3.2
+
+Fixes for Windows, other platforms are unchanged:
+
+  * Use `wchar_t` for C functions to support non-ASCII filenames
+  * Look for SoX in Program Files (the installer doesn't put it in the `PATH`)
+
+## v0.3.1
+
+  * Added support for Drums 1, Drums 2 and Organ parts
+  * Better argument handling (quits on unrecognized argument)
+
+## v0.3
+
+Split the package into a library and an executable.
+No functional changes to the executable.
+
+## v0.2.0.1
+
+Documentation, packaging, and version bounds improvements.
+
+## v0.2
+
+  * Proper Mac support (knows the official library location).
+  * Changed the long forms of some commands for consistency.
+  * Removed `-u` and `-b`, replaced with a simple `-c` option which does a dry
+    run of audio extraction. That is, it does no audio conversion, but exits
+    with a code of 0 only if all specified audio parts are found.
+  * Fixed a crash on very large libraries.
+
+## v0.1.1
+
+  * `-u` and `-b` audio options for
+    [`onyxite-customs`](https://github.com/mtolly/onyxite-customs) project
+  * `-T` and `-R` perform exact title/artist search,
+    instead of existing `-t` and `-r` "case-insensitive substring" search
+
+## v0.1
+
+Binary compiled with GHC 7.6.3 32-bit on Windows 7 64-bit.
+Should also work on Wine.
+You must install [ImageMagick](http://www.imagemagick.org/script/index.php)
+and [SoX](http://sox.sourceforge.net/)
+for sheet music and audio export respectively.
Main.hs view
@@ -1,28 +1,28 @@ {-# LANGUAGE CPP #-} module Main (main) where +import           Control.Applicative    ((<|>))+import           Control.Monad          (forM_, unless, (>=>))+import           Data.Char              (toLower)+import           Data.List              (nub, partition, sort)+import           Data.Maybe             (catMaybes, fromMaybe, mapMaybe,+                                         maybeToList)+import           Data.Version           (showVersion)+import qualified Paths_jammittools      as Paths+import           Sound.Jammit.Base+import           Sound.Jammit.Export+import qualified System.Console.GetOpt  as Opt+import           System.Directory       (createDirectoryIfMissing)+import qualified System.Environment     as Env+import           System.Exit            (exitFailure)+import           System.FilePath        (makeValid, takeDirectory, (<.>), (</>))+import           Text.PrettyPrint.Boxes (hsep, left, render, text, top, vcat,+                                         (/+/))+ #if !MIN_VERSION_base(4,8,0)-import Control.Applicative ((<$>))+import           Control.Applicative    ((<$>)) #endif-import Control.Applicative ((<|>))-import Control.Monad ((>=>), forM_, unless)-import Data.Char (toLower)-import Data.List (sort, nub, partition)-import Data.Maybe (catMaybes, mapMaybe, fromMaybe, listToMaybe)-import Data.Version (showVersion)-import qualified System.Console.GetOpt as Opt-import qualified System.Environment as Env-import System.Exit (exitFailure) -import System.Directory (createDirectoryIfMissing)-import System.FilePath ((</>), makeValid)-import Text.PrettyPrint.Boxes-  (text, vcat, left, render, hsep, top, (/+/))--import Sound.Jammit.Base-import Sound.Jammit.Export-import qualified Paths_jammittools as Paths- printUsage :: IO () printUsage = do   prog <- Env.getProgName@@ -59,7 +59,7 @@             backingOrder = [Drums, Guitar, Keyboard, Bass, Vocal]             isGuitar p = elem (partToInstrument p) [Guitar, Bass]             (gtrs, nongtrs) = partition isGuitar [minBound .. maxBound]-            chosenBacking = listToMaybe $ flip mapMaybe backingOrder $ \i ->+            backingTracks = flip mapMaybe backingOrder $ \i ->               case getOneResult (Without i) audios of                 Left  _  -> Nothing                 Right fp -> Just (i, fp)@@ -68,7 +68,7 @@             (Right note, Right tab) -> let               parts = [note, tab]               systemHeight = sum $ map snd parts-              fout = dout </> drop 4 (map toLower (show p) ++ ".pdf")+              fout = dout </> drop 4 (map toLower $ show p) <.> "pdf"               in do                 putStrLn $ "Exporting notation & tab for " ++ show p                 runSheet [note, tab] (getPageLines systemHeight args) fout@@ -77,7 +77,7 @@           case getOneResult (Notation p) sheets of             Left  _    -> return ()             Right note -> let-              fout = dout </> drop 4 (map toLower (show p) ++ ".pdf")+              fout = dout </> drop 4 (map toLower $ show p) <.> "pdf"               in do                 putStrLn $ "Exporting notation for " ++ show p                 runSheet [note] (getPageLines (snd note) args) fout@@ -85,18 +85,23 @@           case getOneResult (Only p) audios of             Left  _  -> return ()             Right fp -> let-              fout = dout </> drop 4 (map toLower (show p) ++ ".wav")+              fout = dout </> drop 4 (map toLower $ show p) <.> "wav"               in do                 putStrLn $ "Exporting audio for " ++ show p                 runAudio [fp] [] fout-        case chosenBacking of-          Nothing            -> return ()-          Just (inst, fback) -> let-            others = [ fp | (Only p, fp) <- audios, partToInstrument p /= inst ]-            fout = dout </> "backing.wav"-            in do-              putStrLn "Exporting backing audio (could take a while)"-              runAudio [fback] others fout+        if rawBackings args+          then forM_ backingTracks $ \(inst, fback) -> do+            putStrLn $ "Exporting backing track for " ++ show inst+            let fout = dout </> "backing-" ++ map toLower (show inst) <.> "wav"+            runAudio [fback] [] fout+          else case backingTracks of+            []                -> return ()+            (inst, fback) : _ -> let+              others = [ fp | (Only p, fp) <- audios, partToInstrument p /= inst ]+              fout = dout </> "backing.wav"+              in do+                putStrLn "Exporting backing audio (could take a while)"+                runAudio [fback] others fout         clickTracks <- mapM loadBeats $ map (\(fp, _, _) -> fp) matches         case catMaybes clickTracks of           [] -> return ()@@ -190,14 +195,13 @@ -- to filter it. searchResults :: Args -> IO Library searchResults args = do-  jmt <- case jammitDir args of-    Just j  -> return j+  exeDir <- takeDirectory <$> Env.getExecutablePath+  jmt    <- case jammitDir args of+    Just j  -> return [j]     Nothing -> Env.lookupEnv "JAMMIT" >>= \mv -> case mv of-      Just j -> return j-      Nothing -> let-        err = "Couldn't find Jammit directory. Try -j or the env var JAMMIT"-        in fromMaybe (error err) <$> findJammitDir-  db <- loadLibrary jmt+      Just j  -> return [j]+      Nothing -> maybeToList <$> findJammitDir+  db <- concat <$> mapM loadLibrary (exeDir : jmt)   return $ filterLibrary args db  -- | Checks that the search actually narrowed down the library to a single song.@@ -273,6 +277,9 @@   , Opt.Option ['c'] ["check"]     (Opt.NoArg $ \a -> a { function = CheckPresence })     "function: check presence of audio parts"+  , Opt.Option [] ["raw"]+    (Opt.NoArg $ \a -> a { rawBackings = True })+    "when exporting song/library, extract all individual backing tracks"   ]  data Args = Args@@ -282,6 +289,7 @@   , pageLines     :: Maybe Int   , jammitDir     :: Maybe FilePath   , function      :: Function+  , rawBackings   :: Bool   }  data Function@@ -303,6 +311,7 @@   , pageLines     = Nothing   , jammitDir     = Nothing   , function      = PrintUsage+  , rawBackings   = False   }  partToChar :: Part -> Char
README.md view
@@ -52,6 +52,7 @@     # On Windows and OS X, the official app's library location is used     # if you do not specify this flag.     # You can also specify the environment variable JAMMIT.+    # Songs in the same folder as the program will also always be found.      -T "My Song"     # Exact search on title, instead of -t which is case-insensitive substring.@@ -69,6 +70,10 @@     # One system contains a line from each individual part.     # If this flag is not given, an appropriate number of systems will be chosen     # to get close to an 8.5" by 11" page ratio.++    --raw+    # For -x and -b options, exports all original backing tracks separately+    # instead of producing a combined one.  Instrument parts are given by this somewhat terse syntax: 
jammittools.cabal view
@@ -1,5 +1,5 @@ Name:               jammittools-Version:            0.5.3.2+Version:            0.5.4 Synopsis:           Export sheet music and audio from Windows/Mac app Jammit Description: @@ -20,7 +20,9 @@ Category:           Sound Homepage:           https://github.com/mtolly/jammittools Bug-Reports:        https://github.com/mtolly/jammittools/issues-Extra-Source-Files: README.md+Extra-Source-Files:+  README.md+  CHANGELOG.md  library   exposed-modules:
src/Sound/Jammit/Base.hs view
@@ -1,7 +1,7 @@ {- | Basic types and functions for dealing with Jammit song packages. -}-{-# LANGUAGE CPP #-}+{-# LANGUAGE CPP             #-} {-# LANGUAGE RecordWildCards #-} module Sound.Jammit.Base ( Instrument(..)@@ -23,21 +23,22 @@ , sheetWidth, sheetHeight ) where +import           Control.Applicative                ((<|>))+import           Control.Monad                      (filterM, forM, guard)+import           Data.Char                          (toLower)+import qualified Data.Map                           as Map+import           Data.Maybe                         (fromMaybe)+import           Sound.Jammit.Internal.PropertyList+import qualified System.Directory                   as Dir+import           System.Environment                 (lookupEnv)+import           System.FilePath                    (dropTrailingPathSeparator,+                                                     takeFileName, (</>))+import qualified System.Info                        as Info+ #if !MIN_VERSION_base(4,8,0)-import Control.Applicative ((<$>))+import           Control.Applicative                ((<$>)) #endif-import Control.Applicative ((<|>))-import Control.Monad (filterM, guard, forM)-import Data.Char (toLower)-import Data.Maybe (fromMaybe)-import System.Environment (lookupEnv)-import qualified Data.Map as Map-import qualified System.Directory as Dir-import System.FilePath ((</>), takeFileName, dropTrailingPathSeparator)-import qualified System.Info as Info -import Sound.Jammit.Internal.PropertyList- -- | The 'Enum' instance corresponds to the number used in the 'instrument' -- property, and the names (used by 'Show' and 'Read') are capitalized versions -- of those used in the 'skillLevel' property.@@ -236,7 +237,8 @@ songSubdirs dir = case takeFileName $ dropTrailingPathSeparator dir of   fn@('.' : _ : _) | fn /= ".." -> return []   _                             -> do-    isSong <- Dir.doesFileExist $ dir </> "info.plist"+    isSong <- and <$> mapM Dir.doesFileExist+      [dir </> "info.plist", dir </> "tracks.plist"]     let here = [dir | isSong]     subdirs <- lsAbsolute dir >>= filterM Dir.doesDirectoryExist     (here ++) . concat <$> mapM songSubdirs subdirs
src/Sound/Jammit/Export.hs view
@@ -15,23 +15,21 @@ , writeMetronomeTrack ) where -import Control.Applicative (liftA2)-import Control.Monad (forM, forever)-import Data.Char (toLower)-import Data.Int (Int16, Int32)-import Data.List (isInfixOf, sort, isPrefixOf)-import Data.Maybe (catMaybes)--import System.Directory (getDirectoryContents)-import System.FilePath ((</>), splitFileName, takeFileName)--import Sound.Jammit.Internal.Image-import Sound.Jammit.Base-import Sound.Jammit.Internal.Audio-import Sound.Jammit.Internal.TempIO--import qualified Data.Conduit.Audio as A-import Control.Monad.Trans.Resource (MonadResource, runResourceT)+import           Control.Applicative          (liftA2)+import           Control.Monad                (forM, forever)+import           Control.Monad.Trans.Resource (MonadResource, runResourceT)+import           Data.Char                    (toLower)+import qualified Data.Conduit.Audio           as A+import           Data.Int                     (Int16, Int32)+import           Data.List                    (isInfixOf, isPrefixOf, sort)+import           Data.Maybe                   (catMaybes, fromMaybe)+import           Sound.Jammit.Base+import           Sound.Jammit.Internal.Audio+import           Sound.Jammit.Internal.Image+import           Sound.Jammit.Internal.TempIO+import           System.Directory             (getDirectoryContents)+import           System.FilePath              (splitFileName, takeFileName,+                                               (</>))  type Library = [(FilePath, Info, [Track])] @@ -81,15 +79,18 @@     _ -> []  audioSource :: (MonadResource m) => FilePath -> IO (A.AudioSource m Int16)-audioSource fp = if takeFileName fp `elem` [tttDrums, tttDrumsBack]-  then fmap (A.padStart $ A.Frames 38) $ readIMA fp-  else readIMA fp-  -- I've only found one audio file where the instruments are not aligned:-  -- the drums and drums backing track for Take the Time are 38 samples ahead-  -- of the other instruments. So as a hack, we pad the front of them by 38-  -- samples to line things up.-  where tttDrums     = "793EAAE0-6761-44D7-9A9A-1FB451A2A438_jcfx"-        tttDrumsBack = "37EE5AA5-4049-4CED-844A-D34F6B165F67_jcfx"+audioSource fp = let+  -- These are hacks that make one instrument line up with the rest of a song.+  timingHacks =+    -- Take the Time (Dream Theater)+    [ ("793EAAE0-6761-44D7-9A9A-1FB451A2A438_jcfx", A.padStart $ A.Frames 38) -- Drums+    , ("37EE5AA5-4049-4CED-844A-D34F6B165F67_jcfx", A.padStart $ A.Frames 38) -- Drums backing+    -- Rockstar (Nickelback)+    , ("FB9A99DB-C70C-47CB-B63F-A78F11AC05D5_jcfx", A.dropStart $ A.Frames 10) -- Vocal+    , ("7998F9B7-8A97-49D0-A3A9-C001FDD1C1DC_jcfx", A.dropStart $ A.Frames 10) -- B Vocals+    , ("C4C543A0-EB1F-4628-A401-C5860D675FA4_jcfx", A.dropStart $ A.Frames 10) -- Vocal backing+    ]+  in fromMaybe id (lookup (takeFileName fp) timingHacks) <$> readIMA fp  runAudio   :: [FilePath] -- ^ AIFCs to mix in normally
src/Sound/Jammit/Internal/Audio.hs view
@@ -13,10 +13,12 @@  import           Control.Monad                (liftM2, unless) import           Control.Monad.IO.Class       (liftIO)+import           Control.Monad.Trans.Resource (MonadResource) import qualified Data.ByteString              as B-import           Data.ByteString.Char8        () -- for IsString instance+import           Data.ByteString.Char8        () import qualified Data.ByteString.Unsafe       as B import qualified Data.Conduit                 as C+import qualified Data.Conduit.Audio           as A import qualified Data.Conduit.List            as CL import qualified Data.Vector.Storable         as V import           Foreign                      (Int16, Int32, Ptr, Word16,@@ -26,9 +28,6 @@ import           GHC.IO.Handle                (HandlePosn (..)) import qualified System.IO                    as IO import           System.IO.Unsafe             (unsafePerformIO)--import           Control.Monad.Trans.Resource (MonadResource)-import qualified Data.Conduit.Audio           as A  parseChunk :: IO.Handle -> IO (B.ByteString, (HandlePosn, HandlePosn)) parseChunk h = do
src/Sound/Jammit/Internal/Image.hs view
@@ -3,18 +3,17 @@ , jpegsToPDF ) where -import qualified Codec.Picture as P-import Codec.Picture.Types (convertImage)-import Control.Monad (forM_, replicateM)-import Control.Monad.IO.Class (MonadIO)-import Control.Monad.Trans.Class (lift)-import qualified Graphics.PDF as PDF-import qualified Data.ByteString.Lazy as BL-import qualified Data.Conduit as C-import Data.Maybe (catMaybes)-import qualified Data.Vector.Storable as V--import Sound.Jammit.Internal.TempIO+import qualified Codec.Picture                as P+import           Codec.Picture.Types          (convertImage)+import           Control.Monad                (forM_, replicateM)+import           Control.Monad.IO.Class       (MonadIO)+import           Control.Monad.Trans.Class    (lift)+import qualified Data.ByteString.Lazy         as BL+import qualified Data.Conduit                 as C+import           Data.Maybe                   (catMaybes)+import qualified Data.Vector.Storable         as V+import qualified Graphics.PDF                 as PDF+import           Sound.Jammit.Internal.TempIO  loadPNG :: FilePath -> IO (P.Image P.PixelRGB8) loadPNG fp = do
src/Sound/Jammit/Internal/PropertyList.hs view
@@ -1,8 +1,8 @@ -- | A very simple, non-robust property list parser.-{-# LANGUAGE CPP #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE CPP               #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE LambdaCase        #-}+{-# LANGUAGE RecordWildCards   #-} module Sound.Jammit.Internal.PropertyList ( readPropertyList , PropertyList(..)@@ -11,18 +11,18 @@ , fromLookup ) where -import qualified Data.Text.IO as TIO-import Text.XML.Light-import qualified Data.Map as Map-import Text.Read (readMaybe)-import Data.Maybe (mapMaybe, fromJust)-import Data.Char (isSpace)-import Control.Monad (guard)+import           Control.Monad       (guard)+import           Data.Char           (isSpace)+import qualified Data.Map            as Map+import           Data.Maybe          (fromJust, mapMaybe)+import qualified Data.Text.IO        as TIO+import           Text.Read           (readMaybe)+import           Text.XML.Light  #if !MIN_VERSION_base(4,8,0)-import Prelude hiding (mapM)-import Data.Traversable (mapM)-import Control.Applicative ((<$>))+import           Control.Applicative ((<$>))+import           Data.Traversable    (mapM)+import           Prelude             hiding (mapM) #endif  data PropertyList
src/Sound/Jammit/Internal/TempIO.hs view
@@ -8,15 +8,15 @@ , liftIO ) where -import Data.List (stripPrefix)-import System.IO (hClose)-import System.IO.Error (catchIOError)--import Control.Monad.IO.Class (liftIO)-import Control.Monad.Trans.Reader (ask, ReaderT(..))-import System.Directory (copyFile, renameFile)-import System.FilePath (splitPath)-import System.IO.Temp (openTempFile, withSystemTempDirectory)+import           Control.Monad.IO.Class     (liftIO)+import           Control.Monad.Trans.Reader (ReaderT (..), ask)+import           Data.List                  (stripPrefix)+import           System.Directory           (copyFile, renameFile)+import           System.FilePath            (splitPath)+import           System.IO                  (hClose)+import           System.IO.Error            (catchIOError)+import           System.IO.Temp             (openTempFile,+                                             withSystemTempDirectory)  -- | A wrapper around IO with a designated directory for temporary files. type TempIO = ReaderT FilePath IO