conductive-base-0.3: Sound/Conductive/Player.hs
------------------------------------------------------------------------------
-- /home/renick/haskell/conductive-0.3c/Player.hs
-- created: Sun Sep 16 22:20:21 JST 2012
------------------------------------------------------------------------------
-- | This is a collection of functions which are useful for dealing with Players.
module Sound.Conductive.Player where
import Control.Concurrent
import Control.Concurrent.STM
import Data.Map
import Data.Maybe
import Sound.Conductive.ConductiveBaseData
import Sound.Conductive.MusicalEnvironment
import Sound.Conductive.MusicalTime
import Sound.Conductive.MutableMap
-- | Displays information about a single player.
displayPlayer :: MusicalEnvironment -> String -> IO ()
displayPlayer env player = do
Just p <- (playerStore env) ?@ player
let pString = concat [ playerName p
, " "
, show $ playerStatus p
, " "
, "a: "
, playerAction p
, " "
, "IOI: "
, playerIOI p
]
putStrLn pString
-- | Creates a thread delay specified in seconds.
sleep :: (RealFrac a) => a -> IO ()
sleep x = threadDelay $ truncate $ x * 1000000
-- | Creates a new player.
newPlayer
:: String -- ^ the name of the player
-> String -- ^ the clock it should follow
-> String -- ^ the name of the IOI function to use
-> String -- ^ the name of the action to use
-> Double -- ^ the beat to start on when played
-> Player
newPlayer name t deltaf actionf beat =
Player { playerName = name
, playerStatus = Stopped
, playerIOI = deltaf
, playerCounter = 0
, playerClock = t
, playerAction = actionf
, playerInterrupt = "defaultInterrupt"
, playerStartingBeat = beat
, playerBeat = beat
, playerPauseTime = 0
}
-- | Used to change a player stored in a MusicalEnvironment.
modifyPlayer :: MusicalEnvironment -> String -> (Player -> Player) -> IO ()
modifyPlayer e k func = do
Just p <- (playerStore e) ?@ k
addVal (playerStore e) (k,func p)
withPlayers :: MusicalEnvironment -> (Player -> a1) -> [String] -> IO [Maybe a1]
withPlayers e func = withKeys (playerStore e) func
------------------------------------------------------------------------------
-- the boilerplate for records is annoying. Is there no cleaner way?
---- can I eliminate all of these functions with a cleaner spelling using modifyPlayer directly?
--------------------------------------------------------------------------------
-- functions for updating one record of a player
-- | Change the name of a player from old (second argument) to new (third argument).
swapName :: MusicalEnvironment -> String -> String -> IO ()
swapName e k new = modifyPlayer e k $ (\x -> x { playerName = new })
-- | Change the status of a player.
swapStatus :: MusicalEnvironment -> String -> PlayerStatus -> IO ()
swapStatus e k new = modifyPlayer e k $ (\x -> x { playerStatus = new })
-- | Change the IOI function of a player (second argument) from old IOI function to new one (third argument).
swapIOI :: MusicalEnvironment -> String -> String -> IO ()
swapIOI e k new = modifyPlayer e k $ (\x -> x { playerIOI = new })
-- | Change the counter value of a player.
swapCounter :: MusicalEnvironment -> String -> Integer -> IO ()
swapCounter e k new = modifyPlayer e k $ (\x -> x { playerCounter = new })
-- | Change the clock a player is following from old (second argument) to new (third argument).
swapClock :: MusicalEnvironment -> String -> String -> IO ()
swapClock e k new = modifyPlayer e k $ (\x -> x { playerClock = new })
-- | Change the action function of a player from old (second argument) to new (third argument).
swapActions :: MusicalEnvironment -> String -> String -> IO ()
swapActions e k new = modifyPlayer e k $ (\x -> x { playerAction = new })
-- | Change the interrupt function of a player from old (second argument) to new (third argument).
swapInterrupt :: MusicalEnvironment -> String -> String -> IO ()
swapInterrupt e k new = modifyPlayer e k $ (\x -> x { playerInterrupt = new })
-- | Change the beat of the next event of a player.
swapBeat :: MusicalEnvironment -> String -> Double -> IO ()
swapBeat e k new = modifyPlayer e k $ (\x -> x { playerBeat = new })
-- | Change the time of the last pause of a player.
swapPauseTime :: MusicalEnvironment -> String -> Double -> IO ()
swapPauseTime e k new = modifyPlayer e k $ (\x -> x { playerPauseTime = new })
------------------------------------------------------------------------------
-- | Plays a player, specified by the string, from a MusicalEnvironment. The start time is determined by the playerBeat field of the player.
play :: MusicalEnvironment -> String -> IO ()
play e player = let
statusCheck p Stopped = (forkIO $ basicPlay e player $ Stopped) >> return ()
statusCheck p Playing = putStrLn $ "Player "++player++" is already Playing!"
statusCheck p Pausing = sleep 0.01 >> play e player -- why is there a hardcoded sleep here and below?
statusCheck p Paused = (forkIO $ basicPlay e player $ Paused) >> return ()
statusCheck p Stopping = sleep 0.01 >> play e player
statusCheck p Resetting = sleep 0.01 >> play e player
in do Just p <- (playerStore e) ?@ player
statusCheck p $ playerStatus p
-- | Plays a player, specified by the string, from a MusicalEnvironment. The start time is given in beats as the first argument (the Double), from which the player automatically adjusts the playerBeat record.
playAt :: Double -> MusicalEnvironment -> String -> IO ()
playAt beat e player = do
swapBeat e player beat
play e player
-- | Plays a player, specified by the string, from a MusicalEnvironment. The start time is given as a time string as the first argument (the Double), from which the player automatically adjusts the playerBeat record. Time strings are specified in the MusicalTime module.
playAtTimeString :: [Char] -> MusicalEnvironment -> String -> IO ()
playAtTimeString ts e player = do
Just p <- (playerStore e) ?@ player
Just clock <- (tempoClockStore e) ?@ (playerClock p)
let beat = musicalTimeToBeats clock $ timeStringToMusicalTime ts
playAt beat e player
playNow :: MusicalEnvironment -> String -> IO ()
playNow e player = do
Just p <- (playerStore e) ?@ player
Just tc <- (tempoClockStore e) ?@ (playerClock p)
cb <- currentBeat tc
playAt cb e player
playAtStartOfNMeasure :: MusicalEnvironment -> Int -> String -> IO ()
playAtStartOfNMeasure e measure player = do
Just p <- (playerStore e) ?@ player
Just clock <- (tempoClockStore e) ?@ (playerClock p)
cm <- currentMeasure clock
let beat = musicalTimeToBeats clock $ MusicalTime {measure = cm + measure, beat = 0.0}
playAt beat e player
------------------------------------------------------------------------------
-- the basicPlay function, number of cases
basicPlay :: MusicalEnvironment -> String -> PlayerStatus -> IO ()
basicPlay e player Stopped = do
swapStatus e player Playing
basicPlay e player Playing
basicPlay e player Playing = do
Just p <- (playerStore e) ?@ player
Just tc <- (tempoClockStore e) ?@ (playerClock p)
cb <- currentBeat tc
let pb = playerBeat p
if (pb > cb)
then do let diff = pb - cb
putStrLn "\n-------------------------------------------------"
putStrLn $ "The beat for player " ++ player ++ " to play on is in the future."
putStrLn $ show player
putStrLn $ "currentBeat = " ++ show cb
putStrLn $ "playerBeat = " ++ (show $ playerBeat p)
putStrLn $ "diff = " ++ (show $ diff)
putStrLn "Sleeping and trying again at that time."
putStrLn "-------------------------------------------------\n"
sleep diff
Just p1 <- (playerStore e) ?@ player
basicPlay e player $ playerStatus p1
else do Just interrupt <- (interruptStore e) ?@ (playerInterrupt p)
sequence_ interrupt
Just a <- (actionStore e) ?@ (playerAction p)
Just iOIFunc <- (iOIStore e) ?@ (playerIOI p)
beatOfNextEvent <- iOIFunc e p pb cb
forkIO $ a e p cb (beatOfNextEvent - cb) >> return ()
swapBeat e player beatOfNextEvent
-- swapCounter e player $ 1 + (playerCounter p)
sleep $ beatsToDelta tc $ beatOfNextEvent - cb
Just p1 <- (playerStore e) ?@ player
basicPlay e player $ playerStatus p
basicPlay e player Pausing = do
swapStatus e player Paused
putStrLn $ "Player " ++ player ++ " has been paused."
return ()
basicPlay e player Paused = do
swapStatus e player Playing
basicPlay e player Playing
basicPlay e player Stopping = do
swapStatus e player Stopped
putStrLn $ "Player " ++ player ++ " has been stopped."
return ()
basicPlay e player Resetting = do
swapStatus e player Stopped
putStrLn $ "Player " ++ player ++ " has been reset."
return ()
-- end of the basicPlay function
------------------------------------------------------------------------------
-- | Pauses the specified player.
pause :: MusicalEnvironment -> String -> IO ()
pause e player = do
Just p <- (playerStore e) ?@ player
if (playerStatus p) == Playing
then do actualTime <- currentTime
swapPauseTime e player actualTime
swapStatus e player Pausing >> return ()
else do putStrLn $ "Player " ++ player ++ " is not playing. It cannot be paused."
-- | Stops the specified player. Doing so resets both the playerBeat and playerCounter to 0.
stop :: MusicalEnvironment -> String -> IO ()
stop e player = do
Just p <- (playerStore e) ?@ player
if (playerStatus p) == Playing
then do swapCounter e player 0
swapBeat e player 0
swapStatus e player Stopping >> return ()
else do putStrLn $ "Player " ++ player ++ " is not playing. It cannot be stopped."
-- | Resets a paused player. Resetting means setting the playerBeat and playerCounter to 0.
reset :: MusicalEnvironment -> String -> IO ()
reset e player = do
Just p <- (playerStore e) ?@ player
if (playerStatus p) == Paused
then do swapCounter e player 0
swapBeat e player 0
swapStatus e player Resetting >> return ()
else do putStrLn $ "Player " ++ player ++ " is not paused, therefore this player cannot be reset."
playN :: MusicalEnvironment -> [String] -> IO ()
playN e ps = mapM_ (play e) ps
playAll :: MusicalEnvironment -> IO ()
playAll e = players e >>= \x -> mapM_ (play e) $ x
-- the playAt functions appear not to work correctly. why?
-- playNAt e timestring ps = mapM_ (playAtTimeString timestring e) ps
-- playAllAt e timestring = players e >>= \x -> mapM_ (playAtTimeString timestring e) $ x
pauseN :: MusicalEnvironment -> [String] -> IO ()
pauseN e ps = mapM_ (pause e) ps
pauseAll :: MusicalEnvironment -> IO ()
pauseAll e = players e >>= \x -> mapM_ (pause e) $ x
stopN :: MusicalEnvironment -> [String] -> IO ()
stopN e ps = mapM_ (stop e) ps
stopAll :: MusicalEnvironment -> IO ()
stopAll e = players e >>= \x -> mapM_ (stop e) $ x
resetN :: MusicalEnvironment -> [String] -> IO ()
resetN e ps = mapM_ (reset e) ps
resetAll :: MusicalEnvironment -> IO ()
resetAll e = players e >>= \x -> mapM_ (reset e) $ x
-- | Used for setting up the playerStore of a MusicalEnvironment. It automatically creates one player according to the arguments it is given.
newPlayerStore :: (String, (String, String, String, Double)) -> IO (TVar (Map String Player))
newPlayerStore (k,(t, deltaf, actionf, beat)) =
let p = newPlayer k t deltaf actionf beat
in newMMap [(k,p)]
-- | Creates a new player from the given arguments and adds it to the MusicalEnvironment.
addNewPlayer :: MusicalEnvironment -> (String, (String, String, String, Double)) -> IO ()
addNewPlayer e (n,(c,d,a,b)) = addVal (playerStore e) (n,newPlayer n c d a b)