module Termtab.Parser.GP5.Internal (
GP5ScoreInfo (..),
GP5MeasureHeader (..),
GP5Track (..),
GP5Note (..),
GP5Beat (..),
GP5Bar (..),
getVersion,
getScoreInfo,
skipLyrics,
skipPageSetup,
skipRSEMaster,
getTempoName,
getChannels,
getMeasureHeader,
getTrack,
getBeat,
getBar,
getInt8',
getInt32le',
getU8,
getByteSizeString,
getIntByteSizeString,
getIntSizeString,
) where
import Control.Monad (replicateM, replicateM_, when)
import Data.Binary.Get
import Data.Bits (testBit)
import Data.ByteString qualified as BS
import Data.Text qualified as T
import Data.Text.Encoding qualified as TE
data GP5ScoreInfo = GP5ScoreInfo
{ GP5ScoreInfo -> Text
gsiTitle :: T.Text
, GP5ScoreInfo -> Text
gsiSubtitle :: T.Text
, GP5ScoreInfo -> Text
gsiArtist :: T.Text
, GP5ScoreInfo -> Text
gsiAlbum :: T.Text
, GP5ScoreInfo -> Text
gsiLyricist :: T.Text
, GP5ScoreInfo -> Text
gsiComposer :: T.Text
}
deriving (Int -> GP5ScoreInfo -> ShowS
[GP5ScoreInfo] -> ShowS
GP5ScoreInfo -> String
(Int -> GP5ScoreInfo -> ShowS)
-> (GP5ScoreInfo -> String)
-> ([GP5ScoreInfo] -> ShowS)
-> Show GP5ScoreInfo
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> GP5ScoreInfo -> ShowS
showsPrec :: Int -> GP5ScoreInfo -> ShowS
$cshow :: GP5ScoreInfo -> String
show :: GP5ScoreInfo -> String
$cshowList :: [GP5ScoreInfo] -> ShowS
showList :: [GP5ScoreInfo] -> ShowS
Show, GP5ScoreInfo -> GP5ScoreInfo -> Bool
(GP5ScoreInfo -> GP5ScoreInfo -> Bool)
-> (GP5ScoreInfo -> GP5ScoreInfo -> Bool) -> Eq GP5ScoreInfo
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: GP5ScoreInfo -> GP5ScoreInfo -> Bool
== :: GP5ScoreInfo -> GP5ScoreInfo -> Bool
$c/= :: GP5ScoreInfo -> GP5ScoreInfo -> Bool
/= :: GP5ScoreInfo -> GP5ScoreInfo -> Bool
Eq)
data =
{ :: Int
, GP5MeasureHeader -> Int
gmhTimeDen :: Int
, GP5MeasureHeader -> Int
gmhKeyRoot :: Int
, GP5MeasureHeader -> Int
gmhKeyMode :: Int
}
deriving (Int -> GP5MeasureHeader -> ShowS
[GP5MeasureHeader] -> ShowS
GP5MeasureHeader -> String
(Int -> GP5MeasureHeader -> ShowS)
-> (GP5MeasureHeader -> String)
-> ([GP5MeasureHeader] -> ShowS)
-> Show GP5MeasureHeader
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> GP5MeasureHeader -> ShowS
showsPrec :: Int -> GP5MeasureHeader -> ShowS
$cshow :: GP5MeasureHeader -> String
show :: GP5MeasureHeader -> String
$cshowList :: [GP5MeasureHeader] -> ShowS
showList :: [GP5MeasureHeader] -> ShowS
Show, GP5MeasureHeader -> GP5MeasureHeader -> Bool
(GP5MeasureHeader -> GP5MeasureHeader -> Bool)
-> (GP5MeasureHeader -> GP5MeasureHeader -> Bool)
-> Eq GP5MeasureHeader
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: GP5MeasureHeader -> GP5MeasureHeader -> Bool
== :: GP5MeasureHeader -> GP5MeasureHeader -> Bool
$c/= :: GP5MeasureHeader -> GP5MeasureHeader -> Bool
/= :: GP5MeasureHeader -> GP5MeasureHeader -> Bool
Eq)
data GP5Track = GP5Track
{ GP5Track -> Text
gtName :: T.Text
, GP5Track -> [Int]
gtStrings :: [Int]
, GP5Track -> Int
gtChannel :: Int
, GP5Track -> Int
gtProgram :: Int
, GP5Track -> Bool
gtIsDrums :: Bool
}
deriving (Int -> GP5Track -> ShowS
[GP5Track] -> ShowS
GP5Track -> String
(Int -> GP5Track -> ShowS)
-> (GP5Track -> String) -> ([GP5Track] -> ShowS) -> Show GP5Track
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> GP5Track -> ShowS
showsPrec :: Int -> GP5Track -> ShowS
$cshow :: GP5Track -> String
show :: GP5Track -> String
$cshowList :: [GP5Track] -> ShowS
showList :: [GP5Track] -> ShowS
Show, GP5Track -> GP5Track -> Bool
(GP5Track -> GP5Track -> Bool)
-> (GP5Track -> GP5Track -> Bool) -> Eq GP5Track
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: GP5Track -> GP5Track -> Bool
== :: GP5Track -> GP5Track -> Bool
$c/= :: GP5Track -> GP5Track -> Bool
/= :: GP5Track -> GP5Track -> Bool
Eq)
data GP5Note = GP5Note
{ GP5Note -> Int
gnString :: Int
, GP5Note -> Int
gnFret :: Int
, GP5Note -> Int
gnDynamic :: Int
}
deriving (Int -> GP5Note -> ShowS
[GP5Note] -> ShowS
GP5Note -> String
(Int -> GP5Note -> ShowS)
-> (GP5Note -> String) -> ([GP5Note] -> ShowS) -> Show GP5Note
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> GP5Note -> ShowS
showsPrec :: Int -> GP5Note -> ShowS
$cshow :: GP5Note -> String
show :: GP5Note -> String
$cshowList :: [GP5Note] -> ShowS
showList :: [GP5Note] -> ShowS
Show, GP5Note -> GP5Note -> Bool
(GP5Note -> GP5Note -> Bool)
-> (GP5Note -> GP5Note -> Bool) -> Eq GP5Note
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: GP5Note -> GP5Note -> Bool
== :: GP5Note -> GP5Note -> Bool
$c/= :: GP5Note -> GP5Note -> Bool
/= :: GP5Note -> GP5Note -> Bool
Eq)
data GP5Beat = GP5Beat
{ GP5Beat -> Int
gbDuration :: Int
, GP5Beat -> Bool
gbIsRest :: Bool
, GP5Beat -> [GP5Note]
gbNotes :: [GP5Note]
}
deriving (Int -> GP5Beat -> ShowS
[GP5Beat] -> ShowS
GP5Beat -> String
(Int -> GP5Beat -> ShowS)
-> (GP5Beat -> String) -> ([GP5Beat] -> ShowS) -> Show GP5Beat
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> GP5Beat -> ShowS
showsPrec :: Int -> GP5Beat -> ShowS
$cshow :: GP5Beat -> String
show :: GP5Beat -> String
$cshowList :: [GP5Beat] -> ShowS
showList :: [GP5Beat] -> ShowS
Show, GP5Beat -> GP5Beat -> Bool
(GP5Beat -> GP5Beat -> Bool)
-> (GP5Beat -> GP5Beat -> Bool) -> Eq GP5Beat
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: GP5Beat -> GP5Beat -> Bool
== :: GP5Beat -> GP5Beat -> Bool
$c/= :: GP5Beat -> GP5Beat -> Bool
/= :: GP5Beat -> GP5Beat -> Bool
Eq)
data GP5Bar = GP5Bar
{ GP5Bar -> [GP5Beat]
gbarBeats :: [GP5Beat]
}
deriving (Int -> GP5Bar -> ShowS
[GP5Bar] -> ShowS
GP5Bar -> String
(Int -> GP5Bar -> ShowS)
-> (GP5Bar -> String) -> ([GP5Bar] -> ShowS) -> Show GP5Bar
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> GP5Bar -> ShowS
showsPrec :: Int -> GP5Bar -> ShowS
$cshow :: GP5Bar -> String
show :: GP5Bar -> String
$cshowList :: [GP5Bar] -> ShowS
showList :: [GP5Bar] -> ShowS
Show, GP5Bar -> GP5Bar -> Bool
(GP5Bar -> GP5Bar -> Bool)
-> (GP5Bar -> GP5Bar -> Bool) -> Eq GP5Bar
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: GP5Bar -> GP5Bar -> Bool
== :: GP5Bar -> GP5Bar -> Bool
$c/= :: GP5Bar -> GP5Bar -> Bool
/= :: GP5Bar -> GP5Bar -> Bool
Eq)
getU8 :: Get Int
getU8 :: Get Int
getU8 = Word8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word8 -> Int) -> Get Word8 -> Get Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Word8
getWord8
getInt8' :: Get Int
getInt8' :: Get Int
getInt8' = do
b <- Get Word8
getWord8
let n = Word8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
b :: Int
return (if n >= 128 then n - 256 else n)
getInt32le' :: Get Int
getInt32le' :: Get Int
getInt32le' = Int32 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int32 -> Int) -> Get Int32 -> Get Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Int32
getInt32le
getByteSizeString :: Int -> Get T.Text
getByteSizeString :: Int -> Get Text
getByteSizeString Int
fieldLen = do
strLen <- Get Int
getU8
raw <- getByteString fieldLen
return (TE.decodeUtf8 (BS.take strLen raw))
getIntByteSizeString :: Get T.Text
getIntByteSizeString :: Get Text
getIntByteSizeString = do
_total <- Get Int
getInt32le'
strLen <- getU8
raw <- getByteString strLen
return (TE.decodeUtf8 raw)
getIntSizeString :: Get T.Text
getIntSizeString :: Get Text
getIntSizeString = do
len <- Get Int
getInt32le'
raw <- getByteString len
return (TE.decodeUtf8 raw)
getVersion :: Get (Int, Int)
getVersion :: Get (Int, Int)
getVersion = do
raw <- Int -> Get Text
getByteSizeString Int
30
let s = Text -> String
T.unpack Text
raw
case parseVersionSuffix s of
Just (Int, Int)
v -> (Int, Int) -> Get (Int, Int)
forall a. a -> Get a
forall (m :: * -> *) a. Monad m => a -> m a
return (Int, Int)
v
Maybe (Int, Int)
Nothing -> String -> Get (Int, Int)
forall a. String -> Get a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String
"Unrecognised GP version string: " String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
s)
parseVersionSuffix :: String -> Maybe (Int, Int)
parseVersionSuffix :: String -> Maybe (Int, Int)
parseVersionSuffix String
s =
case [String] -> [String]
forall a. [a] -> [a]
reverse (String -> [String]
words String
s) of
(String
verStr : [String]
_) ->
let stripped :: String
stripped = case String
verStr of Char
'v' : String
rest -> String
rest; String
_ -> String
verStr
in case (Char -> Bool) -> String -> (String, String)
forall a. (a -> Bool) -> [a] -> ([a], [a])
break (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'.') String
stripped of
(String
majS, Char
'.' : String
minS) -> case (ReadS Int
forall a. Read a => ReadS a
reads String
majS, ReadS Int
forall a. Read a => ReadS a
reads String
minS) of
([(Int
maj, String
"")], [(Int
mn, String
"")]) -> (Int, Int) -> Maybe (Int, Int)
forall a. a -> Maybe a
Just (Int
maj, Int
mn)
([(Int, String)], [(Int, String)])
_ -> Maybe (Int, Int)
forall a. Maybe a
Nothing
(String, String)
_ -> case ReadS Int
forall a. Read a => ReadS a
reads String
stripped of
[(Int
maj, String
"")] -> (Int, Int) -> Maybe (Int, Int)
forall a. a -> Maybe a
Just (Int
maj, Int
0)
[(Int, String)]
_ -> Maybe (Int, Int)
forall a. Maybe a
Nothing
[] -> Maybe (Int, Int)
forall a. Maybe a
Nothing
getScoreInfo :: Get GP5ScoreInfo
getScoreInfo :: Get GP5ScoreInfo
getScoreInfo = do
title <- Get Text
getIntByteSizeString
subtitle <- getIntByteSizeString
artist <- getIntByteSizeString
album <- getIntByteSizeString
lyricist <- getIntByteSizeString
composer <- getIntByteSizeString
_copy1 <- getIntByteSizeString
_copy2 <- getIntByteSizeString
_tabAuthor <- getIntByteSizeString
_instr <- getIntByteSizeString
noticeCount <- getInt32le'
replicateM_ noticeCount getIntByteSizeString
return
GP5ScoreInfo
{ gsiTitle = title
, gsiSubtitle = subtitle
, gsiArtist = artist
, gsiAlbum = album
, gsiLyricist = lyricist
, gsiComposer = composer
}
skipLyrics :: Get ()
skipLyrics :: Get ()
skipLyrics = do
_ <- Get Int
getInt32le'
replicateM_ 5 $ do
_ <- getInt32le'
_ <- getIntSizeString
return ()
skipPageSetup :: Get ()
skipPageSetup :: Get ()
skipPageSetup = do
Int -> Get ()
skip Int
36
Int -> Get Text -> Get ()
forall (m :: * -> *) a. Applicative m => Int -> m a -> m ()
replicateM_ Int
11 Get Text
getIntByteSizeString
skipRSEMaster :: Int -> Get ()
skipRSEMaster :: Int -> Get ()
skipRSEMaster Int
minor = Bool -> Get () -> Get ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Int
minor Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
10) (Int -> Get ()
skip Int
19)
getTempoName :: Get T.Text
getTempoName :: Get Text
getTempoName = Get Text
getIntByteSizeString
getChannels :: Get [Int]
getChannels :: Get [Int]
getChannels = Int -> Get Int -> Get [Int]
forall (m :: * -> *) a. Applicative m => Int -> m a -> m [a]
replicateM Int
64 Get Int
readChannel
where
readChannel :: Get Int
readChannel = do
program <- Get Int
getInt32le'
skip 7
return program
getMeasureHeader :: Int -> (Int, Int) -> (Int, Int) -> Get GP5MeasureHeader
Int
idx (Int
prevNum, Int
prevDen) (Int
prevRoot, Int
prevMode) = do
flags <- Get Int
getU8
(timeNum, timeDen) <-
if flags `testBit` 0
then do
n <- getU8
d <- getU8
return (n, d)
else return (prevNum, prevDen)
when (flags `testBit` 2) (skip 1)
when (flags `testBit` 3) (skip 1)
(keyRoot, keyMode) <-
if flags `testBit` 4
then do
r <- getInt8'
m <- getU8
return (r, m)
else return (prevRoot, prevMode)
when (idx == 0) (skip 1)
return
GP5MeasureHeader
{ gmhTimeNum = timeNum
, gmhTimeDen = timeDen
, gmhKeyRoot = keyRoot
, gmhKeyMode = keyMode
}
getTrack :: [Int] -> Get GP5Track
getTrack :: [Int] -> Get GP5Track
getTrack [Int]
programs = do
flags <- Get Int
getU8
name <- getByteSizeString 40
strCount <- getInt32le'
allPitches <- replicateM 7 getInt32le'
let pitches = Int -> [Int] -> [Int]
forall a. Int -> [a] -> [a]
take Int
strCount [Int]
allPitches
_port <- getInt32le'
chanIdx <- getInt32le'
_chanFx <- getInt32le'
_frets <- getInt32le'
_height <- getInt32le'
skip 4
let prog =
if Int
chanIdx Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
1 Bool -> Bool -> Bool
&& Int
chanIdx Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= [Int] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Int]
programs
then [Int]
programs [Int] -> Int -> Int
forall a. HasCallStack => [a] -> Int -> a
!! (Int
chanIdx Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)
else Int
0
return
GP5Track
{ gtName = name
, gtStrings = pitches
, gtChannel = chanIdx
, gtProgram = prog
, gtIsDrums = flags `testBit` 0
}
getBeat :: Get GP5Beat
getBeat :: Get GP5Beat
getBeat = do
flags <- Get Int
getU8
isRest <-
if flags `testBit` 6
then (\Int
s -> Int
s Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0x02) <$> getU8
else return False
durRaw <- getInt8'
when (flags `testBit` 5) (skip 1)
when (flags `testBit` 1) skipChord
when (flags `testBit` 2) (getIntSizeString >> return ())
when (flags `testBit` 3) skipBeatEffects
when (flags `testBit` 4) skipMixTable
stringMask <- getU8
let strNums = [Int
s | Int
s <- [Int
1 .. Int
7], Int
stringMask Int -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
`testBit` (Int
s Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)]
notes <- mapM getNote strNums
return
GP5Beat
{ gbDuration = durRaw
, gbIsRest = isRest
, gbNotes = notes
}
getNote :: Int -> Get GP5Note
getNote :: Int -> Get GP5Note
getNote Int
strNum = do
flags <- Get Int
getU8
_strIdx <- getU8
fret <- getU8
dynamic <- getU8
when (flags `testBit` 3) skipBend
when (flags `testBit` 4) (skip 4)
return
GP5Note
{ gnString = strNum
, gnFret = fret
, gnDynamic = fromIntegral dynamic
}
getBar :: Get GP5Bar
getBar :: Get GP5Bar
getBar = do
voice0 <- Get [GP5Beat]
getVoice
_voice1 <- getVoice
return GP5Bar{gbarBeats = voice0}
getVoice :: Get [GP5Beat]
getVoice :: Get [GP5Beat]
getVoice = do
beatCount <- Get Int
getInt32le'
replicateM beatCount getBeat
skipChord :: Get ()
skipChord :: Get ()
skipChord = do
newFmt <- Get Int
getU8
if newFmt == 1
then do
skip 16
_ <- getByteSizeString 20
skip 4
replicateM_ 7 (skip 4)
skip 32
else do
_ <- getByteSizeString 8
skip 2
replicateM_ 6 (getIntByteSizeString >> return ())
skipBeatEffects :: Get ()
skipBeatEffects :: Get ()
skipBeatEffects = do
flags1 <- Get Int
getU8
flags2 <- getU8
when (flags1 `testBit` 0) skipBend
when (flags1 `testBit` 1) (skip 1)
when (flags1 `testBit` 2) (skip 2)
when (flags2 `testBit` 5) (skip 1)
when (flags2 `testBit` 6) (skip 1)
when (flags2 `testBit` 7) skipHarmonic
skipMixTable :: Get ()
skipMixTable :: Get ()
skipMixTable = do
instrument <- Get Int
getInt8'
skip 16
volume <- getInt8'
balance <- getInt8'
chorus <- getInt8'
reverb <- getInt8'
phaser <- getInt8'
tremolo <- getInt8'
_tempoStr <- getIntByteSizeString
tempo <- getInt32le'
when (instrument >= 0) (skip 1)
when (volume >= 0) (skip 1)
when (balance >= 0) (skip 1)
when (chorus >= 0) (skip 1)
when (reverb >= 0) (skip 1)
when (phaser >= 0) (skip 1)
when (tremolo >= 0) (skip 1)
when (tempo >= 0) (skip 1)
skip 1
skipBend :: Get ()
skipBend :: Get ()
skipBend = do
Int -> Get ()
skip Int
5
pointCount <- Get Int
getInt32le'
skip (pointCount * 11)
skipHarmonic :: Get ()
skipHarmonic :: Get ()
skipHarmonic = do
htype <- Get Int
getU8
case htype of
Int
2 -> Int -> Get ()
skip Int
3
Int
3 -> Int -> Get ()
skip Int
1
Int
_ -> () -> Get ()
forall a. a -> Get a
forall (m :: * -> *) a. Monad m => a -> m a
return ()