jammittools 0.5.0.1 → 0.5.0.2
raw patch · 3 files changed
+148/−65 lines, 3 filesdep ~HPDFdep ~jammittoolsPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: HPDF, jammittools
API changes (from Hackage documentation)
- Sound.Jammit.Base: album :: Info -> String
- Sound.Jammit.Base: artist :: Info -> String
- Sound.Jammit.Base: bpm :: Info -> String
- Sound.Jammit.Base: copyright :: Info -> String
- Sound.Jammit.Base: countInBeats :: Info -> Integer
- Sound.Jammit.Base: courtesyOf :: Info -> String
- Sound.Jammit.Base: demo :: Info -> Bool
- Sound.Jammit.Base: explicit :: Info -> Bool
- Sound.Jammit.Base: genre :: Info -> String
- Sound.Jammit.Base: identifier :: Track -> String
- Sound.Jammit.Base: instance Bounded Instrument
- Sound.Jammit.Base: instance Bounded Part
- Sound.Jammit.Base: instance Enum Instrument
- Sound.Jammit.Base: instance Enum Part
- Sound.Jammit.Base: instance Eq AudioPart
- Sound.Jammit.Base: instance Eq Beat
- Sound.Jammit.Base: instance Eq Info
- Sound.Jammit.Base: instance Eq Instrument
- Sound.Jammit.Base: instance Eq Part
- Sound.Jammit.Base: instance Eq Section
- Sound.Jammit.Base: instance Eq SheetPart
- Sound.Jammit.Base: instance Eq SkillLevel
- Sound.Jammit.Base: instance Eq Track
- Sound.Jammit.Base: instance Ord AudioPart
- Sound.Jammit.Base: instance Ord Beat
- Sound.Jammit.Base: instance Ord Info
- Sound.Jammit.Base: instance Ord Instrument
- Sound.Jammit.Base: instance Ord Part
- Sound.Jammit.Base: instance Ord Section
- Sound.Jammit.Base: instance Ord SheetPart
- Sound.Jammit.Base: instance Ord SkillLevel
- Sound.Jammit.Base: instance Ord Track
- Sound.Jammit.Base: instance PropertyListItem Beat
- Sound.Jammit.Base: instance PropertyListItem Info
- Sound.Jammit.Base: instance PropertyListItem Instrument
- Sound.Jammit.Base: instance PropertyListItem Section
- Sound.Jammit.Base: instance PropertyListItem SkillLevel
- Sound.Jammit.Base: instance PropertyListItem Track
- Sound.Jammit.Base: instance Read AudioPart
- Sound.Jammit.Base: instance Read Beat
- Sound.Jammit.Base: instance Read Info
- Sound.Jammit.Base: instance Read Instrument
- Sound.Jammit.Base: instance Read Part
- Sound.Jammit.Base: instance Read Section
- Sound.Jammit.Base: instance Read SheetPart
- Sound.Jammit.Base: instance Read SkillLevel
- Sound.Jammit.Base: instance Read Track
- Sound.Jammit.Base: instance Show AudioPart
- Sound.Jammit.Base: instance Show Beat
- Sound.Jammit.Base: instance Show Info
- Sound.Jammit.Base: instance Show Instrument
- Sound.Jammit.Base: instance Show Part
- Sound.Jammit.Base: instance Show Section
- Sound.Jammit.Base: instance Show SheetPart
- Sound.Jammit.Base: instance Show SkillLevel
- Sound.Jammit.Base: instance Show Track
- Sound.Jammit.Base: instrument :: Info -> Instrument
- Sound.Jammit.Base: isDownbeat :: Beat -> Bool
- Sound.Jammit.Base: isGhostBeat :: Beat -> Bool
- Sound.Jammit.Base: position :: Beat -> Double
- Sound.Jammit.Base: publishedBy :: Info -> String
- Sound.Jammit.Base: scoreSystemHeight :: Track -> Maybe Integer
- Sound.Jammit.Base: scoreSystemInterval :: Track -> Maybe Integer
- Sound.Jammit.Base: sectionBeat :: Section -> Integer
- Sound.Jammit.Base: sectionNumber :: Section -> Integer
- Sound.Jammit.Base: sectionType :: Section -> Integer
- Sound.Jammit.Base: skillLevel :: Info -> SkillLevel
- Sound.Jammit.Base: sku :: Info -> String
- Sound.Jammit.Base: slow :: Info -> Double
- Sound.Jammit.Base: title :: Info -> String
- Sound.Jammit.Base: trackClass :: Track -> String
- Sound.Jammit.Base: trackTitle :: Track -> Maybe String
- Sound.Jammit.Base: version :: Info -> Integer
- Sound.Jammit.Base: writtenBy :: Info -> String
+ Sound.Jammit.Base: [album] :: Info -> String
+ Sound.Jammit.Base: [artist] :: Info -> String
+ Sound.Jammit.Base: [bpm] :: Info -> String
+ Sound.Jammit.Base: [copyright] :: Info -> String
+ Sound.Jammit.Base: [countInBeats] :: Info -> Integer
+ Sound.Jammit.Base: [courtesyOf] :: Info -> String
+ Sound.Jammit.Base: [demo] :: Info -> Bool
+ Sound.Jammit.Base: [explicit] :: Info -> Bool
+ Sound.Jammit.Base: [genre] :: Info -> String
+ Sound.Jammit.Base: [identifier] :: Track -> String
+ Sound.Jammit.Base: [instrument] :: Info -> Instrument
+ Sound.Jammit.Base: [isDownbeat] :: Beat -> Bool
+ Sound.Jammit.Base: [isGhostBeat] :: Beat -> Bool
+ Sound.Jammit.Base: [position] :: Beat -> Double
+ Sound.Jammit.Base: [publishedBy] :: Info -> String
+ Sound.Jammit.Base: [scoreSystemHeight] :: Track -> Maybe Integer
+ Sound.Jammit.Base: [scoreSystemInterval] :: Track -> Maybe Integer
+ Sound.Jammit.Base: [sectionBeat] :: Section -> Integer
+ Sound.Jammit.Base: [sectionNumber] :: Section -> Integer
+ Sound.Jammit.Base: [sectionType] :: Section -> Integer
+ Sound.Jammit.Base: [skillLevel] :: Info -> SkillLevel
+ Sound.Jammit.Base: [sku] :: Info -> String
+ Sound.Jammit.Base: [slow] :: Info -> Double
+ Sound.Jammit.Base: [title] :: Info -> String
+ Sound.Jammit.Base: [trackClass] :: Track -> String
+ Sound.Jammit.Base: [trackTitle] :: Track -> Maybe String
+ Sound.Jammit.Base: [version] :: Info -> Integer
+ Sound.Jammit.Base: [writtenBy] :: Info -> String
+ Sound.Jammit.Base: instance GHC.Classes.Eq Sound.Jammit.Base.AudioPart
+ Sound.Jammit.Base: instance GHC.Classes.Eq Sound.Jammit.Base.Beat
+ Sound.Jammit.Base: instance GHC.Classes.Eq Sound.Jammit.Base.Info
+ Sound.Jammit.Base: instance GHC.Classes.Eq Sound.Jammit.Base.Instrument
+ Sound.Jammit.Base: instance GHC.Classes.Eq Sound.Jammit.Base.Part
+ Sound.Jammit.Base: instance GHC.Classes.Eq Sound.Jammit.Base.Section
+ Sound.Jammit.Base: instance GHC.Classes.Eq Sound.Jammit.Base.SheetPart
+ Sound.Jammit.Base: instance GHC.Classes.Eq Sound.Jammit.Base.SkillLevel
+ Sound.Jammit.Base: instance GHC.Classes.Eq Sound.Jammit.Base.Track
+ Sound.Jammit.Base: instance GHC.Classes.Ord Sound.Jammit.Base.AudioPart
+ Sound.Jammit.Base: instance GHC.Classes.Ord Sound.Jammit.Base.Beat
+ Sound.Jammit.Base: instance GHC.Classes.Ord Sound.Jammit.Base.Info
+ Sound.Jammit.Base: instance GHC.Classes.Ord Sound.Jammit.Base.Instrument
+ Sound.Jammit.Base: instance GHC.Classes.Ord Sound.Jammit.Base.Part
+ Sound.Jammit.Base: instance GHC.Classes.Ord Sound.Jammit.Base.Section
+ Sound.Jammit.Base: instance GHC.Classes.Ord Sound.Jammit.Base.SheetPart
+ Sound.Jammit.Base: instance GHC.Classes.Ord Sound.Jammit.Base.SkillLevel
+ Sound.Jammit.Base: instance GHC.Classes.Ord Sound.Jammit.Base.Track
+ Sound.Jammit.Base: instance GHC.Enum.Bounded Sound.Jammit.Base.Instrument
+ Sound.Jammit.Base: instance GHC.Enum.Bounded Sound.Jammit.Base.Part
+ Sound.Jammit.Base: instance GHC.Enum.Enum Sound.Jammit.Base.Instrument
+ Sound.Jammit.Base: instance GHC.Enum.Enum Sound.Jammit.Base.Part
+ Sound.Jammit.Base: instance GHC.Read.Read Sound.Jammit.Base.AudioPart
+ Sound.Jammit.Base: instance GHC.Read.Read Sound.Jammit.Base.Beat
+ Sound.Jammit.Base: instance GHC.Read.Read Sound.Jammit.Base.Info
+ Sound.Jammit.Base: instance GHC.Read.Read Sound.Jammit.Base.Instrument
+ Sound.Jammit.Base: instance GHC.Read.Read Sound.Jammit.Base.Part
+ Sound.Jammit.Base: instance GHC.Read.Read Sound.Jammit.Base.Section
+ Sound.Jammit.Base: instance GHC.Read.Read Sound.Jammit.Base.SheetPart
+ Sound.Jammit.Base: instance GHC.Read.Read Sound.Jammit.Base.SkillLevel
+ Sound.Jammit.Base: instance GHC.Read.Read Sound.Jammit.Base.Track
+ Sound.Jammit.Base: instance GHC.Show.Show Sound.Jammit.Base.AudioPart
+ Sound.Jammit.Base: instance GHC.Show.Show Sound.Jammit.Base.Beat
+ Sound.Jammit.Base: instance GHC.Show.Show Sound.Jammit.Base.Info
+ Sound.Jammit.Base: instance GHC.Show.Show Sound.Jammit.Base.Instrument
+ Sound.Jammit.Base: instance GHC.Show.Show Sound.Jammit.Base.Part
+ Sound.Jammit.Base: instance GHC.Show.Show Sound.Jammit.Base.Section
+ Sound.Jammit.Base: instance GHC.Show.Show Sound.Jammit.Base.SheetPart
+ Sound.Jammit.Base: instance GHC.Show.Show Sound.Jammit.Base.SkillLevel
+ Sound.Jammit.Base: instance GHC.Show.Show Sound.Jammit.Base.Track
+ Sound.Jammit.Base: instance Sound.Jammit.Internal.PropertyList.PropertyListItem Sound.Jammit.Base.Beat
+ Sound.Jammit.Base: instance Sound.Jammit.Internal.PropertyList.PropertyListItem Sound.Jammit.Base.Info
+ Sound.Jammit.Base: instance Sound.Jammit.Internal.PropertyList.PropertyListItem Sound.Jammit.Base.Instrument
+ Sound.Jammit.Base: instance Sound.Jammit.Internal.PropertyList.PropertyListItem Sound.Jammit.Base.Section
+ Sound.Jammit.Base: instance Sound.Jammit.Internal.PropertyList.PropertyListItem Sound.Jammit.Base.SkillLevel
+ Sound.Jammit.Base: instance Sound.Jammit.Internal.PropertyList.PropertyListItem Sound.Jammit.Base.Track
- Sound.Jammit.Base: sheetHeight :: Num a => a
+ Sound.Jammit.Base: sheetHeight :: (Num a) => a
- Sound.Jammit.Base: sheetWidth :: Num a => a
+ Sound.Jammit.Base: sheetWidth :: (Num a) => a
- Sound.Jammit.Export: audioSource :: MonadResource m => FilePath -> IO (AudioSource m Int16)
+ Sound.Jammit.Export: audioSource :: (MonadResource m) => FilePath -> IO (AudioSource m Int16)
Files
- cbits/ima4.c +115/−0
- jammittools.cabal +5/−4
- src/Sound/Jammit/Internal/Audio.hs +28/−61
+ cbits/ima4.c view
@@ -0,0 +1,115 @@+static int index_table[16] = {+ -1, -1, -1, -1, 2, 4, 6, 8,+ -1, -1, -1, -1, 2, 4, 6, 8,+};++static int step_table[89] = {+ 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,+ 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,+ 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,+ 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,+ 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,+ 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,+ 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,+ 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,+ 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767+};++/* parse a chunk - waits a 34 bytes buffer, returns a little endian buffer */+short decode_chunk(unsigned char *input, unsigned char *output, short predictor)+{+ char d;+ int i;+ short step_index;+ int hnb, lnb;+ int diff;+ int sign, delta;+ short step;++ d = *input++;+ // predictor=d << 8;+ d = *input++;+ step_index = d & 127;+ // predictor |= d&128;++ /* is this necessary ? */+ if (step_index < 0) step_index = 0;+ if (step_index > 88) step_index = 88;++ step = step_table[step_index];++ for (i=0; i<32; i++, input++) {+ d = *input;+ hnb = (d>>4) & 15;+ lnb = d & 15;++ /* decode lnb */+ step_index += index_table[lnb];+ /* necessary ? */+ if (step_index<0) step_index = 0;+ if (step_index>88) step_index = 88;++ sign = lnb & 8;+ delta = lnb & 7;+ diff = step >> 3;+ if (delta & 4) diff += step;+ if (delta & 2) diff += step >> 1;+ if (delta & 1) diff += step >> 2;+ /* take care of clamping (Adam Sampson <ats@offog.org>) */+ if (sign) {+ int p = ((int) predictor) - diff;+ if (p < -32768)+ predictor = -32768;+ else+ predictor = p;+ } else {+ int p = ((int) predictor) + diff;+ if (p > 32767)+ predictor = 32767;+ else+ predictor = p;+ }++ *output = predictor&255;+ output++;+ *output = (predictor>>8)&255;+ output++;++ step = step_table[step_index];++ /* decode hnb */+ step_index += index_table[hnb];+ if (step_index<0) step_index = 0;+ if (step_index>88) step_index = 88;++ sign = hnb & 8;+ delta = hnb & 7;+ diff = step >> 3;+ if (delta & 4) diff += step;+ if (delta & 2) diff += step >> 1;+ if (delta & 1) diff += step >> 2;+ /* take care of clamping (Adam Sampson <ats@offog.org>) */+ if (sign) {+ int p = ((int) predictor) - diff;+ if (p < -32768)+ predictor = -32768;+ else+ predictor = p;+ } else {+ int p = ((int) predictor) + diff;+ if (p > 32767)+ predictor = 32767;+ else+ predictor = p;+ }++ *output = predictor&255;+ output++;+ *output = (predictor>>8)&255;+ output++;++ step = step_table[step_index];+ }++ return predictor;+}
jammittools.cabal view
@@ -1,5 +1,5 @@ Name: jammittools-Version: 0.5.0.1+Version: 0.5.0.2 Synopsis: Export sheet music and audio from Windows/Mac app Jammit Description: @@ -43,14 +43,15 @@ , temporary >= 1.1.2.5 && < 1.3 , transformers >= 0.3.0.0 && < 0.5 , JuicyPixels >= 3.2.2 && < 3.3- , HPDF >= 1.4.6 && < 1.5+ , HPDF >= 1.4.7 && < 1.5 , bytestring >= 0.10.4.0 && < 0.11 , conduit >= 1.2.3.1 && < 1.3 , vector >= 0.10.12.2 && < 0.11 - , conduit-audio >= 0.1 && < 0.2+ , conduit-audio >= 0.1 && < 0.3 , resourcet ghc-options: -Wall -O2+ c-sources: cbits/ima4.c executable jammittools main-is: Main.hs@@ -61,7 +62,7 @@ , directory >= 1.2.0.1 && < 1.3 , filepath >= 1.3.0.1 && < 1.5 , boxes >= 0.1.3 && < 0.2- , jammittools == 0.5.0.1+ , jammittools == 0.5.0.2 ghc-options: -Wall -O2 source-repository head
src/Sound/Jammit/Internal/Audio.hs view
@@ -2,30 +2,32 @@ AIFC\/IMA audio decoding functions in this module are ported from http://sed.free.fr/aifc2wav.html -}-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE OverloadedStrings #-} module Sound.Jammit.Internal.Audio ( readIMA , writeWAV , clamp ) where -import qualified Data.Conduit as C-import qualified Data.Conduit.List as CL-import qualified Data.Vector.Storable as V-import Data.Int (Int16, Int32)-import Data.Word (Word16, Word32)-import Control.Monad.IO.Class (liftIO)-import qualified Data.ByteString as B-import Data.ByteString.Char8 () -- for IsString instance-import qualified System.IO as IO-import GHC.IO.Handle (HandlePosn(..))-import Data.Bits (shiftL, shiftR, (.&.))-import Control.Monad (unless, forM, liftM2)-import Control.Monad.ST (runST)-import Data.STRef (newSTRef, readSTRef, modifySTRef)+import Control.Monad (liftM2, unless)+import Control.Monad.IO.Class (liftIO)+import qualified Data.ByteString as B+import Data.ByteString.Char8 () -- for IsString instance+import qualified Data.ByteString.Unsafe as B+import qualified Data.Conduit as C+import qualified Data.Conduit.List as CL+import qualified Data.Vector.Storable as V+import Foreign (Int16, Int32, Ptr, Word16,+ Word32, Word8, castPtr,+ finalizerFree, mallocBytes,+ newForeignPtr, shiftL, shiftR)+import GHC.IO.Handle (HandlePosn (..))+import qualified System.IO as IO+import System.IO.Unsafe (unsafePerformIO) -import qualified Data.Conduit.Audio as A-import Control.Monad.Trans.Resource (MonadResource)+import Control.Monad.Trans.Resource (MonadResource)+import qualified Data.Conduit.Audio as A parseChunk :: IO.Handle -> IO (B.ByteString, (HandlePosn, HandlePosn)) parseChunk h = do@@ -97,57 +99,22 @@ go 0 0 frames return $ A.AudioSource src 44100 2 $ fromIntegral frames +foreign import ccall unsafe "decode_chunk"+ c_decodeChunk :: Ptr Word8 -> Ptr Word8 -> Int16 -> IO Int16+ decodeChunk :: (Int16, B.ByteString) -> (Int16, V.Vector Int16)-decodeChunk (initPredictor, chunk) = runST $ do- predictor <- newSTRef initPredictor- stepIndex <- newSTRef (fromIntegral (B.index chunk 1) .&. 127 :: Int16)- let step = fmap (\si -> stepTable V.! fromIntegral si) $ readSTRef stepIndex- v <- fmap (V.fromList . concat) $ forM (B.unpack $ B.drop 2 chunk) $ \d -> do- let hnb = fromIntegral $ d `shiftR` 4 :: Int32- lnb = fromIntegral $ d .&. 15 :: Int32- forM [lnb, hnb] $ \nb -> do- thisStep <- step- let sign = nb .&. 8- diff = sum- [ thisStep `shiftR` 3- , if nb .&. 4 /= 0 then thisStep else 0- , if nb .&. 2 /= 0 then thisStep `shiftR` 1 else 0- , if nb .&. 1 /= 0 then thisStep `shiftR` 2 else 0- ]- modifySTRef predictor $ \old -> let- op = if sign /= 0 then (-) else (+)- new = fromIntegral old `op` fromIntegral diff :: Int32- in fromIntegral $ clamp (-32768, 32767) new- modifySTRef stepIndex $ \old ->- clamp (0, 88) $ old + (indexTable V.! fromIntegral nb)- readSTRef predictor- finalPredictor <- readSTRef predictor- return (finalPredictor, v)+decodeChunk (initPredictor, chunk) = unsafePerformIO $ do+ B.unsafeUseAsCString chunk $ \cstr -> do+ p <- mallocBytes 128+ lastPredictor <- c_decodeChunk (castPtr cstr) p initPredictor+ fp <- newForeignPtr finalizerFree $ castPtr p+ return (lastPredictor, V.unsafeFromForeignPtr0 fp 64) clamp :: (Ord a) => (a, a) -> a -> a clamp (vmin, vmax) v | v < vmin = vmin | v > vmax = vmax | otherwise = v--indexTable :: V.Vector Int16-indexTable = V.fromList- [ -1, -1, -1, -1, 2, 4, 6, 8- , -1, -1, -1, -1, 2, 4, 6, 8- ]--stepTable :: V.Vector Int16-stepTable = V.fromList- [ 7, 8, 9, 10, 11, 12, 13, 14, 16, 17- , 19, 21, 23, 25, 28, 31, 34, 37, 41, 45- , 50, 55, 60, 66, 73, 80, 88, 97, 107, 118- , 130, 143, 157, 173, 190, 209, 230, 253, 279, 307- , 337, 371, 408, 449, 494, 544, 598, 658, 724, 796- , 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066- , 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358- , 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899- , 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767- ] writeWAV :: (MonadResource m) => FilePath -> A.AudioSource m Int16 -> m () writeWAV fp (A.AudioSource s r c _) = s C.$$ C.bracketP