I'm building a script that reads 381 bytes from a file and attempts to decode the input. I am interested in 348 of those bytes I am labelling "presets". 3 byte chunks of the presets ByteString can be decoded into a single Int16, and "values" below are the 116 Int16 I am interested in...
decodeFile :: FilePath -> IO [Maybe PresetValue]
decodeFile filename =
do h <- openFile (dir ++ filename) ReadMode
header <- h `BL.hGet` 32
presets <- h `BL.hGet` 348
f7 <- h `BL.hGet` 1
let values = Bin.runGet getPresets presets
hClose h
return values
getPresets = do
empty <- Bin.isEmpty
if empty
then return []
else do p <- getAndDecodeTriple
ps <- getPresets
return (p:ps)
getAndDecodeTriple = do
b1 <- Bin.getWord8
b2 <- Bin.getWord8
b3 <- Bin.getWord8
return $ decode (b1,b2,b3)
The problem I am having is decoding a 3 byte chunk, given I know how it was encoded in C++
Here is the C++ encoding
void SysexReader::sx_encode(int val, char* dest)
{
char encode;
// Encode Byte 1 (4 bits of payload)
encode = 0x40 | ((val >> 12) & 0x000F);
*dest++ = encode;
// Encode Byte 2 (6 bits of payload)
encode = (val >> 6) & 0x003F;
*dest++ = encode;
// Encode Byte 3 (6 bits of payload)
encode = val & 0x003F;
*dest = encode;
}
Here is the C++ encoding translated to Haskell...
type Encoding a = (a,a,a)
type PresetValue = Int16
encode :: Integral a => PresetValue -> Encoding a
encode val =
let f = fromIntegral
in (f $ enc1 val, f $ enc2 val, f $ enc3 val)
where
enc1 = or40 . and000F . (flip shiftR 12)
where and000F = (0x000F .&.)
or40 = (0x40 .|.)
enc2 = enc3 . flip shiftR 6
enc3 = (0x003F .&.)
My attempt at decoding uses the fact that I have the encoding procedure and I know that PresetValue can only be in the range of (0,127)
-- (3 Sysex Bytes) -> (Preset Value) --
-------------------------------------------------------
decode :: Integral a => (a,a,a) -> Maybe PresetValue
decode encoded =
case match of
[value] -> Just value
[] -> Nothing --error "encode not surjective"
many -> error "encode not injective"
where
match = filter (\x -> encode x == encoded) [0..127]
Unfortunately I can't decode all values, as you can see from the 116-entry list below containing Nothing in many places.
[Just 14,Just 84,Just 97,Just 117,Just 114,Just 117,Just 115,Just 32,Just 73,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Nothing,Nothing,Just 0,Nothing,Nothing,Nothing,Just 0,Nothing,Nothing,Just 0,Just 0,Nothing,Nothing,Just 0,Just 1,Nothing,Just 0,Nothing,Nothing,Just 0,Just 0,Just 0,Just 1,
Just 0,Just 0,Nothing,Just 5,Just 0,Just 1,Just 0,Just 0,Just 0,Nothing,Nothing,
Just 3,Just 2,Just 0,Just 0,Nothing,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Nothing,Nothing,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Just 0,Nothing]
What am I doing wrong? I feel like it must be the types I am using to represent each chunk from the incoming file. Or maybe I'm losing information using fromIntegral.
I've been a developer for a while and have never posted a question on here and always fought through for an answer, but I'm really lost on this one. Thanks.
`mod` 256somewhere?