module Sound.SonicVisualiser.XML where
import Control.Arrow
import Control.Exception
import Prelude hiding (catch)
import Control.Monad
import Data.Maybe
import Data.Map (Map)
import qualified Data.Map as Map
import Sound.SonicVisualiser.Types
import Text.XML.Light
type ModelMap = Map Int Model
type DataMap = Map Int Data
mkName :: String -> QName
mkName s = QName s Nothing Nothing
getAttr :: String -> Element -> IO String
getAttr name e =
case findAttr (mkName name) e of
Nothing -> fail $ "getAttr: missing attribute " ++ name
Just s -> return s
getAttrDefault :: String -> String -> Element -> IO String
getAttrDefault name def e = return $ fromMaybe def (findAttr (mkName name) e)
readAttr :: Read a => String -> Element -> IO a
readAttr name e =
case findAttr (mkName name) e of
Nothing -> fail $ "readAttr: missing attribute " ++ name
Just s -> catch (readIO s) (const $ fail $ "readAttr: parse error " ++ name)
readAttrDefault :: Read a => String -> a -> Element -> IO a
readAttrDefault name def e =
case findAttr (mkName name) e of
Nothing -> return def
Just s -> catch (readIO s) (const $ fail $ "readAttr: parse error " ++ name)
getPoint :: Element -> IO Point
getPoint e = do
frame <- catch (liftM fromJust (readAttr "frame" e)) (const parseError)
value <- readAttr "value" e
return $ Point {
pointFrame = frame,
pointValue = value,
pointLabel = findAttr (mkName "label") e
}
where
readAttr name e =
case findAttr (mkName name) e of
Nothing -> return Nothing
Just s -> catch (readIO s >>= return . Just) (const parseError)
parseError = fail "getPoint: couldn't parse point"
getPoints :: Element -> IO [Point]
getPoints e = do
let ps = filterChildrenName (== mkName "point") e
mapM getPoint ps
getDatasets :: Element -> IO DataMap
getDatasets e = do
ds <- mapM pair (filterChildrenName (== mkName "dataset") e)
return (Map.fromList ds)
where
pair e = do
id <- readId (findAttr (mkName "id") e)
ps <- getPoints e
return (id, DataSet ps)
readId attr = catch (readIO (fromJust attr)) (\_ -> fail "getDatasets: couldn't parse id")
readModelType :: String -> IO ModelType
readModelType "wavefile" = return WaveFile
readModelType "sparse" = return SparseMatrix
readModelType s = fail $ "unknown ModelType " ++ s
getModel :: DataMap -> Element -> IO (Int, Model)
getModel ds e = do
mid <- readAttr "id" e
name <- getAttr "name" e
sampleRate <- readAttr "sampleRate" e
startFrame <- readAttr "start" e
endFrame <- readAttr "end" e
theType <- getAttr "type" e >>= readModelType
dimensions <- readAttrDefault "dimensions" (1 :: Int) e
resolution <- readAttrDefault "resolution" (1 :: Int) e
isMain <- getAttrDefault "mainModel" "false" e >>= (return . (==) "true")
theData <- case theType of
WaveFile ->
getAttr "file" e >>= return . File
SparseMatrix -> do
dsid <- readAttr "dataset" e
return $ fromMaybe (DataSet []) (Map.lookup dsid ds)
return (mid, Model {
modelName = name,
modelSampleRate = sampleRate,
modelStartFrame = startFrame,
modelEndFrame = endFrame,
modelType = theType,
modelDimensions = dimensions,
modelResolution = resolution,
modelIsMain = isMain,
modelData = theData
})
getModels :: DataMap -> Element -> IO ModelMap
getModels ds e = do
assocs <- mapM (getModel ds) (filterChildrenName (== mkName "model") e)
return $ Map.fromList assocs
getLayer :: ModelMap -> Element -> IO (Maybe Layer)
getLayer ms e = do
name <- getAttr "name" e
mid <- readAttr "model" e
return $ maybe Nothing (Just . Layer name) (Map.lookup mid ms)
getLayers :: ModelMap -> Element -> IO [Layer]
getLayers ms e = do
ls <- mapM (getLayer ms) (filterChildrenName (== mkName "layer") e)
return $ [ x | Just x <- ls ]
getDerivation :: ModelMap -> Element -> IO (Maybe Derivation)
getDerivation ms e = do
sourceId <- readAttr "source" e
modelId <- readAttr "model" e
channel <- readAttr "channel" e
domain <- readAttr "domain" e
stepSize <- readAttr "stepSize" e
blockSize <- readAttr "blockSize" e
windowType <- readAttr "windowType" e
transform <- getAttr "transform" e
return $ do
source <- Map.lookup sourceId ms
model <- Map.lookup modelId ms
return $ Derivation {
derivationSource = source,
derivationModel = model,
derivationChannel = channel,
derivationDomain = domain,
derivationStepSize = stepSize,
derivationBlockSize = blockSize,
derivationWindowType = windowType,
derivationTransform = transform
}
getDerivations :: ModelMap -> Element -> IO [Derivation]
getDerivations ms e = do
ds <- mapM (getDerivation ms) (filterChildrenName (== mkName "derivations") e)
return $ [ x | Just x <- ds ]
readString :: String -> IO Document
readString s = do
case parseXMLDoc s of
Nothing -> fail "XML parse error"
Just xml -> do
let xmlData = (findChildren (mkName "sv") >>> head >>> findChildren (mkName "data") >>> head) xml
dmap <- getDatasets xmlData
mmap <- getModels dmap xmlData
layers <- getLayers mmap xmlData
derivs <- getDerivations mmap xmlData
let doc = Document (Map.elems mmap) layers derivs
return doc