-- |
-- Copyright  : (c) Keera Studios, 2010-2014.
-- License    : See LICENSE file.
-- Maintainer : Ivan Perez <ivan.perez@keera.co.uk>
--
-- A layer of abstraction on top of SDL audio.
--
-- It plays audio soundfx asynchronously (in a new thread), which means that
-- programs must be compiled with the threaded Runtime System (ghc flag is
-- -threaded).
module Audio
    ( Music(..)
    , Audio(..)
    , initAudio
    , loadAudio
    , loadMusic
    , playMusic
    , playFile
    , stopMusic
    , musicPlaying
    )
  where

-- External imports
import           Control.Applicative            ((<$>))
import           Control.Concurrent
import           Control.Monad
import qualified Graphics.UI.SDL.Mixer.Channels as SDL.Mixer.Channels
import qualified Graphics.UI.SDL.Mixer.General  as SDL.Mixer
import qualified Graphics.UI.SDL.Mixer.Music    as SDL.Mixer.Music
import qualified Graphics.UI.SDL.Mixer.Samples  as SDL.Mixer.Samples
import qualified Graphics.UI.SDL.Mixer.Types    as SDL.Mixer.Types

data Music = Music { musicName :: String, unMusic :: SDL.Mixer.Types.Music }

data Audio = Audio { audioName :: String, unAudio :: SDL.Mixer.Types.Chunk }

-- | Initialize the audio subsystem.
--
-- Audio quality and number of channels are fixed (16).
initAudio :: IO ()
initAudio = void $ do
  _result <- SDL.Mixer.openAudio 44100 SDL.Mixer.AudioS16LSB 2 4096
  SDL.Mixer.Channels.allocateChannels 16

-- | Load a music file, returning a 'Music' if loaded successfully.
loadMusic :: String -> IO (Maybe Music)
loadMusic fp = fmap (Music fp) <$> SDL.Mixer.Music.tryLoadMUS fp

-- | Play music in a loop at max volume.
playMusic :: Music -> IO ()
playMusic m = do
  SDL.Mixer.Music.setMusicVolume 100
  SDL.Mixer.Music.playMusic (unMusic m) (-1)

-- | Stop playing music
stopMusic :: IO ()
stopMusic = SDL.Mixer.Music.haltMusic

-- | Is music playing?
musicPlaying :: IO Bool
musicPlaying = SDL.Mixer.Music.playingMusic

-- | Load an audio file.
loadAudio :: String -> IO (Maybe Audio)
loadAudio fp = fmap (Audio fp) <$> SDL.Mixer.Samples.tryLoadWAV fp

-- | Play an audio file for the given number of seconds.
--
-- This function spawns a new OS thread. Remember to compile your program
-- with the threaded RTS.
playFile :: Audio -> Int -> IO ()
playFile wav t = void $ forkOS $ do
  _v <- SDL.Mixer.Channels.playChannel (-1) (unAudio wav) 0
  threadDelay (t * 1000)
