packages feed

HGamer3D-Audio-0.5.0: HGamer3D/Audio/SystemAudio.hs

{-# Language StandaloneDeriving, DeriveDataTypeable #-}
{-# OPTIONS_HADDOCK hide #-}

-- This source file is part of HGamer3D
-- (A project to enable 3D game development in Haskell)
-- For the latest info, see http://www.hgamer3d.org
--
-- (c) 2011-2014 Peter Althainz
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.

module HGamer3D.Audio.SystemAudio

where

import qualified Data.Map as M
import Data.Maybe
import Data.Typeable
import Data.Dynamic
import Data.IORef
import Data.Traversable
import Control.Concurrent

import qualified HGamer3D.Data as D
import HGamer3D.Common
import qualified HGamer3D.BaseAPI.Audio as A

import qualified HGamer3D.Audio.AudioSource as AS
import qualified HGamer3D.Audio.AudioListener as AL

data AudioMap = AudioMap (M.Map String A.AudioSource) AS.AudioSource

{- In this module the AS.AudioSource is a map of AS.AudioSlots, where each slot aligns to a
   AudioSource from the base Audio module. So the AS.AudioSource is a map of real sources, 
   to be able to have multiple sounds played by one object.
-}

-- | create AudioMap from pure data definition out of Schema
audioMap :: AS.AudioSource -> IO AudioMap
audioMap mapAS = do

  let makeSource asource = do
        -- create source
        (source, parameter) <- case asource of
          AS.Music parameter -> A.musicAudioSource (AS.aspFile parameter) >>= \s -> return (fromJust s, parameter)
          AS.Sound parameter -> A.soundAudioSource (AS.aspFile parameter) >>= \s -> return (fromJust s, parameter)
        -- set all parameters
        A.setAudioSourceVolume source (AS.aspVolume parameter)
        A.setAudioSourceLoop source (AS.aspLoopFlag parameter)
        A.setAudioSourcePositionDependent source (AS.aspPositionFlag parameter)
        A.setAudioSourcePitch source (AS.aspPitch parameter)
        A.setAudioSourceAttenuation source (AS.aspAttenuation parameter)
        A.setAudioSourceMinDistance source (AS.aspMinDistance parameter)
        return source

  obMap <- traverse makeSource mapAS
  return $ AudioMap obMap mapAS

-- | update AudioSlots from change in data definition, tbd
updateAudioMap :: AudioMap -> AS.AudioSource -> IO AudioMap
updateAudioMap inSlots newSlots = return inSlots

-- | remove AudioSlots, tbd
removeAudioMap :: AudioMap -> IO ()
removeAudioMap as = return ()

setPosition :: AudioMap -> D.Position -> IO ()
setPosition (AudioMap obMap schema) pos = do
            traverse ((flip A.setAudioSourcePosition) pos) obMap
            return ()

playCmd :: AudioMap -> Maybe AS.AudioCmd -> IO ()
playCmd (AudioMap obMap schema) (Just (AS.PlayAudio slot)) = A.playAudioSource (fromJust (M.lookup slot obMap))
playCmd (AudioMap obMap schema) (Just (AS.StopAudio slot)) = A.stopAudioSource (fromJust (M.lookup slot obMap))
playCmd _ _ = return ()

data ECSAudio = ECSAudio     -- no audio specific data here, but never mind

instance System ECSAudio where

    initializeSystem = do
         
      lock <- newMVar ()
      newERefs <- newIORef []
      delERefs <- newIORef []
      let records = []
      let system = ECSAudio

      let systemFunction system eref = do

          e <- readE eref -- this e is used to create the representation

          if e #? CTASr then do

             rep <- audioMap ((e # CTASr) :: AS.AudioSource)
             ref <- newIORef rep
             if e #? CTPos then setPosition rep (e # CTPos) else return ()

             let createRecord ct = do
                 if e #? ct then do
                    l <- componentListener eref ct
                    let uf = case ct of
                                  CTASr -> \ _ e' -> do
                                                              updateAudioMap rep (e' # CTASr)
                                                              if e #? CTPos then setPosition rep (e' # CTPos) else return ()
                                                              return () 
                                  CTCmd -> \ _ e' -> do
                                                              playCmd rep (e' ?# CTCmd)
                                                              return ()
                                  _ -> \ _ _ -> return ()

                    let df = return ()
                    return $ Just (l, uf, df)
                    else return Nothing

             newRecords <- Prelude.mapM createRecord [CTASr, CTCmd]
             return (map fromJust (filter isJust newRecords))

             else return []
        
      return (SystemData lock newERefs delERefs records system systemFunction)

    stepSystem (SystemData lock newERefs delERefs records system systemFunction) = do
      return False

forkAudioWorld :: D.GameTime -> IO [SomeSystem]
forkAudioWorld sleepT = do
                       system <- (runSystem sleepT) :: IO (SystemData ECSAudio)
                       return $ [SomeSystem system]