-- | Byte functions.
module Music.Theory.Byte where

import Control.Monad.ST {- base -}
import Data.Char {- base -}
import Data.Maybe {- base -}
import Data.Word {- base -}
import Numeric {- base -}

import Data.Array.ST {- array -}
import Data.Array.Unsafe {- array -}

import qualified Data.ByteString as B {- bytestring -}
import qualified Data.List.Split as Split {- split -}

import qualified Music.Theory.Math.Convert as T {- hmt-base -}
import qualified Music.Theory.Read as T {- hmt-base -}

{-
import Data.Int {- base -}
import qualified Data.ByteString.Lazy as L {- bytestring -}

-- * LBS

-- | Section function for 'L.ByteString', ie. from (n,m).
--
-- > lbs_slice 4 5 (L.pack [1..10]) == L.pack [5,6,7,8,9]
lbs_slice :: Int64 -> Int64 -> L.ByteString -> L.ByteString
lbs_slice n m = L.take m . L.drop n

-- | Variant of slice with start and end indices (zero-indexed).
--
-- > lbs_section 4 8 (L.pack [1..]) == L.pack [5,6,7,8,9]
lbs_section :: Int64 -> Int64 -> L.ByteString -> L.ByteString
lbs_section l r = L.take (r - l + 1) . L.drop l
-}

-- * Enumerations & Char

-- | 'toEnum' of 'T.word8_to_int'
word8_to_enum :: Enum e => Word8 -> e
word8_to_enum :: forall e. Enum e => Word8 -> e
word8_to_enum = forall a. Enum a => Int -> a
toEnum forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word8 -> Int
T.word8_to_int

-- | 'T.int_to_word8_maybe' of 'fromEnum'
enum_to_word8 :: Enum e => e -> Maybe Word8
enum_to_word8 :: forall e. Enum e => e -> Maybe Word8
enum_to_word8 = Int -> Maybe Word8
T.int_to_word8_maybe forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Enum a => a -> Int
fromEnum

-- | Type-specialised 'word8_to_enum'
--
-- > map word8_to_char [60,62] == "<>"
word8_to_char :: Word8 -> Char
word8_to_char :: Word8 -> Char
word8_to_char = forall e. Enum e => Word8 -> e
word8_to_enum

-- | 'T.int_to_word8' of 'fromEnum'
char_to_word8 :: Char -> Word8
char_to_word8 :: Char -> Word8
char_to_word8 = Int -> Word8
T.int_to_word8 forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Enum a => a -> Int
fromEnum

-- | 'T.int_to_word8' of 'digitToInt'
digit_to_word8 :: Char -> Word8
digit_to_word8 :: Char -> Word8
digit_to_word8 = Int -> Word8
T.int_to_word8 forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Int
digitToInt

-- | 'intToDigit' of 'T.word8_to_int'
word8_to_digit :: Word8 -> Char
word8_to_digit :: Word8 -> Char
word8_to_digit = Int -> Char
intToDigit forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word8 -> Int
T.word8_to_int

-- * Indexing

-- | 'at' of 'T.word8_to_int'
word8_at :: [t] -> Word8 -> t
word8_at :: forall t. [t] -> Word8 -> t
word8_at [t]
l = forall a. [a] -> Int -> a
(!!) [t]
l forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word8 -> Int
T.word8_to_int

-- * Text

-- | Given /n/ in (0,255) make two character hex string.
--
-- > mapMaybe byte_hex_pp [0x0F,0xF0,0xF0F] == ["0F","F0"]
byte_hex_pp :: (Integral i, Show i) => i -> Maybe String
byte_hex_pp :: forall i. (Integral i, Show i) => i -> Maybe String
byte_hex_pp i
n =
    case forall a. (Integral a, Show a) => a -> ShowS
showHex i
n String
"" of
      [Char
c] -> forall a. a -> Maybe a
Just [Char
'0',Char -> Char
toUpper Char
c]
      [Char
c,Char
d] -> forall a. a -> Maybe a
Just (forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
toUpper [Char
c,Char
d])
      String
_ -> forall a. Maybe a
Nothing

-- | Erroring variant.
byte_hex_pp_err :: (Integral i, Show i) => i -> String
byte_hex_pp_err :: forall i. (Integral i, Show i) => i -> String
byte_hex_pp_err = forall a. a -> Maybe a -> a
fromMaybe (forall a. HasCallStack => String -> a
error String
"byte_hex_pp") forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall i. (Integral i, Show i) => i -> Maybe String
byte_hex_pp

-- | 'byte_hex_pp_err' either plain (ws = False) or with spaces (ws = True).
--   Plain is the same format written by xxd -p and read by xxd -r -p.
--
-- > byte_seq_hex_pp True [0x0F,0xF0] == "0F F0"
byte_seq_hex_pp :: (Integral i, Show i) => Bool -> [i] -> String
byte_seq_hex_pp :: forall i. (Integral i, Show i) => Bool -> [i] -> String
byte_seq_hex_pp Bool
ws = (if Bool
ws then [String] -> String
unwords else forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map forall i. (Integral i, Show i) => i -> String
byte_hex_pp_err

-- | Read two character hexadecimal string.
--
-- > mapMaybe read_hex_byte (Split.chunksOf 2 "0FF0F") == [0x0F,0xF0]
read_hex_byte :: (Eq t, Integral t) => String -> Maybe t
read_hex_byte :: forall t. (Eq t, Integral t) => String -> Maybe t
read_hex_byte String
s =
    case String
s of
      [Char
_,Char
_] -> forall t. ReadS t -> String -> Maybe t
T.reads_to_read_precise forall a. (Eq a, Num a) => ReadS a
readHex String
s
      String
_ -> forall a. Maybe a
Nothing

-- | Erroring variant.
read_hex_byte_err :: (Eq t, Integral t) => String -> t
read_hex_byte_err :: forall t. (Eq t, Integral t) => String -> t
read_hex_byte_err = forall a. a -> Maybe a -> a
fromMaybe (forall a. HasCallStack => String -> a
error String
"read_hex_byte") forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall t. (Eq t, Integral t) => String -> Maybe t
read_hex_byte

-- | Sequence of 'read_hex_byte_err'
--
-- > read_hex_byte_seq "000FF0FF" == [0x00,0x0F,0xF0,0xFF]
read_hex_byte_seq :: (Eq t, Integral t) => String -> [t]
read_hex_byte_seq :: forall t. (Eq t, Integral t) => String -> [t]
read_hex_byte_seq = forall a b. (a -> b) -> [a] -> [b]
map forall t. (Eq t, Integral t) => String -> t
read_hex_byte_err forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall e. Int -> [e] -> [[e]]
Split.chunksOf Int
2

-- | Variant that filters white space.
--
-- > read_hex_byte_seq_ws "00 0F F0 FF" == [0x00,0x0F,0xF0,0xFF]
read_hex_byte_seq_ws :: (Eq t, Integral t) => String -> [t]
read_hex_byte_seq_ws :: forall t. (Eq t, Integral t) => String -> [t]
read_hex_byte_seq_ws = forall t. (Eq t, Integral t) => String -> [t]
read_hex_byte_seq forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Bool
isSpace)

-- * IO

-- | Load binary 'U8' sequence from file.
load_byte_seq :: Integral i => FilePath -> IO [i]
load_byte_seq :: forall i. Integral i => String -> IO [i]
load_byte_seq = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (forall a b. (a -> b) -> [a] -> [b]
map forall a b. (Integral a, Num b) => a -> b
fromIntegral forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> [Word8]
B.unpack) forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> IO ByteString
B.readFile

-- | Store binary 'U8' sequence to file.
store_byte_seq :: Integral i => FilePath -> [i] -> IO ()
store_byte_seq :: forall i. Integral i => String -> [i] -> IO ()
store_byte_seq String
fn = String -> ByteString -> IO ()
B.writeFile String
fn forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Word8] -> ByteString
B.pack forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map forall a b. (Integral a, Num b) => a -> b
fromIntegral

-- | Load hexadecimal text 'U8' sequences from file.
load_hex_byte_seq :: Integral i => FilePath -> IO [[i]]
load_hex_byte_seq :: forall i. Integral i => String -> IO [[i]]
load_hex_byte_seq = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (forall a b. (a -> b) -> [a] -> [b]
map forall t. (Eq t, Integral t) => String -> [t]
read_hex_byte_seq forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> [String]
lines) forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> IO String
readFile

-- | Store 'U8' sequences as hexadecimal text, one sequence per line.
store_hex_byte_seq :: (Integral i,Show i) => FilePath -> [[i]] -> IO ()
store_hex_byte_seq :: forall i. (Integral i, Show i) => String -> [[i]] -> IO ()
store_hex_byte_seq String
fn = String -> String -> IO ()
writeFile String
fn forall b c a. (b -> c) -> (a -> b) -> a -> c
. [String] -> String
unlines forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map (forall i. (Integral i, Show i) => Bool -> [i] -> String
byte_seq_hex_pp Bool
False)

{-

import qualified Data.ByteString.Base64 as Base64 {- base64-bytestring -}
let fn = "/home/rohan/sw/hsc3-data/data/yamaha/dx7/rom/ROM1A.syx"
b <- load_byte_seq fn :: IO [Word8]
let e = B.unpack (Base64.encode (B.pack b))
let r = B.unpack (Base64.decodeLenient (B.pack e))
(length b,length e,length r,b == r) == (4104,5472,4104,True)
map word8_to_char e

-}

-- * Cast

-- > castFloatToWord32 3.141 == 1078527525
castFloatToWord32 :: Float -> Word32
castFloatToWord32 :: Float -> Word32
castFloatToWord32 Float
d = forall a. (forall s. ST s a) -> a
runST ((forall a b c. (a -> b -> c) -> b -> a -> c
flip forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
a i e -> i -> m e
readArray Int
0 forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< forall s ix a b. STUArray s ix a -> ST s (STUArray s ix b)
castSTUArray forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
(i, i) -> e -> m (a i e)
newArray (Int
0, Int
0::Int) Float
d) :: ST s Word32)

-- > castWord32ToFloat 1078527525 == 3.141
castWord32ToFloat :: Word32 -> Float
castWord32ToFloat :: Word32 -> Float
castWord32ToFloat Word32
d = forall a. (forall s. ST s a) -> a
runST ((forall a b c. (a -> b -> c) -> b -> a -> c
flip forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
a i e -> i -> m e
readArray Int
0 forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< forall s ix a b. STUArray s ix a -> ST s (STUArray s ix b)
castSTUArray forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
(i, i) -> e -> m (a i e)
newArray (Int
0, Int
0::Int) Word32
d) :: ST s Float)

-- > castDoubleToWord64 3.141 == 4614255322014802772
castDoubleToWord64 :: Double -> Word64
castDoubleToWord64 :: Double -> Word64
castDoubleToWord64 Double
d = forall a. (forall s. ST s a) -> a
runST ((forall a b c. (a -> b -> c) -> b -> a -> c
flip forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
a i e -> i -> m e
readArray Int
0 forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< forall s ix a b. STUArray s ix a -> ST s (STUArray s ix b)
castSTUArray forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
(i, i) -> e -> m (a i e)
newArray (Int
0, Int
0::Int) Double
d) :: ST s Word64)

-- > castWord64ToDouble 4614255322014802772 == 3.141
castWord64ToDouble :: Word64 -> Double
castWord64ToDouble :: Word64 -> Double
castWord64ToDouble Word64
d = forall a. (forall s. ST s a) -> a
runST ((forall a b c. (a -> b -> c) -> b -> a -> c
flip forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
a i e -> i -> m e
readArray Int
0 forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< forall s ix a b. STUArray s ix a -> ST s (STUArray s ix b)
castSTUArray forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
(i, i) -> e -> m (a i e)
newArray (Int
0, Int
0::Int) Word64
d) :: ST s Double)