module Main where import Codec.Midi import qualified Codec.Wav as Wav import qualified Codec.SoundFont as SF import Data.Audio import Data.Arbitrary import Data.ByteString.Parser import Data.ByteString.Builder import Test.QuickCheck hiding ((.&.)) import Data.Int import Data.Word import Data.Bits import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as L import Control.Monad import Data.Monoid import Debug.Trace import System.FilePath test :: Testable prop => prop -> IO () test = quickCheck roundTrip :: (Eq a, Show a) => (a -> Builder) -> Parser a -> a -> Bool roundTrip b p a = if Right a == ea' then True else trace (unlines $ [show ea']) $ False where ea' = runParser p bs bs = toLazyByteString $ b a testAudio :: IO () testAudio = do putStrLn "TESTING Inctances of Audible" test (prop_audible :: Word8 -> Bool) test (prop_audible :: Word16 -> Bool) test (prop_audible :: Word32 -> Bool) -- test (prop_audible :: Word64 -> Bool) test (prop_audible :: Int8 -> Bool) test (prop_audible :: Int16 -> Bool) test (prop_audible :: Int32 -> Bool) -- test (prop_audible :: Int64 -> Bool) -- These two tests are commented, because they fail -- Reason for that is the fact that Double is not abble to accomodate -- 64 bit numbers in full precision where prop_audible :: (Eq a, Audible a) => a -> Bool prop_audible a = (a == fromSample s) && (s >= -1.0) && (s <= 1.0) where s = toSample a testMidi :: IO () testMidi = do putStrLn "TESTING PARSING AND BUILDING of Midi" test $ roundTrip buildMessage (parseMessage Nothing) test $ roundTrip buildMidi parseMidi test $ \trk -> trk == fromAbsTime (toAbsTime trk :: Track Ticks) test $ \trk td -> trk == fromRealTime td (toRealTime td trk) test $ \m -> (not $ null $ tracks m) ==> let (Midi SingleTrack _ trks) = toSingleTrack m in length (concat $ tracks m) - length (concat trks) == length (tracks m) - 1 c <- readFile "mid.txt" let midiFiles = lines c sequence_ $ map f midiFiles return () where f n = do putStr $ "Parsing " ++ n ++ " ... " r <- importFile n m <- case r of Left e -> fail $ "\n Failed: " ++ e ++ "\n" Right m -> return m putStrLn $ "OK" putStr "Testing ... " when (not $ roundTrip buildMidi parseMidi m) $ fail "Failed\n" putStrLn "OK" exportFile ("./tmp/" ++ (takeBaseName n) ++ ".mid") m testWav :: IO () testWav = do putStrLn "TESTING PARSING AND BUILDING of Wav" test $ roundTrip (Wav.buildWav :: Audio Word8 -> Builder) Wav.parseWav test $ roundTrip (Wav.buildWav :: Audio Int16 -> Builder) Wav.parseWav test $ roundTrip (Wav.buildWav :: Audio Int32 -> Builder) Wav.parseWav c <- readFile "wav.txt" let wavFiles = lines c sequence_ $ map f wavFiles return () where f n = do putStr $ "Parsing " ++ n ++ " ... " r <- Wav.importFile n a <- case r of Left e -> fail $ "\n Failed: " ++ e ++ "\n" Right a -> return $! (a :: Audio Int16) putStrLn $ "OK" putStr "Testing ... " when (not $ roundTrip Wav.buildWav Wav.parseWav a) $ fail "Failed\n" let sd1 = (convert $ sampleData a) :: SampleData Word8 when (not $ roundTrip Wav.buildWav Wav.parseWav (a {sampleData = sd1})) $ fail "Failed\n" let sd2 = (convert $ sampleData a) :: SampleData Int32 when (not $ roundTrip Wav.buildWav Wav.parseWav (a {sampleData = sd2})) $ fail "Failed\n" putStrLn "OK" Wav.exportFile ("./tmp/" ++ (takeBaseName n) ++ ".wav") a testSoundFont :: IO () testSoundFont = do putStrLn "TESTING PARSING AND BUILDING of SoundFont" test $ roundTrip SF.buildSoundFont SF.parseSoundFont c <- readFile "sf2.txt" let soundFontFiles = lines c sequence_ $ map f soundFontFiles return () where f n = do putStr $ "Parsing " ++ n ++ " ... " r <- SF.importFile n sf <- case r of Left e -> fail $ "\n Failed: " ++ e ++ "\n" Right sf -> return sf putStrLn $ "OK" putStr "Testing ... " when (not $ roundTrip SF.buildSoundFont SF.parseSoundFont sf) $ fail "Failed\n" putStrLn "OK" SF.exportFile ("./tmp/" ++ (takeBaseName n) ++ ".sf2") sf testParserBuilder :: IO () testParserBuilder = do putStrLn "TESTING PARSING AND BUILDING OF NUMERICAL TYPES" test $ roundTrip putWord8 getWord8 test $ roundTrip putWord16be getWord16be test $ roundTrip putWord16le getWord16le test $ \w -> roundTrip putWord24be getWord24be (w .&. 0xFFFFFF) test $ \w -> roundTrip putWord24le getWord24le (w .&. 0xFFFFFF) test $ roundTrip putWord32be getWord32be test $ roundTrip putWord32le getWord32le test $ roundTrip putWord64be getWord64be test $ roundTrip putWord64le getWord64le test $ roundTrip putInt8 getInt8 test $ roundTrip putInt16be getInt16be test $ roundTrip putInt16le getInt16le test $ roundTrip putInt32be getInt32be test $ roundTrip putInt32le getInt32le test $ roundTrip putInt64be getInt64be test $ roundTrip putInt64le getInt64le test $ roundTrip putWordHost getWordHost test $ roundTrip putWord16host getWord16host test $ roundTrip putWord32host getWord32host test $ roundTrip putWord64host getWord64host test $ roundTrip putVarLenBe getVarLenBe test $ roundTrip putVarLenLe getVarLenLe putStrLn "TESTING PARSING AND BUILDING OF String and ByteString" test $ \s1 s2 -> roundTrip (\s -> putString s `mappend` putString s2) (getString $ length s1) s1 test $ \s1 s2 -> roundTrip (\s -> fromByteString s `mappend` fromByteString s2) (getByteString $ B.length s1) s1 test $ \s1 s2 -> roundTrip (\s -> fromLazyByteString s `mappend` fromLazyByteString s2) (getLazyByteString $ L.length s1) s1 test $ \bs -> roundTrip fromLazyByteString getRemainingLazyByteString bs main :: IO () main = do testAudio testParserBuilder testMidi testWav testSoundFont