{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}

-- |
--
-- Module      : SDL.Mixer
-- License     : BSD3
-- Stability   : experimental
--
-- Bindings to the @SDL2_mixer@ library.
module SDL.Mixer
  ( -- * Audio setup

    --

    -- | In order to use the rest of the library, you need to
    -- supply 'withAudio' or 'openAudio' with an 'Audio' configuration.
    withAudio,
    Audio (..),
    Format (..),
    Output (..),
    defaultAudio,
    ChunkSize,
    queryAudio,

    -- ** Alternative
    openAudio,
    closeAudio,

    -- * Loading audio data

    --

    -- | Use 'load' or 'decode' to get both 'Chunk' and 'Music' values.
    Loadable (..),
    Chunk (..),
    chunkDecoders,
    Music (..),
    musicDecoders,

    -- * Chunks

    --

    -- | 'Chunk's are played on 'Channel's, which can be combined into 'Group's.

    -- ** Playing chunks
    Channel,
    pattern AllChannels,
    setChannels,
    getChannels,
    play,
    playForever,
    Times,
    pattern Once,
    pattern Forever,
    playOn,
    Milliseconds,
    Limit,
    pattern NoLimit,
    playLimit,
    fadeIn,
    fadeInOn,
    fadeInLimit,

    -- ** Grouping channels
    reserveChannels,
    Group,
    pattern DefaultGroup,
    group,
    groupSpan,
    groupCount,
    getAvailable,
    getOldest,
    getNewest,

    -- ** Controlling playback
    pause,
    resume,
    halt,
    haltAfter,
    haltGroup,

    -- ** Setting the volume
    Volume,
    HasVolume (..),

    -- ** Querying for status
    playing,
    playingCount,
    paused,
    pausedCount,
    playedLast,
    Fading,
    fading,

    -- ** Fading out
    fadeOut,
    fadeOutGroup,

    -- ** Reacting to finish
    whenChannelFinished,

    -- * Music

    --

    -- | 'Chunk's and 'Music' differ by the way they are played. While multiple
    -- 'Chunk's can be played on different desired 'Channel's at the same time,
    -- there can only be one 'Music' playing at the same time.
    --
    -- Therefore, the functions used for 'Music' are separate.

    -- ** Playing music
    playMusic,
    Position,
    fadeInMusic,
    fadeInMusicAt,
    fadeInMusicAtMOD,

    -- ** Controlling playback
    pauseMusic,
    haltMusic,
    resumeMusic,
    rewindMusic,
    setMusicPosition,
    setMusicPositionMOD,

    -- ** Setting the volume
    setMusicVolume,
    getMusicVolume,

    -- ** Querying for status
    playingMusic,
    pausedMusic,
    fadingMusic,
    MusicType (..),
    musicType,
    playingMusicType,

    -- ** Fading out
    fadeOutMusic,

    -- ** Reacting to finish
    whenMusicFinished,

    -- * Effects
    Effect,
    EffectFinished,
    pattern PostProcessing,
    effect,

    -- ** In-built effects
    effectPan,
    effectDistance,
    effectPosition,
    effectReverseStereo,

    -- * Other
    initialize,
    InitFlag (..),
    quit,
    version,
  )
where

import Control.Exception (throwIO)
import Control.Exception.Lifted (finally)
import Control.Monad (forM, void, when, (<=<), (>=>))
import Control.Monad.IO.Class (MonadIO, liftIO)
import Control.Monad.Trans.Control (MonadBaseControl)
import Data.Bits ((.&.), (.|.))
import Data.ByteString as BS (ByteString, readFile)
import Data.ByteString.Unsafe (unsafeUseAsCStringLen)
import Data.Default.Class (Default (def))
import Data.IORef (IORef, newIORef, readIORef, writeIORef)
import Data.Int (Int16)
import Data.Vector.Storable.Mutable (IOVector, unsafeFromForeignPtr0)
import Data.Word (Word8)
import Foreign.C.String (peekCString)
import Foreign.C.Types (CInt)
import Foreign.ForeignPtr (castForeignPtr, newForeignPtr_)
import Foreign.Marshal.Alloc (alloca)
import Foreign.Ptr (FunPtr, Ptr, castPtr, freeHaskellFunPtr, nullFunPtr, nullPtr)
import Foreign.Storable (Storable (peek))
import SDL (SDLException (SDLCallFailed))
import SDL.Internal.Exception
  ( getError,
    throwIf0,
    throwIfNeg,
    throwIfNeg_,
    throwIfNull,
    throwIf_,
  )
import qualified SDL.Raw
import SDL.Raw.Filesystem (rwFromConstMem)
import qualified SDL.Raw.Mixer
import System.IO.Unsafe (unsafePerformIO)

-- | Initialize the library by loading support for a certain set of
-- sample/music formats.
--
-- Note that calling this is not strictly necessary: support for a certain
-- format will be loaded automatically when attempting to load data in that
-- format. Using 'initialize' allows you to decide /when/ to load support.
--
-- You may call this function multiple times.
initialize :: (Foldable f, MonadIO m) => f InitFlag -> m ()
initialize :: f InitFlag -> m ()
initialize f InitFlag
flags = do
  let raw :: CInt
raw = (CInt -> InitFlag -> CInt) -> CInt -> f InitFlag -> CInt
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl (\CInt
a InitFlag
b -> CInt
a CInt -> CInt -> CInt
forall a. Bits a => a -> a -> a
.|. InitFlag -> CInt
initToCInt InitFlag
b) CInt
0 f InitFlag
flags
  (CInt -> Bool) -> Text -> Text -> m CInt -> m ()
forall (m :: * -> *) a.
MonadIO m =>
(a -> Bool) -> Text -> Text -> m a -> m ()
throwIf_ ((CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
/= CInt
raw) (CInt -> Bool) -> (CInt -> CInt) -> CInt -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (CInt -> CInt -> CInt
forall a. Bits a => a -> a -> a
.&. CInt
raw)) Text
"SDL.Mixer.initialize" Text
"Mix_Init" (m CInt -> m ()) -> m CInt -> m ()
forall a b. (a -> b) -> a -> b
$
    CInt -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> m CInt
SDL.Raw.Mixer.init CInt
raw

-- | Used with 'initialize' to designate loading support for a particular
-- sample/music format.
data InitFlag
  = InitFLAC
  | InitMOD
  | InitMP3
  | InitOGG
  deriving stock (InitFlag -> InitFlag -> Bool
(InitFlag -> InitFlag -> Bool)
-> (InitFlag -> InitFlag -> Bool) -> Eq InitFlag
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: InitFlag -> InitFlag -> Bool
$c/= :: InitFlag -> InitFlag -> Bool
== :: InitFlag -> InitFlag -> Bool
$c== :: InitFlag -> InitFlag -> Bool
Eq, Eq InitFlag
Eq InitFlag
-> (InitFlag -> InitFlag -> Ordering)
-> (InitFlag -> InitFlag -> Bool)
-> (InitFlag -> InitFlag -> Bool)
-> (InitFlag -> InitFlag -> Bool)
-> (InitFlag -> InitFlag -> Bool)
-> (InitFlag -> InitFlag -> InitFlag)
-> (InitFlag -> InitFlag -> InitFlag)
-> Ord InitFlag
InitFlag -> InitFlag -> Bool
InitFlag -> InitFlag -> Ordering
InitFlag -> InitFlag -> InitFlag
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: InitFlag -> InitFlag -> InitFlag
$cmin :: InitFlag -> InitFlag -> InitFlag
max :: InitFlag -> InitFlag -> InitFlag
$cmax :: InitFlag -> InitFlag -> InitFlag
>= :: InitFlag -> InitFlag -> Bool
$c>= :: InitFlag -> InitFlag -> Bool
> :: InitFlag -> InitFlag -> Bool
$c> :: InitFlag -> InitFlag -> Bool
<= :: InitFlag -> InitFlag -> Bool
$c<= :: InitFlag -> InitFlag -> Bool
< :: InitFlag -> InitFlag -> Bool
$c< :: InitFlag -> InitFlag -> Bool
compare :: InitFlag -> InitFlag -> Ordering
$ccompare :: InitFlag -> InitFlag -> Ordering
$cp1Ord :: Eq InitFlag
Ord, InitFlag
InitFlag -> InitFlag -> Bounded InitFlag
forall a. a -> a -> Bounded a
maxBound :: InitFlag
$cmaxBound :: InitFlag
minBound :: InitFlag
$cminBound :: InitFlag
Bounded, ReadPrec [InitFlag]
ReadPrec InitFlag
Int -> ReadS InitFlag
ReadS [InitFlag]
(Int -> ReadS InitFlag)
-> ReadS [InitFlag]
-> ReadPrec InitFlag
-> ReadPrec [InitFlag]
-> Read InitFlag
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [InitFlag]
$creadListPrec :: ReadPrec [InitFlag]
readPrec :: ReadPrec InitFlag
$creadPrec :: ReadPrec InitFlag
readList :: ReadS [InitFlag]
$creadList :: ReadS [InitFlag]
readsPrec :: Int -> ReadS InitFlag
$creadsPrec :: Int -> ReadS InitFlag
Read, Int -> InitFlag -> ShowS
[InitFlag] -> ShowS
InitFlag -> String
(Int -> InitFlag -> ShowS)
-> (InitFlag -> String) -> ([InitFlag] -> ShowS) -> Show InitFlag
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [InitFlag] -> ShowS
$cshowList :: [InitFlag] -> ShowS
show :: InitFlag -> String
$cshow :: InitFlag -> String
showsPrec :: Int -> InitFlag -> ShowS
$cshowsPrec :: Int -> InitFlag -> ShowS
Show)

initToCInt :: InitFlag -> CInt
initToCInt :: InitFlag -> CInt
initToCInt = \case
  InitFlag
InitFLAC -> CInt
forall a. (Eq a, Num a) => a
SDL.Raw.Mixer.INIT_FLAC
  InitFlag
InitMOD -> CInt
forall a. (Eq a, Num a) => a
SDL.Raw.Mixer.INIT_MOD
  InitFlag
InitMP3 -> CInt
forall a. (Eq a, Num a) => a
SDL.Raw.Mixer.INIT_MP3
  InitFlag
InitOGG -> CInt
forall a. (Eq a, Num a) => a
SDL.Raw.Mixer.INIT_OGG

-- | Cleans up any loaded libraries, freeing memory.
quit :: MonadIO m => m ()
quit :: m ()
quit = m ()
forall (m :: * -> *). MonadIO m => m ()
SDL.Raw.Mixer.quit -- FIXME: May not free all init'd libs! Check docs.

-- | Gets the major, minor, patch versions of the linked @SDL2_mixer@ library.
version :: (Integral a, MonadIO m) => m (a, a, a)
version :: m (a, a, a)
version = IO (a, a, a) -> m (a, a, a)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (a, a, a) -> m (a, a, a)) -> IO (a, a, a) -> m (a, a, a)
forall a b. (a -> b) -> a -> b
$ do
  SDL.Raw.Version Word8
major Word8
minor Word8
patch <- Ptr Version -> IO Version
forall a. Storable a => Ptr a -> IO a
peek (Ptr Version -> IO Version) -> IO (Ptr Version) -> IO Version
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< IO (Ptr Version)
forall (m :: * -> *). MonadIO m => m (Ptr Version)
SDL.Raw.Mixer.getVersion
  (a, a, a) -> IO (a, a, a)
forall (m :: * -> *) a. Monad m => a -> m a
return (Word8 -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
major, Word8 -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
minor, Word8 -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
patch)

-- | Initializes the @SDL2_mixer@ API.
--
-- This should be the first function you call after initializing @SDL@ itself
-- with 'SDL.Init.InitAudio'.
--
-- Automatically cleans up the API when the inner computation finishes.
withAudio ::
  (MonadBaseControl IO m, MonadIO m) => Audio -> ChunkSize -> m a -> m a
withAudio :: Audio -> Int -> m a -> m a
withAudio Audio
conf Int
csize m a
act = do
  Audio -> Int -> m ()
forall (m :: * -> *). MonadIO m => Audio -> Int -> m ()
openAudio Audio
conf Int
csize
  m a -> m () -> m a
forall (m :: * -> *) a b.
MonadBaseControl IO m =>
m a -> m b -> m a
finally m a
act m ()
forall (m :: * -> *). MonadIO m => m ()
closeAudio

-- | An alternative to 'withAudio', also initializes the @SDL2_mixer@ API.
--
-- However, 'openAudio' does not take care of automatically calling
-- 'closeAudio' after a computation ends, so you have to take care to do so
-- manually.
openAudio :: MonadIO m => Audio -> ChunkSize -> m ()
openAudio :: Audio -> Int -> m ()
openAudio Audio {Int
Output
Format
audioOutput :: Audio -> Output
audioFormat :: Audio -> Format
audioFrequency :: Audio -> Int
audioOutput :: Output
audioFormat :: Format
audioFrequency :: Int
..} Int
chunkSize =
  Text -> Text -> m CInt -> m ()
forall (m :: * -> *) a.
(MonadIO m, Num a, Ord a) =>
Text -> Text -> m a -> m ()
throwIfNeg_ Text
"SDL.Mixer.openAudio" Text
"Mix_OpenAudio" (m CInt -> m ()) -> m CInt -> m ()
forall a b. (a -> b) -> a -> b
$
    CInt -> Format -> CInt -> CInt -> m CInt
forall (m :: * -> *).
MonadIO m =>
CInt -> Format -> CInt -> CInt -> m CInt
SDL.Raw.Mixer.openAudio
      (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
audioFrequency)
      (Format -> Format
formatToWord Format
audioFormat)
      (Output -> CInt
outputToCInt Output
audioOutput)
      (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
chunkSize)

-- | An audio configuration. Use this with 'withAudio'.
data Audio = Audio
  { -- | A sampling frequency.
    Audio -> Int
audioFrequency :: Int,
    -- | An output sample format.
    Audio -> Format
audioFormat :: Format,
    -- | 'Mono' or 'Stereo' output.
    Audio -> Output
audioOutput :: Output
  }
  deriving stock (Audio -> Audio -> Bool
(Audio -> Audio -> Bool) -> (Audio -> Audio -> Bool) -> Eq Audio
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Audio -> Audio -> Bool
$c/= :: Audio -> Audio -> Bool
== :: Audio -> Audio -> Bool
$c== :: Audio -> Audio -> Bool
Eq, ReadPrec [Audio]
ReadPrec Audio
Int -> ReadS Audio
ReadS [Audio]
(Int -> ReadS Audio)
-> ReadS [Audio]
-> ReadPrec Audio
-> ReadPrec [Audio]
-> Read Audio
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [Audio]
$creadListPrec :: ReadPrec [Audio]
readPrec :: ReadPrec Audio
$creadPrec :: ReadPrec Audio
readList :: ReadS [Audio]
$creadList :: ReadS [Audio]
readsPrec :: Int -> ReadS Audio
$creadsPrec :: Int -> ReadS Audio
Read, Int -> Audio -> ShowS
[Audio] -> ShowS
Audio -> String
(Int -> Audio -> ShowS)
-> (Audio -> String) -> ([Audio] -> ShowS) -> Show Audio
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Audio] -> ShowS
$cshowList :: [Audio] -> ShowS
show :: Audio -> String
$cshow :: Audio -> String
showsPrec :: Int -> Audio -> ShowS
$cshowsPrec :: Int -> Audio -> ShowS
Show)

instance Default Audio where
  def :: Audio
def =
    Audio :: Int -> Format -> Output -> Audio
Audio
      { audioFrequency :: Int
audioFrequency = Int
forall a. (Eq a, Num a) => a
SDL.Raw.Mixer.DEFAULT_FREQUENCY,
        audioFormat :: Format
audioFormat = Format -> Format
wordToFormat Format
forall a. (Eq a, Num a) => a
SDL.Raw.Mixer.DEFAULT_FORMAT,
        audioOutput :: Output
audioOutput = CInt -> Output
cIntToOutput CInt
forall a. (Eq a, Num a) => a
SDL.Raw.Mixer.DEFAULT_CHANNELS
      }

-- | A default 'Audio' configuration.
--
-- Same as 'Data.Default.Class.def'.
--
-- Uses 22050 as the 'audioFrequency', 'FormatS16_Sys' as the 'audioFormat' and
-- 'Stereo' as the 'audioOutput'.
defaultAudio :: Audio
defaultAudio :: Audio
defaultAudio = Audio
forall a. Default a => a
def

-- | The size of each mixed sample.
--
-- The smaller this is, the more often callbacks will be invoked. If this is
-- made too small on a slow system, the sounds may skip. If made too large,
-- sound effects could lag.
type ChunkSize = Int

-- | A sample format.
data Format
  = -- | Unsigned 8-bit samples.
    FormatU8
  | -- | Signed 8-bit samples.
    FormatS8
  | -- | Unsigned 16-bit samples, in little-endian byte order.
    FormatU16_LSB
  | -- | Signed 16-bit samples, in little-endian byte order.
    FormatS16_LSB
  | -- | Unsigned 16-bit samples, in big-endian byte order.
    FormatU16_MSB
  | -- | signed 16-bit samples, in big-endian byte order.
    FormatS16_MSB
  | -- | Unsigned 16-bit samples, in system byte order.
    FormatU16_Sys
  | -- | Signed 16-bit samples, in system byte order.
    FormatS16_Sys
  deriving stock (Format -> Format -> Bool
(Format -> Format -> Bool)
-> (Format -> Format -> Bool) -> Eq Format
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Format -> Format -> Bool
$c/= :: Format -> Format -> Bool
== :: Format -> Format -> Bool
$c== :: Format -> Format -> Bool
Eq, Eq Format
Eq Format
-> (Format -> Format -> Ordering)
-> (Format -> Format -> Bool)
-> (Format -> Format -> Bool)
-> (Format -> Format -> Bool)
-> (Format -> Format -> Bool)
-> (Format -> Format -> Format)
-> (Format -> Format -> Format)
-> Ord Format
Format -> Format -> Bool
Format -> Format -> Ordering
Format -> Format -> Format
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Format -> Format -> Format
$cmin :: Format -> Format -> Format
max :: Format -> Format -> Format
$cmax :: Format -> Format -> Format
>= :: Format -> Format -> Bool
$c>= :: Format -> Format -> Bool
> :: Format -> Format -> Bool
$c> :: Format -> Format -> Bool
<= :: Format -> Format -> Bool
$c<= :: Format -> Format -> Bool
< :: Format -> Format -> Bool
$c< :: Format -> Format -> Bool
compare :: Format -> Format -> Ordering
$ccompare :: Format -> Format -> Ordering
$cp1Ord :: Eq Format
Ord, Format
Format -> Format -> Bounded Format
forall a. a -> a -> Bounded a
maxBound :: Format
$cmaxBound :: Format
minBound :: Format
$cminBound :: Format
Bounded, ReadPrec [Format]
ReadPrec Format
Int -> ReadS Format
ReadS [Format]
(Int -> ReadS Format)
-> ReadS [Format]
-> ReadPrec Format
-> ReadPrec [Format]
-> Read Format
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [Format]
$creadListPrec :: ReadPrec [Format]
readPrec :: ReadPrec Format
$creadPrec :: ReadPrec Format
readList :: ReadS [Format]
$creadList :: ReadS [Format]
readsPrec :: Int -> ReadS Format
$creadsPrec :: Int -> ReadS Format
Read, Int -> Format -> ShowS
[Format] -> ShowS
Format -> String
(Int -> Format -> ShowS)
-> (Format -> String) -> ([Format] -> ShowS) -> Show Format
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Format] -> ShowS
$cshowList :: [Format] -> ShowS
show :: Format -> String
$cshow :: Format -> String
showsPrec :: Int -> Format -> ShowS
$cshowsPrec :: Int -> Format -> ShowS
Show)

formatToWord :: Format -> SDL.Raw.Mixer.Format
formatToWord :: Format -> Format
formatToWord = \case
  Format
FormatU8 -> Format
forall a. (Eq a, Num a) => a
SDL.Raw.Mixer.AUDIO_U8
  Format
FormatS8 -> Format
forall a. (Eq a, Num a) => a
SDL.Raw.Mixer.AUDIO_S8
  Format
FormatU16_LSB -> Format
forall a. (Eq a, Num a) => a
SDL.Raw.Mixer.AUDIO_U16LSB
  Format
FormatS16_LSB -> Format
forall a. (Eq a, Num a) => a
SDL.Raw.Mixer.AUDIO_S16LSB
  Format
FormatU16_MSB -> Format
forall a. (Eq a, Num a) => a
SDL.Raw.Mixer.AUDIO_U16MSB
  Format
FormatS16_MSB -> Format
forall a. (Eq a, Num a) => a
SDL.Raw.Mixer.AUDIO_S16MSB
  Format
FormatU16_Sys -> Format
forall a. (Eq a, Num a) => a
SDL.Raw.Mixer.AUDIO_U16SYS
  Format
FormatS16_Sys -> Format
forall a. (Eq a, Num a) => a
SDL.Raw.Mixer.AUDIO_S16SYS

wordToFormat :: SDL.Raw.Mixer.Format -> Format
wordToFormat :: Format -> Format
wordToFormat = \case
  Format
SDL.Raw.Mixer.AUDIO_U8 -> Format
FormatU8
  Format
SDL.Raw.Mixer.AUDIO_S8 -> Format
FormatS8
  Format
SDL.Raw.Mixer.AUDIO_U16LSB -> Format
FormatU16_LSB
  Format
SDL.Raw.Mixer.AUDIO_S16LSB -> Format
FormatS16_LSB
  Format
SDL.Raw.Mixer.AUDIO_U16MSB -> Format
FormatU16_MSB
  Format
SDL.Raw.Mixer.AUDIO_S16MSB -> Format
FormatS16_MSB
  Format
SDL.Raw.Mixer.AUDIO_U16SYS -> Format
FormatU16_Sys
  Format
SDL.Raw.Mixer.AUDIO_S16SYS -> Format
FormatS16_Sys
  Format
_ -> String -> Format
forall a. HasCallStack => String -> a
error String
"SDL.Mixer.wordToFormat: unknown Format."

-- | The number of sound channels in output.
data Output = Mono | Stereo
  deriving stock (Output -> Output -> Bool
(Output -> Output -> Bool)
-> (Output -> Output -> Bool) -> Eq Output
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Output -> Output -> Bool
$c/= :: Output -> Output -> Bool
== :: Output -> Output -> Bool
$c== :: Output -> Output -> Bool
Eq, Eq Output
Eq Output
-> (Output -> Output -> Ordering)
-> (Output -> Output -> Bool)
-> (Output -> Output -> Bool)
-> (Output -> Output -> Bool)
-> (Output -> Output -> Bool)
-> (Output -> Output -> Output)
-> (Output -> Output -> Output)
-> Ord Output
Output -> Output -> Bool
Output -> Output -> Ordering
Output -> Output -> Output
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Output -> Output -> Output
$cmin :: Output -> Output -> Output
max :: Output -> Output -> Output
$cmax :: Output -> Output -> Output
>= :: Output -> Output -> Bool
$c>= :: Output -> Output -> Bool
> :: Output -> Output -> Bool
$c> :: Output -> Output -> Bool
<= :: Output -> Output -> Bool
$c<= :: Output -> Output -> Bool
< :: Output -> Output -> Bool
$c< :: Output -> Output -> Bool
compare :: Output -> Output -> Ordering
$ccompare :: Output -> Output -> Ordering
$cp1Ord :: Eq Output
Ord, Output
Output -> Output -> Bounded Output
forall a. a -> a -> Bounded a
maxBound :: Output
$cmaxBound :: Output
minBound :: Output
$cminBound :: Output
Bounded, ReadPrec [Output]
ReadPrec Output
Int -> ReadS Output
ReadS [Output]
(Int -> ReadS Output)
-> ReadS [Output]
-> ReadPrec Output
-> ReadPrec [Output]
-> Read Output
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [Output]
$creadListPrec :: ReadPrec [Output]
readPrec :: ReadPrec Output
$creadPrec :: ReadPrec Output
readList :: ReadS [Output]
$creadList :: ReadS [Output]
readsPrec :: Int -> ReadS Output
$creadsPrec :: Int -> ReadS Output
Read, Int -> Output -> ShowS
[Output] -> ShowS
Output -> String
(Int -> Output -> ShowS)
-> (Output -> String) -> ([Output] -> ShowS) -> Show Output
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Output] -> ShowS
$cshowList :: [Output] -> ShowS
show :: Output -> String
$cshow :: Output -> String
showsPrec :: Int -> Output -> ShowS
$cshowsPrec :: Int -> Output -> ShowS
Show)

outputToCInt :: Output -> CInt
outputToCInt :: Output -> CInt
outputToCInt = \case
  Output
Mono -> CInt
1
  Output
Stereo -> CInt
2

cIntToOutput :: CInt -> Output
cIntToOutput :: CInt -> Output
cIntToOutput = \case
  CInt
1 -> Output
Mono
  CInt
2 -> Output
Stereo
  CInt
_ -> String -> Output
forall a. HasCallStack => String -> a
error String
"SDL.Mixer.cIntToOutput: unknown number of channels."

-- | Get the audio format in use by the opened audio device.
--
-- This may or may not match the 'Audio' you asked for when calling
-- 'withAudio'.
queryAudio :: MonadIO m => m Audio
queryAudio :: m Audio
queryAudio =
  IO Audio -> m Audio
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO
    (IO Audio -> m Audio)
-> ((Ptr CInt -> IO Audio) -> IO Audio)
-> (Ptr CInt -> IO Audio)
-> m Audio
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Ptr CInt -> IO Audio) -> IO Audio
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca
    ((Ptr CInt -> IO Audio) -> m Audio)
-> (Ptr CInt -> IO Audio) -> m Audio
forall a b. (a -> b) -> a -> b
$ \Ptr CInt
freq ->
      (Ptr Format -> IO Audio) -> IO Audio
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr Format -> IO Audio) -> IO Audio)
-> (Ptr Format -> IO Audio) -> IO Audio
forall a b. (a -> b) -> a -> b
$ \Ptr Format
form ->
        (Ptr CInt -> IO Audio) -> IO Audio
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr CInt -> IO Audio) -> IO Audio)
-> (Ptr CInt -> IO Audio) -> IO Audio
forall a b. (a -> b) -> a -> b
$ \Ptr CInt
chan -> do
          IO CInt -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO CInt -> IO ()) -> (IO CInt -> IO CInt) -> IO CInt -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text -> IO CInt -> IO CInt
forall a (m :: * -> *).
(Eq a, MonadIO m, Num a) =>
Text -> Text -> m a -> m a
throwIf0 Text
"SDL.Mixer.queryAudio" Text
"Mix_QuerySpec" (IO CInt -> IO ()) -> IO CInt -> IO ()
forall a b. (a -> b) -> a -> b
$
            Ptr CInt -> Ptr Format -> Ptr CInt -> IO CInt
forall (m :: * -> *).
MonadIO m =>
Ptr CInt -> Ptr Format -> Ptr CInt -> m CInt
SDL.Raw.Mixer.querySpec Ptr CInt
freq Ptr Format
form Ptr CInt
chan
          Int -> Format -> Output -> Audio
Audio
            (Int -> Format -> Output -> Audio)
-> IO Int -> IO (Format -> Output -> Audio)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (CInt -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (CInt -> Int) -> IO CInt -> IO Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Ptr CInt -> IO CInt
forall a. Storable a => Ptr a -> IO a
peek Ptr CInt
freq)
            IO (Format -> Output -> Audio) -> IO Format -> IO (Output -> Audio)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> (Format -> Format
wordToFormat (Format -> Format) -> IO Format -> IO Format
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Ptr Format -> IO Format
forall a. Storable a => Ptr a -> IO a
peek Ptr Format
form)
            IO (Output -> Audio) -> IO Output -> IO Audio
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> (CInt -> Output
cIntToOutput (CInt -> Output) -> IO CInt -> IO Output
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Ptr CInt -> IO CInt
forall a. Storable a => Ptr a -> IO a
peek Ptr CInt
chan)

-- | Shut down and clean up the @SDL2_mixer@ API.
--
-- After calling this, all audio stops.
--
-- You don't have to call this if you're using 'withAudio'.
closeAudio :: MonadIO m => m ()
closeAudio :: m ()
closeAudio = m ()
forall (m :: * -> *). MonadIO m => m ()
SDL.Raw.Mixer.closeAudio

-- | A class of all values that can be loaded from some source. You can load
-- both 'Chunk's and 'Music' this way.
--
-- Note that you must call 'withAudio' before using these, since they have to
-- know the audio configuration to properly convert the data for playback.
class Loadable a where
  -- | Load the value from a 'ByteString'.
  decode :: MonadIO m => ByteString -> m a

  -- | Same as 'decode', but loads from a file instead.
  load :: MonadIO m => FilePath -> m a
  load = ByteString -> m a
forall a (m :: * -> *).
(Loadable a, MonadIO m) =>
ByteString -> m a
decode (ByteString -> m a) -> (String -> m ByteString) -> String -> m a
forall (m :: * -> *) b c a.
Monad m =>
(b -> m c) -> (a -> m b) -> a -> m c
<=< (IO ByteString -> m ByteString
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO ByteString -> m ByteString)
-> (String -> IO ByteString) -> String -> m ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> IO ByteString
BS.readFile)

  -- | Frees the value's memory. It should no longer be used.
  --
  -- __Note that you shouldn't free those values that are currently playing.__
  free :: MonadIO m => a -> m ()

-- | A volume, where 0 is silent and 128 loudest.
--
-- 'Volume's lesser than 0 or greater than 128 function as if they are 0 and
-- 128, respectively.
type Volume = Int

volumeToCInt :: Volume -> CInt
volumeToCInt :: Int -> CInt
volumeToCInt = Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> CInt) -> (Int -> Int) -> Int -> CInt
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 (Int -> Int) -> (Int -> Int) -> Int -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Int -> Int
forall a. Ord a => a -> a -> a
min Int
128

-- | A class of all values that have a 'Volume'.
class HasVolume a where
  -- | Gets the value's currently set 'Volume'.
  --
  -- If the value is a 'Channel' and 'AllChannels' is used, gets the /average/
  -- 'Volume' of all 'Channel's.
  getVolume :: MonadIO m => a -> m Volume

  -- | Sets a value's 'Volume'.
  --
  -- If the value is a 'Chunk', the volume setting only takes effect when the
  -- 'Chunk' is used on a 'Channel', being mixed into the output.
  --
  -- In case of being used on a 'Channel', the volume setting takes effect
  -- during the final mix, along with the 'Chunk' volume. For instance, setting
  -- the 'Volume' of a certain 'Channel' to 64 will halve the volume of all
  -- 'Chunk's played on that 'Channel'. If 'AllChannels' is used, sets all
  -- 'Channel's to the given 'Volume' instead.
  setVolume :: MonadIO m => Volume -> a -> m ()

-- | Returns the names of all chunk decoders currently available.
--
-- These depend on the availability of shared libraries for each of the
-- formats. The list may contain any of the following, and possibly others:
-- @WAVE@, @AIFF@, @VOC@, @OFF@, @FLAC@, @MP3@.
chunkDecoders :: MonadIO m => m [String]
chunkDecoders :: m [String]
chunkDecoders =
  IO [String] -> m [String]
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO [String] -> m [String]) -> IO [String] -> m [String]
forall a b. (a -> b) -> a -> b
$ do
    CInt
num <- IO CInt
forall (m :: * -> *). MonadIO m => m CInt
SDL.Raw.Mixer.getNumChunkDecoders
    [CInt] -> (CInt -> IO String) -> IO [String]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM [CInt
0 .. CInt
num CInt -> CInt -> CInt
forall a. Num a => a -> a -> a
- CInt
1] ((CInt -> IO String) -> IO [String])
-> (CInt -> IO String) -> IO [String]
forall a b. (a -> b) -> a -> b
$ CInt -> IO CString
forall (m :: * -> *). MonadIO m => CInt -> m CString
SDL.Raw.Mixer.getChunkDecoder (CInt -> IO CString) -> (CString -> IO String) -> CInt -> IO String
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> CString -> IO String
peekCString

-- | A loaded audio chunk.
newtype Chunk = Chunk (Ptr SDL.Raw.Mixer.Chunk)
  deriving stock (Chunk -> Chunk -> Bool
(Chunk -> Chunk -> Bool) -> (Chunk -> Chunk -> Bool) -> Eq Chunk
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Chunk -> Chunk -> Bool
$c/= :: Chunk -> Chunk -> Bool
== :: Chunk -> Chunk -> Bool
$c== :: Chunk -> Chunk -> Bool
Eq, Int -> Chunk -> ShowS
[Chunk] -> ShowS
Chunk -> String
(Int -> Chunk -> ShowS)
-> (Chunk -> String) -> ([Chunk] -> ShowS) -> Show Chunk
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Chunk] -> ShowS
$cshowList :: [Chunk] -> ShowS
show :: Chunk -> String
$cshow :: Chunk -> String
showsPrec :: Int -> Chunk -> ShowS
$cshowsPrec :: Int -> Chunk -> ShowS
Show)

instance Loadable Chunk where
  decode :: ByteString -> m Chunk
decode ByteString
bytes = IO Chunk -> m Chunk
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Chunk -> m Chunk) -> IO Chunk -> m Chunk
forall a b. (a -> b) -> a -> b
$ do
    ByteString -> (CStringLen -> IO Chunk) -> IO Chunk
forall a. ByteString -> (CStringLen -> IO a) -> IO a
unsafeUseAsCStringLen ByteString
bytes ((CStringLen -> IO Chunk) -> IO Chunk)
-> (CStringLen -> IO Chunk) -> IO Chunk
forall a b. (a -> b) -> a -> b
$ \(CString
cstr, Int
len) -> do
      Ptr RWops
rw <- Ptr () -> CInt -> IO (Ptr RWops)
forall (m :: * -> *). MonadIO m => Ptr () -> CInt -> m (Ptr RWops)
rwFromConstMem (CString -> Ptr ()
forall a b. Ptr a -> Ptr b
castPtr CString
cstr) (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
len)
      (Ptr Chunk -> Chunk) -> IO (Ptr Chunk) -> IO Chunk
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Ptr Chunk -> Chunk
Chunk
        (IO (Ptr Chunk) -> IO Chunk)
-> (IO (Ptr Chunk) -> IO (Ptr Chunk)) -> IO (Ptr Chunk) -> IO Chunk
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text -> IO (Ptr Chunk) -> IO (Ptr Chunk)
forall (m :: * -> *) a.
MonadIO m =>
Text -> Text -> m (Ptr a) -> m (Ptr a)
throwIfNull Text
"SDL.Mixer.decode<Chunk>" Text
"Mix_LoadWAV_RW"
        (IO (Ptr Chunk) -> IO Chunk) -> IO (Ptr Chunk) -> IO Chunk
forall a b. (a -> b) -> a -> b
$ Ptr RWops -> CInt -> IO (Ptr Chunk)
forall (m :: * -> *).
MonadIO m =>
Ptr RWops -> CInt -> m (Ptr Chunk)
SDL.Raw.Mixer.loadWAV_RW Ptr RWops
rw CInt
0

  free :: Chunk -> m ()
free (Chunk Ptr Chunk
p) = IO () -> m ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> IO () -> m ()
forall a b. (a -> b) -> a -> b
$ Ptr Chunk -> IO ()
forall (m :: * -> *). MonadIO m => Ptr Chunk -> m ()
SDL.Raw.Mixer.freeChunk Ptr Chunk
p

instance HasVolume Chunk where
  getVolume :: Chunk -> m Int
getVolume (Chunk Ptr Chunk
p) = CInt -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (CInt -> Int) -> m CInt -> m Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Ptr Chunk -> CInt -> m CInt
forall (m :: * -> *). MonadIO m => Ptr Chunk -> CInt -> m CInt
SDL.Raw.Mixer.volumeChunk Ptr Chunk
p (-CInt
1)
  setVolume :: Int -> Chunk -> m ()
setVolume Int
v (Chunk Ptr Chunk
p) = m CInt -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m CInt -> m ()) -> (CInt -> m CInt) -> CInt -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Ptr Chunk -> CInt -> m CInt
forall (m :: * -> *). MonadIO m => Ptr Chunk -> CInt -> m CInt
SDL.Raw.Mixer.volumeChunk Ptr Chunk
p (CInt -> m ()) -> CInt -> m ()
forall a b. (a -> b) -> a -> b
$ Int -> CInt
volumeToCInt Int
v

-- | A mixing channel.
--
-- Use the 'Integral' instance to define these: the first channel is 0, the
-- second 1 and so on.
--
-- The default number of 'Channel's available at startup is 8, so note that you
-- cannot usemore than these starting 8 if you haven't created more with
-- 'setChannels'.
--
-- The starting 'Volume' of each 'Channel' is the maximum: 128.
newtype Channel = Channel CInt
  deriving stock (Channel -> Channel -> Bool
(Channel -> Channel -> Bool)
-> (Channel -> Channel -> Bool) -> Eq Channel
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Channel -> Channel -> Bool
$c/= :: Channel -> Channel -> Bool
== :: Channel -> Channel -> Bool
$c== :: Channel -> Channel -> Bool
Eq, Eq Channel
Eq Channel
-> (Channel -> Channel -> Ordering)
-> (Channel -> Channel -> Bool)
-> (Channel -> Channel -> Bool)
-> (Channel -> Channel -> Bool)
-> (Channel -> Channel -> Bool)
-> (Channel -> Channel -> Channel)
-> (Channel -> Channel -> Channel)
-> Ord Channel
Channel -> Channel -> Bool
Channel -> Channel -> Ordering
Channel -> Channel -> Channel
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Channel -> Channel -> Channel
$cmin :: Channel -> Channel -> Channel
max :: Channel -> Channel -> Channel
$cmax :: Channel -> Channel -> Channel
>= :: Channel -> Channel -> Bool
$c>= :: Channel -> Channel -> Bool
> :: Channel -> Channel -> Bool
$c> :: Channel -> Channel -> Bool
<= :: Channel -> Channel -> Bool
$c<= :: Channel -> Channel -> Bool
< :: Channel -> Channel -> Bool
$c< :: Channel -> Channel -> Bool
compare :: Channel -> Channel -> Ordering
$ccompare :: Channel -> Channel -> Ordering
$cp1Ord :: Eq Channel
Ord)
  deriving newtype (Int -> Channel
Channel -> Int
Channel -> [Channel]
Channel -> Channel
Channel -> Channel -> [Channel]
Channel -> Channel -> Channel -> [Channel]
(Channel -> Channel)
-> (Channel -> Channel)
-> (Int -> Channel)
-> (Channel -> Int)
-> (Channel -> [Channel])
-> (Channel -> Channel -> [Channel])
-> (Channel -> Channel -> [Channel])
-> (Channel -> Channel -> Channel -> [Channel])
-> Enum Channel
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
enumFromThenTo :: Channel -> Channel -> Channel -> [Channel]
$cenumFromThenTo :: Channel -> Channel -> Channel -> [Channel]
enumFromTo :: Channel -> Channel -> [Channel]
$cenumFromTo :: Channel -> Channel -> [Channel]
enumFromThen :: Channel -> Channel -> [Channel]
$cenumFromThen :: Channel -> Channel -> [Channel]
enumFrom :: Channel -> [Channel]
$cenumFrom :: Channel -> [Channel]
fromEnum :: Channel -> Int
$cfromEnum :: Channel -> Int
toEnum :: Int -> Channel
$ctoEnum :: Int -> Channel
pred :: Channel -> Channel
$cpred :: Channel -> Channel
succ :: Channel -> Channel
$csucc :: Channel -> Channel
Enum, Enum Channel
Real Channel
Real Channel
-> Enum Channel
-> (Channel -> Channel -> Channel)
-> (Channel -> Channel -> Channel)
-> (Channel -> Channel -> Channel)
-> (Channel -> Channel -> Channel)
-> (Channel -> Channel -> (Channel, Channel))
-> (Channel -> Channel -> (Channel, Channel))
-> (Channel -> Integer)
-> Integral Channel
Channel -> Integer
Channel -> Channel -> (Channel, Channel)
Channel -> Channel -> Channel
forall a.
Real a
-> Enum a
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> (a, a))
-> (a -> a -> (a, a))
-> (a -> Integer)
-> Integral a
toInteger :: Channel -> Integer
$ctoInteger :: Channel -> Integer
divMod :: Channel -> Channel -> (Channel, Channel)
$cdivMod :: Channel -> Channel -> (Channel, Channel)
quotRem :: Channel -> Channel -> (Channel, Channel)
$cquotRem :: Channel -> Channel -> (Channel, Channel)
mod :: Channel -> Channel -> Channel
$cmod :: Channel -> Channel -> Channel
div :: Channel -> Channel -> Channel
$cdiv :: Channel -> Channel -> Channel
rem :: Channel -> Channel -> Channel
$crem :: Channel -> Channel -> Channel
quot :: Channel -> Channel -> Channel
$cquot :: Channel -> Channel -> Channel
$cp2Integral :: Enum Channel
$cp1Integral :: Real Channel
Integral, Num Channel
Ord Channel
Num Channel -> Ord Channel -> (Channel -> Rational) -> Real Channel
Channel -> Rational
forall a. Num a -> Ord a -> (a -> Rational) -> Real a
toRational :: Channel -> Rational
$ctoRational :: Channel -> Rational
$cp2Real :: Ord Channel
$cp1Real :: Num Channel
Real, Integer -> Channel
Channel -> Channel
Channel -> Channel -> Channel
(Channel -> Channel -> Channel)
-> (Channel -> Channel -> Channel)
-> (Channel -> Channel -> Channel)
-> (Channel -> Channel)
-> (Channel -> Channel)
-> (Channel -> Channel)
-> (Integer -> Channel)
-> Num Channel
forall a.
(a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a)
-> (a -> a)
-> (a -> a)
-> (Integer -> a)
-> Num a
fromInteger :: Integer -> Channel
$cfromInteger :: Integer -> Channel
signum :: Channel -> Channel
$csignum :: Channel -> Channel
abs :: Channel -> Channel
$cabs :: Channel -> Channel
negate :: Channel -> Channel
$cnegate :: Channel -> Channel
* :: Channel -> Channel -> Channel
$c* :: Channel -> Channel -> Channel
- :: Channel -> Channel -> Channel
$c- :: Channel -> Channel -> Channel
+ :: Channel -> Channel -> Channel
$c+ :: Channel -> Channel -> Channel
Num)

instance Show Channel where
  show :: Channel -> String
show = \case
    Channel
AllChannels -> String
"AllChannels"
    Channel CInt
c -> String
"Channel " String -> ShowS
forall a. [a] -> [a] -> [a]
++ CInt -> String
forall a. Show a => a -> String
show CInt
c

-- The lowest-numbered channel is CHANNEL_POST, or -2, for post processing
-- effects. This function makes sure a channel is higher than CHANNEL_POST.
clipChan :: CInt -> CInt
clipChan :: CInt -> CInt
clipChan = CInt -> CInt -> CInt
forall a. Ord a => a -> a -> a
max CInt
forall a. (Eq a, Num a) => a
SDL.Raw.Mixer.CHANNEL_POST

-- | Prepares a given number of 'Channel's for use.
--
-- There are 8 such 'Channel's already prepared for use after 'withAudio' is
-- called.
--
-- You may call this multiple times, even with sounds playing. If setting a
-- lesser number of 'Channel's than are currently in use, the higher 'Channel's
-- will be stopped, their finish callbacks invoked, and their memory freed.
-- Passing in 0 or less will therefore stop and free all mixing channels.
--
-- Any 'Music' playing is not affected by this function.
setChannels :: MonadIO m => Int -> m ()
setChannels :: Int -> m ()
setChannels = m CInt -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m CInt -> m ()) -> (Int -> m CInt) -> Int -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CInt -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> m CInt
SDL.Raw.Mixer.allocateChannels (CInt -> m CInt) -> (Int -> CInt) -> Int -> m CInt
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> CInt) -> (Int -> Int) -> Int -> CInt
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0

-- | Gets the number of 'Channel's currently in use.
getChannels :: MonadIO m => m Int
getChannels :: m Int
getChannels = CInt -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (CInt -> Int) -> m CInt -> m Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CInt -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> m CInt
SDL.Raw.Mixer.allocateChannels (-CInt
1)

-- | Reserve a given number of 'Channel's, starting from 'Channel' 0.
--
-- A reserved 'Channel' is considered not to be available for playing samples
-- when using any 'play' or 'fadeIn' function variant with 'AllChannels'. In
-- other words, whenever you let 'SDL.Mixer' pick the first available 'Channel'
-- itself, these reserved 'Channel's will not be considered.
reserveChannels :: MonadIO m => Int -> m Int
reserveChannels :: Int -> m Int
reserveChannels =
  (CInt -> Int) -> m CInt -> m Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap CInt -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (m CInt -> m Int) -> (Int -> m CInt) -> Int -> m Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CInt -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> m CInt
SDL.Raw.Mixer.reserveChannels (CInt -> m CInt) -> (Int -> CInt) -> Int -> m CInt
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral

-- | Gets the most recent 'Chunk' played on a 'Channel', if any.
--
-- Using 'AllChannels' is not valid here, and will return 'Nothing'.
--
-- Note that the returned 'Chunk' might be invalid if it was already 'free'd.
playedLast :: MonadIO m => Channel -> m (Maybe Chunk)
playedLast :: Channel -> m (Maybe Chunk)
playedLast (Channel CInt
c) = do
  Ptr Chunk
p <- CInt -> m (Ptr Chunk)
forall (m :: * -> *). MonadIO m => CInt -> m (Ptr Chunk)
SDL.Raw.Mixer.getChunk (CInt -> m (Ptr Chunk)) -> CInt -> m (Ptr Chunk)
forall a b. (a -> b) -> a -> b
$ CInt -> CInt
clipChan CInt
c
  Maybe Chunk -> m (Maybe Chunk)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe Chunk -> m (Maybe Chunk)) -> Maybe Chunk -> m (Maybe Chunk)
forall a b. (a -> b) -> a -> b
$ if Ptr Chunk
p Ptr Chunk -> Ptr Chunk -> Bool
forall a. Eq a => a -> a -> Bool
== Ptr Chunk
forall a. Ptr a
nullPtr then Maybe Chunk
forall a. Maybe a
Nothing else Chunk -> Maybe Chunk
forall a. a -> Maybe a
Just (Ptr Chunk -> Chunk
Chunk Ptr Chunk
p)

-- | Use this value when you wish to perform an operation on /all/ 'Channel's.
--
-- For more information, see each of the functions accepting a 'Channel'.
pattern AllChannels :: Channel
pattern $bAllChannels :: Channel
$mAllChannels :: forall r. Channel -> (Void# -> r) -> (Void# -> r) -> r
AllChannels = -1

instance HasVolume Channel where
  setVolume :: Int -> Channel -> m ()
setVolume Int
v (Channel CInt
c) =
    m CInt -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m CInt -> m ()) -> (CInt -> m CInt) -> CInt -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CInt -> CInt -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> CInt -> m CInt
SDL.Raw.Mixer.volume (CInt -> CInt
clipChan CInt
c) (CInt -> m ()) -> CInt -> m ()
forall a b. (a -> b) -> a -> b
$ Int -> CInt
volumeToCInt Int
v
  getVolume :: Channel -> m Int
getVolume (Channel CInt
c) =
    CInt -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (CInt -> Int) -> m CInt -> m Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CInt -> CInt -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> CInt -> m CInt
SDL.Raw.Mixer.volume (CInt -> CInt
clipChan CInt
c) (-CInt
1)

-- | Play a 'Chunk' once, using the first available 'Channel'.
play :: MonadIO m => Chunk -> m ()
play :: Chunk -> m ()
play = m Channel -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m Channel -> m ()) -> (Chunk -> m Channel) -> Chunk -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Channel -> Times -> Chunk -> m Channel
forall (m :: * -> *).
MonadIO m =>
Channel -> Times -> Chunk -> m Channel
playOn (-Channel
1) Times
Once

-- | Same as 'play', but keeps playing the 'Chunk' forever.
playForever :: MonadIO m => Chunk -> m ()
playForever :: Chunk -> m ()
playForever = m Channel -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m Channel -> m ()) -> (Chunk -> m Channel) -> Chunk -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Channel -> Times -> Chunk -> m Channel
forall (m :: * -> *).
MonadIO m =>
Channel -> Times -> Chunk -> m Channel
playOn (-Channel
1) Times
Forever

-- | How many times should a certain 'Chunk' be played?
newtype Times = Times CInt
  deriving stock (Times -> Times -> Bool
(Times -> Times -> Bool) -> (Times -> Times -> Bool) -> Eq Times
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Times -> Times -> Bool
$c/= :: Times -> Times -> Bool
== :: Times -> Times -> Bool
$c== :: Times -> Times -> Bool
Eq, Eq Times
Eq Times
-> (Times -> Times -> Ordering)
-> (Times -> Times -> Bool)
-> (Times -> Times -> Bool)
-> (Times -> Times -> Bool)
-> (Times -> Times -> Bool)
-> (Times -> Times -> Times)
-> (Times -> Times -> Times)
-> Ord Times
Times -> Times -> Bool
Times -> Times -> Ordering
Times -> Times -> Times
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Times -> Times -> Times
$cmin :: Times -> Times -> Times
max :: Times -> Times -> Times
$cmax :: Times -> Times -> Times
>= :: Times -> Times -> Bool
$c>= :: Times -> Times -> Bool
> :: Times -> Times -> Bool
$c> :: Times -> Times -> Bool
<= :: Times -> Times -> Bool
$c<= :: Times -> Times -> Bool
< :: Times -> Times -> Bool
$c< :: Times -> Times -> Bool
compare :: Times -> Times -> Ordering
$ccompare :: Times -> Times -> Ordering
$cp1Ord :: Eq Times
Ord)
  deriving newtype (Int -> Times
Times -> Int
Times -> [Times]
Times -> Times
Times -> Times -> [Times]
Times -> Times -> Times -> [Times]
(Times -> Times)
-> (Times -> Times)
-> (Int -> Times)
-> (Times -> Int)
-> (Times -> [Times])
-> (Times -> Times -> [Times])
-> (Times -> Times -> [Times])
-> (Times -> Times -> Times -> [Times])
-> Enum Times
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
enumFromThenTo :: Times -> Times -> Times -> [Times]
$cenumFromThenTo :: Times -> Times -> Times -> [Times]
enumFromTo :: Times -> Times -> [Times]
$cenumFromTo :: Times -> Times -> [Times]
enumFromThen :: Times -> Times -> [Times]
$cenumFromThen :: Times -> Times -> [Times]
enumFrom :: Times -> [Times]
$cenumFrom :: Times -> [Times]
fromEnum :: Times -> Int
$cfromEnum :: Times -> Int
toEnum :: Int -> Times
$ctoEnum :: Int -> Times
pred :: Times -> Times
$cpred :: Times -> Times
succ :: Times -> Times
$csucc :: Times -> Times
Enum, Enum Times
Real Times
Real Times
-> Enum Times
-> (Times -> Times -> Times)
-> (Times -> Times -> Times)
-> (Times -> Times -> Times)
-> (Times -> Times -> Times)
-> (Times -> Times -> (Times, Times))
-> (Times -> Times -> (Times, Times))
-> (Times -> Integer)
-> Integral Times
Times -> Integer
Times -> Times -> (Times, Times)
Times -> Times -> Times
forall a.
Real a
-> Enum a
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> (a, a))
-> (a -> a -> (a, a))
-> (a -> Integer)
-> Integral a
toInteger :: Times -> Integer
$ctoInteger :: Times -> Integer
divMod :: Times -> Times -> (Times, Times)
$cdivMod :: Times -> Times -> (Times, Times)
quotRem :: Times -> Times -> (Times, Times)
$cquotRem :: Times -> Times -> (Times, Times)
mod :: Times -> Times -> Times
$cmod :: Times -> Times -> Times
div :: Times -> Times -> Times
$cdiv :: Times -> Times -> Times
rem :: Times -> Times -> Times
$crem :: Times -> Times -> Times
quot :: Times -> Times -> Times
$cquot :: Times -> Times -> Times
$cp2Integral :: Enum Times
$cp1Integral :: Real Times
Integral, Num Times
Ord Times
Num Times -> Ord Times -> (Times -> Rational) -> Real Times
Times -> Rational
forall a. Num a -> Ord a -> (a -> Rational) -> Real a
toRational :: Times -> Rational
$ctoRational :: Times -> Rational
$cp2Real :: Ord Times
$cp1Real :: Num Times
Real, Integer -> Times
Times -> Times
Times -> Times -> Times
(Times -> Times -> Times)
-> (Times -> Times -> Times)
-> (Times -> Times -> Times)
-> (Times -> Times)
-> (Times -> Times)
-> (Times -> Times)
-> (Integer -> Times)
-> Num Times
forall a.
(a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a)
-> (a -> a)
-> (a -> a)
-> (Integer -> a)
-> Num a
fromInteger :: Integer -> Times
$cfromInteger :: Integer -> Times
signum :: Times -> Times
$csignum :: Times -> Times
abs :: Times -> Times
$cabs :: Times -> Times
negate :: Times -> Times
$cnegate :: Times -> Times
* :: Times -> Times -> Times
$c* :: Times -> Times -> Times
- :: Times -> Times -> Times
$c- :: Times -> Times -> Times
+ :: Times -> Times -> Times
$c+ :: Times -> Times -> Times
Num)

-- | A shorthand for playing once.
pattern Once :: Times
pattern $bOnce :: Times
$mOnce :: forall r. Times -> (Void# -> r) -> (Void# -> r) -> r
Once = 1

-- | A shorthand for looping a 'Chunk' forever.
pattern Forever :: Times
pattern $bForever :: Times
$mForever :: forall r. Times -> (Void# -> r) -> (Void# -> r) -> r
Forever = 0

-- | Same as 'play', but plays the 'Chunk' using a given 'Channel' a certain
-- number of 'Times'.
--
-- If 'AllChannels' is used, then plays the 'Chunk' using the first available
-- 'Channel' instead.
--
-- Returns the 'Channel' that was used.
playOn :: MonadIO m => Channel -> Times -> Chunk -> m Channel
playOn :: Channel -> Times -> Chunk -> m Channel
playOn = Int -> Channel -> Times -> Chunk -> m Channel
forall (m :: * -> *).
MonadIO m =>
Int -> Channel -> Times -> Chunk -> m Channel
playLimit Int
NoLimit

-- | A time in milliseconds.
type Milliseconds = Int

-- | An upper limit of time, in milliseconds.
type Limit = Milliseconds

-- | A lack of an upper limit.
pattern NoLimit :: Limit
pattern $bNoLimit :: Int
$mNoLimit :: forall r. Int -> (Void# -> r) -> (Void# -> r) -> r
NoLimit = -1

-- | Same as 'playOn', but imposes an upper limit in 'Milliseconds' to how long
-- the 'Chunk' can play.
--
-- The playing may still stop before the limit is reached.
--
-- This is the most generic play function variant.
playLimit :: MonadIO m => Limit -> Channel -> Times -> Chunk -> m Channel
playLimit :: Int -> Channel -> Times -> Chunk -> m Channel
playLimit Int
l (Channel CInt
c) (Times CInt
t) (Chunk Ptr Chunk
p) =
  Text -> Text -> m Channel -> m Channel
forall (m :: * -> *) a.
(MonadIO m, Num a, Ord a) =>
Text -> Text -> m a -> m a
throwIfNeg
    Text
"SDL.Mixer.playLimit"
    Text
"Mix_PlayChannelTimed"
    ( CInt -> Channel
forall a b. (Integral a, Num b) => a -> b
fromIntegral
        (CInt -> Channel) -> m CInt -> m Channel
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CInt -> Ptr Chunk -> CInt -> CInt -> m CInt
forall (m :: * -> *).
MonadIO m =>
CInt -> Ptr Chunk -> CInt -> CInt -> m CInt
SDL.Raw.Mixer.playChannelTimed
          (CInt -> CInt
clipChan CInt
c)
          Ptr Chunk
p
          (CInt -> CInt -> CInt
forall a. Ord a => a -> a -> a
max (-CInt
1) (CInt -> CInt) -> CInt -> CInt
forall a b. (a -> b) -> a -> b
$ CInt
t CInt -> CInt -> CInt
forall a. Num a => a -> a -> a
- CInt
1)
          (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
l)
    )

-- | Same as 'play', but fades in the 'Chunk' by making the 'Channel' 'Volume'
-- start at 0 and rise to a full 128 over the course of a given number of
-- 'Milliseconds'.
--
-- The 'Chunk' may end playing before the fade-in is complete, if it doesn't
-- last as long as the given fade-in time.
fadeIn :: MonadIO m => Milliseconds -> Chunk -> m ()
fadeIn :: Int -> Chunk -> m ()
fadeIn Int
ms = m Channel -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m Channel -> m ()) -> (Chunk -> m Channel) -> Chunk -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Channel -> Times -> Int -> Chunk -> m Channel
forall (m :: * -> *).
MonadIO m =>
Channel -> Times -> Int -> Chunk -> m Channel
fadeInOn Channel
AllChannels Times
Once Int
ms

-- | Same as 'fadeIn', but allows you to specify the 'Channel' to play on and
-- how many 'Times' to play it, similar to 'playOn'.
--
-- If 'AllChannels' is used, will play the 'Chunk' on the first available
-- 'Channel'.
--
-- Returns the 'Channel' that was used.
fadeInOn :: MonadIO m => Channel -> Times -> Milliseconds -> Chunk -> m Channel
fadeInOn :: Channel -> Times -> Int -> Chunk -> m Channel
fadeInOn = Int -> Channel -> Times -> Int -> Chunk -> m Channel
forall (m :: * -> *).
MonadIO m =>
Int -> Channel -> Times -> Int -> Chunk -> m Channel
fadeInLimit Int
NoLimit

-- | Same as 'fadeInOn', but imposes an upper 'Limit' to how long the 'Chunk'
-- can play, similar to 'playLimit'.
--
-- This is the most generic fade-in function variant.
fadeInLimit ::
  MonadIO m =>
  Limit ->
  Channel ->
  Times ->
  Milliseconds ->
  Chunk ->
  m Channel
fadeInLimit :: Int -> Channel -> Times -> Int -> Chunk -> m Channel
fadeInLimit Int
l (Channel CInt
c) (Times CInt
t) Int
ms (Chunk Ptr Chunk
p) =
  Text -> Text -> m Channel -> m Channel
forall (m :: * -> *) a.
(MonadIO m, Num a, Ord a) =>
Text -> Text -> m a -> m a
throwIfNeg Text
"SDL.Mixer.fadeInLimit" Text
"Mix_FadeInChannelTimed" (m Channel -> m Channel) -> m Channel -> m Channel
forall a b. (a -> b) -> a -> b
$
    CInt -> Channel
forall a b. (Integral a, Num b) => a -> b
fromIntegral
      (CInt -> Channel) -> m CInt -> m Channel
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CInt -> Ptr Chunk -> CInt -> CInt -> CInt -> m CInt
forall (m :: * -> *).
MonadIO m =>
CInt -> Ptr Chunk -> CInt -> CInt -> CInt -> m CInt
SDL.Raw.Mixer.fadeInChannelTimed
        (CInt -> CInt
clipChan CInt
c)
        Ptr Chunk
p
        (CInt -> CInt -> CInt
forall a. Ord a => a -> a -> a
max (-CInt
1) (CInt -> CInt) -> CInt -> CInt
forall a b. (a -> b) -> a -> b
$ CInt
t CInt -> CInt -> CInt
forall a. Num a => a -> a -> a
- CInt
1)
        (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
ms)
        (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
l)

-- | Gradually fade out a given playing 'Channel' during the next
-- 'Milliseconds', even if it is 'pause'd.
--
-- If 'AllChannels' is used, fades out all the playing 'Channel's instead.
fadeOut :: MonadIO m => Milliseconds -> Channel -> m ()
fadeOut :: Int -> Channel -> m ()
fadeOut Int
ms (Channel CInt
c) =
  m CInt -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m CInt -> m ()) -> m CInt -> m ()
forall a b. (a -> b) -> a -> b
$ CInt -> CInt -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> CInt -> m CInt
SDL.Raw.Mixer.fadeOutChannel (CInt -> CInt
clipChan CInt
c) (CInt -> m CInt) -> CInt -> m CInt
forall a b. (a -> b) -> a -> b
$ Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
ms

-- | Same as 'fadeOut', but fades out an entire 'Group' instead.
--
-- Using 'DefaultGroup' here is the same as calling 'fadeOut' with
-- 'AllChannels'.
fadeOutGroup :: MonadIO m => Milliseconds -> Group -> m ()
fadeOutGroup :: Int -> Group -> m ()
fadeOutGroup Int
ms = \case
  Group
DefaultGroup -> Int -> Channel -> m ()
forall (m :: * -> *). MonadIO m => Int -> Channel -> m ()
fadeOut Int
ms Channel
AllChannels
  Group CInt
g -> m CInt -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m CInt -> m ()) -> m CInt -> m ()
forall a b. (a -> b) -> a -> b
$ CInt -> CInt -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> CInt -> m CInt
SDL.Raw.Mixer.fadeOutGroup CInt
g (CInt -> m CInt) -> CInt -> m CInt
forall a b. (a -> b) -> a -> b
$ Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
ms

-- | Pauses the given 'Channel', if it is actively playing.
--
-- If 'AllChannels' is used, will pause all actively playing 'Channel's
-- instead.
--
-- Note that 'pause'd 'Channel's may still be 'halt'ed.
pause :: MonadIO m => Channel -> m ()
pause :: Channel -> m ()
pause (Channel CInt
c) = CInt -> m ()
forall (m :: * -> *). MonadIO m => CInt -> m ()
SDL.Raw.Mixer.pause (CInt -> m ()) -> CInt -> m ()
forall a b. (a -> b) -> a -> b
$ CInt -> CInt
clipChan CInt
c

-- | Resumes playing a 'Channel', or all 'Channel's if 'AllChannels' is used.
resume :: MonadIO m => Channel -> m ()
resume :: Channel -> m ()
resume (Channel CInt
c) = CInt -> m ()
forall (m :: * -> *). MonadIO m => CInt -> m ()
SDL.Raw.Mixer.resume (CInt -> m ()) -> CInt -> m ()
forall a b. (a -> b) -> a -> b
$ CInt -> CInt
clipChan CInt
c

-- | Halts playback on a 'Channel', or all 'Channel's if 'AllChannels' is used.
halt :: MonadIO m => Channel -> m ()
halt :: Channel -> m ()
halt (Channel CInt
c) = m CInt -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m CInt -> m ()) -> m CInt -> m ()
forall a b. (a -> b) -> a -> b
$ CInt -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> m CInt
SDL.Raw.Mixer.haltChannel (CInt -> m CInt) -> CInt -> m CInt
forall a b. (a -> b) -> a -> b
$ CInt -> CInt
clipChan CInt
c

-- | Same as 'halt', but only does so after a certain number of 'Milliseconds'.
--
-- If 'AllChannels' is used, it will halt all the 'Channel's after the given
-- time instead.
haltAfter :: MonadIO m => Milliseconds -> Channel -> m ()
haltAfter :: Int -> Channel -> m ()
haltAfter Int
ms (Channel CInt
c) =
  m CInt -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m CInt -> m ()) -> (CInt -> m CInt) -> CInt -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CInt -> CInt -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> CInt -> m CInt
SDL.Raw.Mixer.expireChannel (CInt -> CInt
clipChan CInt
c) (CInt -> m ()) -> CInt -> m ()
forall a b. (a -> b) -> a -> b
$ Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
ms

-- | Same as 'halt', but halts an entire 'Group' instead.
--
-- Note that using 'DefaultGroup' here is the same as calling 'halt'
-- 'AllChannels'.
haltGroup :: MonadIO m => Group -> m ()
haltGroup :: Group -> m ()
haltGroup = \case
  Group
DefaultGroup -> Channel -> m ()
forall (m :: * -> *). MonadIO m => Channel -> m ()
halt Channel
AllChannels
  Group CInt
g -> m CInt -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m CInt -> m ()) -> m CInt -> m ()
forall a b. (a -> b) -> a -> b
$ CInt -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> m CInt
SDL.Raw.Mixer.haltGroup (CInt -> m CInt) -> CInt -> m CInt
forall a b. (a -> b) -> a -> b
$ CInt -> CInt -> CInt
forall a. Ord a => a -> a -> a
max CInt
0 CInt
g

-- Quackery of the highest order! We keep track of a pointer we gave SDL_mixer,
-- so we can free it at a later time. May the gods have mercy...
{-# NOINLINE channelFinishedFunPtr #-}
channelFinishedFunPtr :: IORef (FunPtr (SDL.Raw.Mixer.Channel -> IO ()))
channelFinishedFunPtr :: IORef (FunPtr (CInt -> IO ()))
channelFinishedFunPtr = IO (IORef (FunPtr (CInt -> IO ())))
-> IORef (FunPtr (CInt -> IO ()))
forall a. IO a -> a
unsafePerformIO (IO (IORef (FunPtr (CInt -> IO ())))
 -> IORef (FunPtr (CInt -> IO ())))
-> IO (IORef (FunPtr (CInt -> IO ())))
-> IORef (FunPtr (CInt -> IO ()))
forall a b. (a -> b) -> a -> b
$ FunPtr (CInt -> IO ()) -> IO (IORef (FunPtr (CInt -> IO ())))
forall a. a -> IO (IORef a)
newIORef FunPtr (CInt -> IO ())
forall a. FunPtr a
nullFunPtr

-- | Sets a callback that gets invoked each time a 'Channel' finishes playing.
--
-- A 'Channel' finishes playing both when playback ends normally and when it is
-- 'halt'ed (also possibly via 'setChannels').
--
-- __Note: don't call other 'SDL.Mixer' functions within this callback.__
whenChannelFinished :: MonadIO m => (Channel -> IO ()) -> m ()
whenChannelFinished :: (Channel -> IO ()) -> m ()
whenChannelFinished Channel -> IO ()
callback = IO () -> m ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> IO () -> m ()
forall a b. (a -> b) -> a -> b
$ do
  -- Sets the callback.
  let callback' :: CInt -> IO ()
callback' = Channel -> IO ()
callback (Channel -> IO ()) -> (CInt -> Channel) -> CInt -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CInt -> Channel
Channel
  FunPtr (CInt -> IO ())
callbackRaw <- (CInt -> IO ()) -> IO (FunPtr (CInt -> IO ()))
SDL.Raw.Mixer.wrapChannelCallback CInt -> IO ()
callback'
  FunPtr (CInt -> IO ()) -> IO ()
forall (m :: * -> *). MonadIO m => FunPtr (CInt -> IO ()) -> m ()
SDL.Raw.Mixer.channelFinished FunPtr (CInt -> IO ())
callbackRaw

  -- Free the function we set last time, if any.
  FunPtr (CInt -> IO ())
lastFunPtr <- IORef (FunPtr (CInt -> IO ())) -> IO (FunPtr (CInt -> IO ()))
forall a. IORef a -> IO a
readIORef IORef (FunPtr (CInt -> IO ()))
channelFinishedFunPtr
  Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (FunPtr (CInt -> IO ())
lastFunPtr FunPtr (CInt -> IO ()) -> FunPtr (CInt -> IO ()) -> Bool
forall a. Eq a => a -> a -> Bool
/= FunPtr (CInt -> IO ())
forall a. FunPtr a
nullFunPtr) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ FunPtr (CInt -> IO ()) -> IO ()
forall a. FunPtr a -> IO ()
freeHaskellFunPtr FunPtr (CInt -> IO ())
lastFunPtr

  -- Then remember the new one. And weep in shame.
  IORef (FunPtr (CInt -> IO ())) -> FunPtr (CInt -> IO ()) -> IO ()
forall a. IORef a -> a -> IO ()
writeIORef IORef (FunPtr (CInt -> IO ()))
channelFinishedFunPtr FunPtr (CInt -> IO ())
callbackRaw

-- | Returns whether the given 'Channel' is playing or not.
--
-- If 'AllChannels' is used, this returns whether /any/ of the channels is
-- currently playing.
playing :: MonadIO m => Channel -> m Bool
playing :: Channel -> m Bool
playing (Channel CInt
c) = (CInt -> CInt -> Bool
forall a. Ord a => a -> a -> Bool
> CInt
0) (CInt -> Bool) -> m CInt -> m Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CInt -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> m CInt
SDL.Raw.Mixer.playing (CInt -> CInt
clipChan CInt
c)

-- | Returns how many 'Channel's are currently playing.
playingCount :: MonadIO m => m Int
playingCount :: m Int
playingCount = CInt -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (CInt -> Int) -> m CInt -> m Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CInt -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> m CInt
SDL.Raw.Mixer.playing (-CInt
1)

-- | Returns whether the given 'Channel' is paused or not.
--
-- If 'AllChannels' is used, this returns whether /any/ of the channels is
-- currently paused.
paused :: MonadIO m => Channel -> m Bool
paused :: Channel -> m Bool
paused (Channel CInt
c) = (CInt -> CInt -> Bool
forall a. Ord a => a -> a -> Bool
> CInt
0) (CInt -> Bool) -> m CInt -> m Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CInt -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> m CInt
SDL.Raw.Mixer.paused (CInt -> CInt
clipChan CInt
c)

-- | Returns how many 'Channel's are currently paused.
pausedCount :: MonadIO m => m Int
pausedCount :: m Int
pausedCount = CInt -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (CInt -> Int) -> m CInt -> m Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CInt -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> m CInt
SDL.Raw.Mixer.paused (-CInt
1)

-- | Describes whether a 'Channel' is fading in, out, or not at all.
data Fading = NoFading | FadingIn | FadingOut
  deriving stock (Fading -> Fading -> Bool
(Fading -> Fading -> Bool)
-> (Fading -> Fading -> Bool) -> Eq Fading
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Fading -> Fading -> Bool
$c/= :: Fading -> Fading -> Bool
== :: Fading -> Fading -> Bool
$c== :: Fading -> Fading -> Bool
Eq, Eq Fading
Eq Fading
-> (Fading -> Fading -> Ordering)
-> (Fading -> Fading -> Bool)
-> (Fading -> Fading -> Bool)
-> (Fading -> Fading -> Bool)
-> (Fading -> Fading -> Bool)
-> (Fading -> Fading -> Fading)
-> (Fading -> Fading -> Fading)
-> Ord Fading
Fading -> Fading -> Bool
Fading -> Fading -> Ordering
Fading -> Fading -> Fading
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Fading -> Fading -> Fading
$cmin :: Fading -> Fading -> Fading
max :: Fading -> Fading -> Fading
$cmax :: Fading -> Fading -> Fading
>= :: Fading -> Fading -> Bool
$c>= :: Fading -> Fading -> Bool
> :: Fading -> Fading -> Bool
$c> :: Fading -> Fading -> Bool
<= :: Fading -> Fading -> Bool
$c<= :: Fading -> Fading -> Bool
< :: Fading -> Fading -> Bool
$c< :: Fading -> Fading -> Bool
compare :: Fading -> Fading -> Ordering
$ccompare :: Fading -> Fading -> Ordering
$cp1Ord :: Eq Fading
Ord, Int -> Fading -> ShowS
[Fading] -> ShowS
Fading -> String
(Int -> Fading -> ShowS)
-> (Fading -> String) -> ([Fading] -> ShowS) -> Show Fading
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Fading] -> ShowS
$cshowList :: [Fading] -> ShowS
show :: Fading -> String
$cshow :: Fading -> String
showsPrec :: Int -> Fading -> ShowS
$cshowsPrec :: Int -> Fading -> ShowS
Show, ReadPrec [Fading]
ReadPrec Fading
Int -> ReadS Fading
ReadS [Fading]
(Int -> ReadS Fading)
-> ReadS [Fading]
-> ReadPrec Fading
-> ReadPrec [Fading]
-> Read Fading
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [Fading]
$creadListPrec :: ReadPrec [Fading]
readPrec :: ReadPrec Fading
$creadPrec :: ReadPrec Fading
readList :: ReadS [Fading]
$creadList :: ReadS [Fading]
readsPrec :: Int -> ReadS Fading
$creadsPrec :: Int -> ReadS Fading
Read)

wordToFading :: SDL.Raw.Mixer.Fading -> Fading
wordToFading :: Fading -> Fading
wordToFading = \case
  Fading
SDL.Raw.Mixer.NO_FADING -> Fading
NoFading
  Fading
SDL.Raw.Mixer.FADING_IN -> Fading
FadingIn
  Fading
SDL.Raw.Mixer.FADING_OUT -> Fading
FadingOut
  Fading
_ -> String -> Fading
forall a. HasCallStack => String -> a
error String
"SDL.Mixer.wordToFading: unknown Fading value."

-- | Returns a `Channel`'s 'Fading' status.
--
-- Note that using 'AllChannels' here is not valid, and will simply return the
-- 'Fading' status of the first 'Channel' instead.
fading :: MonadIO m => Channel -> m Fading
fading :: Channel -> m Fading
fading (Channel CInt
c) =
  Fading -> Fading
wordToFading (Fading -> Fading) -> m Fading -> m Fading
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CInt -> m Fading
forall (m :: * -> *). MonadIO m => CInt -> m Fading
SDL.Raw.Mixer.fadingChannel (CInt -> CInt
clipChan CInt
c)

-- | A group of 'Channel's.
--
-- Grouping 'Channel's together allows you to perform some operations on all of
-- them at once.
--
-- By default, all 'Channel's are members of the 'DefaultGroup'.
newtype Group = Group CInt
  deriving stock (Group -> Group -> Bool
(Group -> Group -> Bool) -> (Group -> Group -> Bool) -> Eq Group
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Group -> Group -> Bool
$c/= :: Group -> Group -> Bool
== :: Group -> Group -> Bool
$c== :: Group -> Group -> Bool
Eq, Eq Group
Eq Group
-> (Group -> Group -> Ordering)
-> (Group -> Group -> Bool)
-> (Group -> Group -> Bool)
-> (Group -> Group -> Bool)
-> (Group -> Group -> Bool)
-> (Group -> Group -> Group)
-> (Group -> Group -> Group)
-> Ord Group
Group -> Group -> Bool
Group -> Group -> Ordering
Group -> Group -> Group
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Group -> Group -> Group
$cmin :: Group -> Group -> Group
max :: Group -> Group -> Group
$cmax :: Group -> Group -> Group
>= :: Group -> Group -> Bool
$c>= :: Group -> Group -> Bool
> :: Group -> Group -> Bool
$c> :: Group -> Group -> Bool
<= :: Group -> Group -> Bool
$c<= :: Group -> Group -> Bool
< :: Group -> Group -> Bool
$c< :: Group -> Group -> Bool
compare :: Group -> Group -> Ordering
$ccompare :: Group -> Group -> Ordering
$cp1Ord :: Eq Group
Ord)
  deriving newtype (Int -> Group
Group -> Int
Group -> [Group]
Group -> Group
Group -> Group -> [Group]
Group -> Group -> Group -> [Group]
(Group -> Group)
-> (Group -> Group)
-> (Int -> Group)
-> (Group -> Int)
-> (Group -> [Group])
-> (Group -> Group -> [Group])
-> (Group -> Group -> [Group])
-> (Group -> Group -> Group -> [Group])
-> Enum Group
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
enumFromThenTo :: Group -> Group -> Group -> [Group]
$cenumFromThenTo :: Group -> Group -> Group -> [Group]
enumFromTo :: Group -> Group -> [Group]
$cenumFromTo :: Group -> Group -> [Group]
enumFromThen :: Group -> Group -> [Group]
$cenumFromThen :: Group -> Group -> [Group]
enumFrom :: Group -> [Group]
$cenumFrom :: Group -> [Group]
fromEnum :: Group -> Int
$cfromEnum :: Group -> Int
toEnum :: Int -> Group
$ctoEnum :: Int -> Group
pred :: Group -> Group
$cpred :: Group -> Group
succ :: Group -> Group
$csucc :: Group -> Group
Enum, Enum Group
Real Group
Real Group
-> Enum Group
-> (Group -> Group -> Group)
-> (Group -> Group -> Group)
-> (Group -> Group -> Group)
-> (Group -> Group -> Group)
-> (Group -> Group -> (Group, Group))
-> (Group -> Group -> (Group, Group))
-> (Group -> Integer)
-> Integral Group
Group -> Integer
Group -> Group -> (Group, Group)
Group -> Group -> Group
forall a.
Real a
-> Enum a
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> (a, a))
-> (a -> a -> (a, a))
-> (a -> Integer)
-> Integral a
toInteger :: Group -> Integer
$ctoInteger :: Group -> Integer
divMod :: Group -> Group -> (Group, Group)
$cdivMod :: Group -> Group -> (Group, Group)
quotRem :: Group -> Group -> (Group, Group)
$cquotRem :: Group -> Group -> (Group, Group)
mod :: Group -> Group -> Group
$cmod :: Group -> Group -> Group
div :: Group -> Group -> Group
$cdiv :: Group -> Group -> Group
rem :: Group -> Group -> Group
$crem :: Group -> Group -> Group
quot :: Group -> Group -> Group
$cquot :: Group -> Group -> Group
$cp2Integral :: Enum Group
$cp1Integral :: Real Group
Integral, Num Group
Ord Group
Num Group -> Ord Group -> (Group -> Rational) -> Real Group
Group -> Rational
forall a. Num a -> Ord a -> (a -> Rational) -> Real a
toRational :: Group -> Rational
$ctoRational :: Group -> Rational
$cp2Real :: Ord Group
$cp1Real :: Num Group
Real, Integer -> Group
Group -> Group
Group -> Group -> Group
(Group -> Group -> Group)
-> (Group -> Group -> Group)
-> (Group -> Group -> Group)
-> (Group -> Group)
-> (Group -> Group)
-> (Group -> Group)
-> (Integer -> Group)
-> Num Group
forall a.
(a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a)
-> (a -> a)
-> (a -> a)
-> (Integer -> a)
-> Num a
fromInteger :: Integer -> Group
$cfromInteger :: Integer -> Group
signum :: Group -> Group
$csignum :: Group -> Group
abs :: Group -> Group
$cabs :: Group -> Group
negate :: Group -> Group
$cnegate :: Group -> Group
* :: Group -> Group -> Group
$c* :: Group -> Group -> Group
- :: Group -> Group -> Group
$c- :: Group -> Group -> Group
+ :: Group -> Group -> Group
$c+ :: Group -> Group -> Group
Num)

-- | The default 'Group' all 'Channel's are in the moment they are created.
pattern DefaultGroup :: Group
pattern $bDefaultGroup :: Group
$mDefaultGroup :: forall r. Group -> (Void# -> r) -> (Void# -> r) -> r
DefaultGroup = -1

-- | Assigns a given 'Channel' to a certain 'Group'.
--
-- If 'DefaultGroup' is used, assigns the 'Channel' the the default starting
-- 'Group' (essentially /ungrouping/ them).
--
-- If 'AllChannels' is used, assigns all 'Channel's to the given 'Group'.
--
-- Returns whether the 'Channel' was successfully grouped or not. Failure is
-- poosible if the 'Channel' does not exist, for instance.
group :: MonadIO m => Group -> Channel -> m Bool
group :: Group -> Channel -> m Bool
group wrapped :: Group
wrapped@(Group CInt
g) Channel
channel =
  case Channel
channel of
    Channel
AllChannels -> do
      Int
total <- m Int
forall (m :: * -> *). MonadIO m => m Int
getChannels
      if Int
total Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0
        then (Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0) (Int -> Bool) -> m Int -> m Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Group -> Channel -> Channel -> m Int
forall (m :: * -> *).
MonadIO m =>
Group -> Channel -> Channel -> m Int
groupSpan Group
wrapped Channel
0 (CInt -> Channel
Channel (CInt -> Channel) -> CInt -> Channel
forall a b. (a -> b) -> a -> b
$ Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> CInt) -> Int -> CInt
forall a b. (a -> b) -> a -> b
$ Int
total Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)
        else Bool -> m Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True -- No channels available -- still a success probably.
    Channel CInt
c ->
      if CInt
c CInt -> CInt -> Bool
forall a. Ord a => a -> a -> Bool
>= CInt
0
        then (CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
== CInt
1) (CInt -> Bool) -> m CInt -> m Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CInt -> CInt -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> CInt -> m CInt
SDL.Raw.Mixer.groupChannel CInt
c CInt
g
        else Bool -> m Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False -- Can't group the post-processing channel or below.

-- | Same as 'groupChannel', but groups all 'Channel's between the first and
-- last given, inclusive.
--
-- If 'DefaultGroup' is used, assigns the entire 'Channel' span to the default
-- starting 'Group' (essentially /ungrouping/ them).
--
-- Using 'AllChannels' is invalid.
--
-- Returns the number of 'Channel's successfully grouped. This number may be
-- less than the number of 'Channel's given, for instance if some of them do
-- not exist.
groupSpan :: MonadIO m => Group -> Channel -> Channel -> m Int
groupSpan :: Group -> Channel -> Channel -> m Int
groupSpan wrap :: Group
wrap@(Group CInt
g) from :: Channel
from@(Channel CInt
c1) to :: Channel
to@(Channel CInt
c2)
  | CInt
c1 CInt -> CInt -> Bool
forall a. Ord a => a -> a -> Bool
< CInt
0 Bool -> Bool -> Bool
|| CInt
c2 CInt -> CInt -> Bool
forall a. Ord a => a -> a -> Bool
< CInt
0 = Int -> m Int
forall (m :: * -> *) a. Monad m => a -> m a
return Int
0
  | CInt
c1 CInt -> CInt -> Bool
forall a. Ord a => a -> a -> Bool
> CInt
c2 = Group -> Channel -> Channel -> m Int
forall (m :: * -> *).
MonadIO m =>
Group -> Channel -> Channel -> m Int
groupSpan Group
wrap Channel
to Channel
from
  | Bool
otherwise = CInt -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (CInt -> Int) -> m CInt -> m Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CInt -> CInt -> CInt -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> CInt -> CInt -> m CInt
SDL.Raw.Mixer.groupChannels CInt
c1 CInt
c2 CInt
g

-- | Returns the number of 'Channels' within a 'Group'.
--
-- If 'DefaultGroup' is used, will return the number of all 'Channel's, since
-- all of them are within the default 'Group'.
groupCount :: MonadIO m => Group -> m Int
groupCount :: Group -> m Int
groupCount (Group CInt
g) = CInt -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (CInt -> Int) -> m CInt -> m Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CInt -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> m CInt
SDL.Raw.Mixer.groupCount CInt
g

-- | Gets the first inactive (not playing) 'Channel' within a given 'Group',
-- if any.
--
-- Using 'DefaultGroup' will give you the first inactive 'Channel' out of all
-- that exist.
getAvailable :: MonadIO m => Group -> m (Maybe Channel)
getAvailable :: Group -> m (Maybe Channel)
getAvailable (Group CInt
g) = do
  CInt
found <- CInt -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> m CInt
SDL.Raw.Mixer.groupAvailable CInt
g
  Maybe Channel -> m (Maybe Channel)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe Channel -> m (Maybe Channel))
-> Maybe Channel -> m (Maybe Channel)
forall a b. (a -> b) -> a -> b
$ if CInt
found CInt -> CInt -> Bool
forall a. Ord a => a -> a -> Bool
>= CInt
0 then Channel -> Maybe Channel
forall a. a -> Maybe a
Just (Channel -> Maybe Channel) -> Channel -> Maybe Channel
forall a b. (a -> b) -> a -> b
$ CInt -> Channel
forall a b. (Integral a, Num b) => a -> b
fromIntegral CInt
found else Maybe Channel
forall a. Maybe a
Nothing

-- | Gets the oldest actively playing 'Channel' within a given 'Group'.
--
-- Returns 'Nothing' when the 'Group' is empty or no 'Channel's within it are
-- playing.
getOldest :: MonadIO m => Group -> m (Maybe Channel)
getOldest :: Group -> m (Maybe Channel)
getOldest (Group CInt
g) = do
  CInt
found <- CInt -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> m CInt
SDL.Raw.Mixer.groupOldest CInt
g
  Maybe Channel -> m (Maybe Channel)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe Channel -> m (Maybe Channel))
-> Maybe Channel -> m (Maybe Channel)
forall a b. (a -> b) -> a -> b
$ if CInt
found CInt -> CInt -> Bool
forall a. Ord a => a -> a -> Bool
>= CInt
0 then Channel -> Maybe Channel
forall a. a -> Maybe a
Just (Channel -> Maybe Channel) -> Channel -> Maybe Channel
forall a b. (a -> b) -> a -> b
$ CInt -> Channel
forall a b. (Integral a, Num b) => a -> b
fromIntegral CInt
found else Maybe Channel
forall a. Maybe a
Nothing

-- | Gets the newest actively playing 'Channel' within a given 'Group'.
--
-- Returns 'Nothing' when the 'Group' is empty or no 'Channel's within it are
-- playing.
getNewest :: MonadIO m => Group -> m (Maybe Channel)
getNewest :: Group -> m (Maybe Channel)
getNewest (Group CInt
g) = do
  CInt
found <- CInt -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> m CInt
SDL.Raw.Mixer.groupNewer CInt
g
  Maybe Channel -> m (Maybe Channel)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe Channel -> m (Maybe Channel))
-> Maybe Channel -> m (Maybe Channel)
forall a b. (a -> b) -> a -> b
$ if CInt
found CInt -> CInt -> Bool
forall a. Ord a => a -> a -> Bool
>= CInt
0 then Channel -> Maybe Channel
forall a. a -> Maybe a
Just (Channel -> Maybe Channel) -> Channel -> Maybe Channel
forall a b. (a -> b) -> a -> b
$ CInt -> Channel
forall a b. (Integral a, Num b) => a -> b
fromIntegral CInt
found else Maybe Channel
forall a. Maybe a
Nothing

-- | Returns the names of all music decoders currently available.
--
-- These depend on the availability of shared libraries for each of the
-- formats. The list may contain any of the following, and possibly others:
-- @WAVE@, @MODPLUG@, @MIKMOD@, @TIMIDITY@, @FLUIDSYNTH@, @NATIVEMIDI@, @OGG@,
-- @FLAC@, @MP3@.
musicDecoders :: MonadIO m => m [String]
musicDecoders :: m [String]
musicDecoders =
  IO [String] -> m [String]
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO [String] -> m [String]) -> IO [String] -> m [String]
forall a b. (a -> b) -> a -> b
$ do
    CInt
num <- IO CInt
forall (m :: * -> *). MonadIO m => m CInt
SDL.Raw.Mixer.getNumMusicDecoders
    [CInt] -> (CInt -> IO String) -> IO [String]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM [CInt
0 .. CInt
num CInt -> CInt -> CInt
forall a. Num a => a -> a -> a
- CInt
1] ((CInt -> IO String) -> IO [String])
-> (CInt -> IO String) -> IO [String]
forall a b. (a -> b) -> a -> b
$ CInt -> IO CString
forall (m :: * -> *). MonadIO m => CInt -> m CString
SDL.Raw.Mixer.getMusicDecoder (CInt -> IO CString) -> (CString -> IO String) -> CInt -> IO String
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> CString -> IO String
peekCString

-- | A loaded music file.
--
-- 'Music' is played on a separate channel different from the normal mixing
-- 'Channel's.
--
-- To manipulate 'Music' outside of post-processing callbacks, use the music
-- variant functions listed below.
newtype Music = Music (Ptr SDL.Raw.Mixer.Music)
  deriving stock (Music -> Music -> Bool
(Music -> Music -> Bool) -> (Music -> Music -> Bool) -> Eq Music
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Music -> Music -> Bool
$c/= :: Music -> Music -> Bool
== :: Music -> Music -> Bool
$c== :: Music -> Music -> Bool
Eq, Int -> Music -> ShowS
[Music] -> ShowS
Music -> String
(Int -> Music -> ShowS)
-> (Music -> String) -> ([Music] -> ShowS) -> Show Music
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Music] -> ShowS
$cshowList :: [Music] -> ShowS
show :: Music -> String
$cshow :: Music -> String
showsPrec :: Int -> Music -> ShowS
$cshowsPrec :: Int -> Music -> ShowS
Show)

instance Loadable Music where
  decode :: ByteString -> m Music
decode ByteString
bytes = IO Music -> m Music
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Music -> m Music) -> IO Music -> m Music
forall a b. (a -> b) -> a -> b
$ do
    ByteString -> (CStringLen -> IO Music) -> IO Music
forall a. ByteString -> (CStringLen -> IO a) -> IO a
unsafeUseAsCStringLen ByteString
bytes ((CStringLen -> IO Music) -> IO Music)
-> (CStringLen -> IO Music) -> IO Music
forall a b. (a -> b) -> a -> b
$ \(CString
cstr, Int
len) -> do
      Ptr RWops
rw <- Ptr () -> CInt -> IO (Ptr RWops)
forall (m :: * -> *). MonadIO m => Ptr () -> CInt -> m (Ptr RWops)
rwFromConstMem (CString -> Ptr ()
forall a b. Ptr a -> Ptr b
castPtr CString
cstr) (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
len)
      (Ptr Music -> Music) -> IO (Ptr Music) -> IO Music
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Ptr Music -> Music
Music
        (IO (Ptr Music) -> IO Music)
-> (IO (Ptr Music) -> IO (Ptr Music)) -> IO (Ptr Music) -> IO Music
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text -> IO (Ptr Music) -> IO (Ptr Music)
forall (m :: * -> *) a.
MonadIO m =>
Text -> Text -> m (Ptr a) -> m (Ptr a)
throwIfNull Text
"SDL.Mixer.decode<Music>" Text
"Mix_LoadMUS_RW"
        (IO (Ptr Music) -> IO Music) -> IO (Ptr Music) -> IO Music
forall a b. (a -> b) -> a -> b
$ Ptr RWops -> CInt -> IO (Ptr Music)
forall (m :: * -> *).
MonadIO m =>
Ptr RWops -> CInt -> m (Ptr Music)
SDL.Raw.Mixer.loadMUS_RW Ptr RWops
rw CInt
0

  free :: Music -> m ()
free (Music Ptr Music
p) = IO () -> m ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> IO () -> m ()
forall a b. (a -> b) -> a -> b
$ Ptr Music -> IO ()
forall (m :: * -> *). MonadIO m => Ptr Music -> m ()
SDL.Raw.Mixer.freeMusic Ptr Music
p

-- | Plays a given 'Music' a certain number of 'Times'.
--
-- The previously playing 'Music' will be halted, unless it is fading out in
-- which case a blocking wait occurs until it fades out completely.
playMusic :: MonadIO m => Times -> Music -> m ()
playMusic :: Times -> Music -> m ()
playMusic Times
times (Music Ptr Music
p) =
  Text -> Text -> m CInt -> m ()
forall (m :: * -> *) a.
(MonadIO m, Num a, Ord a) =>
Text -> Text -> m a -> m ()
throwIfNeg_ Text
"SDL.Mixer.playMusic" Text
"Mix_PlayMusic" (m CInt -> m ()) -> m CInt -> m ()
forall a b. (a -> b) -> a -> b
$
    Ptr Music -> CInt -> m CInt
forall (m :: * -> *). MonadIO m => Ptr Music -> CInt -> m CInt
SDL.Raw.Mixer.playMusic Ptr Music
p (CInt -> m CInt) -> CInt -> m CInt
forall a b. (a -> b) -> a -> b
$
      case Times
times of
        Times
Forever -> (-CInt
1)
        Times CInt
t -> CInt -> CInt -> CInt
forall a. Ord a => a -> a -> a
max CInt
1 CInt
t -- Interpretation differs from normal play? :/

-- | Pauses 'Music' playback, if it is actively playing.
--
-- You may still 'haltMusic' paused 'Music'.
pauseMusic :: MonadIO m => m ()
pauseMusic :: m ()
pauseMusic = m ()
forall (m :: * -> *). MonadIO m => m ()
SDL.Raw.Mixer.pauseMusic

-- | Halts 'Music' playback.
haltMusic :: MonadIO m => m ()
haltMusic :: m ()
haltMusic = m CInt -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void m CInt
forall (m :: * -> *). MonadIO m => m CInt
SDL.Raw.Mixer.haltMusic

-- | Resumes 'Music' playback.
--
-- This works on both paused and halted 'Music'.
--
-- If 'Music' is currently actively playing, this has no effect.
resumeMusic :: MonadIO m => m ()
resumeMusic :: m ()
resumeMusic = m ()
forall (m :: * -> *). MonadIO m => m ()
SDL.Raw.Mixer.resumeMusic

-- | Returns whether a 'Music' is currently playing or not.
--
-- Note that this returns 'True' even if the 'Music' is currently paused.
playingMusic :: MonadIO m => m Bool
playingMusic :: m Bool
playingMusic = (CInt -> CInt -> Bool
forall a. Ord a => a -> a -> Bool
> CInt
0) (CInt -> Bool) -> m CInt -> m Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> m CInt
forall (m :: * -> *). MonadIO m => m CInt
SDL.Raw.Mixer.playingMusic

-- | Returns whether a 'Music' is currently paused or not.
--
-- Note that this returns 'False' if the 'Music' is currently halted.
pausedMusic :: MonadIO m => m Bool
pausedMusic :: m Bool
pausedMusic = (CInt -> CInt -> Bool
forall a. Ord a => a -> a -> Bool
> CInt
0) (CInt -> Bool) -> m CInt -> m Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> m CInt
forall (m :: * -> *). MonadIO m => m CInt
SDL.Raw.Mixer.pausedMusic

-- | Rewinds the 'Music' to the beginning.
--
-- When playing new 'Music', it starts at the beginning by default.
--
-- This function only works with @MOD@, @OGG@, @MP3@ and @NATIVEMIDI@ streams.
rewindMusic :: MonadIO m => m ()
rewindMusic :: m ()
rewindMusic = m ()
forall (m :: * -> *). MonadIO m => m ()
SDL.Raw.Mixer.rewindMusic

-- | Plays a given 'Music' a number of 'Times', but fading it in during a
-- certain number of 'Milliseconds'.
--
-- The fading only occurs during the first time the 'Music' is played.
fadeInMusic :: MonadIO m => Milliseconds -> Times -> Music -> m ()
fadeInMusic :: Int -> Times -> Music -> m ()
fadeInMusic Int
ms Times
times (Music Ptr Music
p) =
  Text -> Text -> m CInt -> m ()
forall (m :: * -> *) a.
(MonadIO m, Num a, Ord a) =>
Text -> Text -> m a -> m ()
throwIfNeg_ Text
"SDL.Mixer.fadeInMusic" Text
"Mix_FadeInMusic" (m CInt -> m ()) -> m CInt -> m ()
forall a b. (a -> b) -> a -> b
$
    Ptr Music -> CInt -> CInt -> m CInt
forall (m :: * -> *).
MonadIO m =>
Ptr Music -> CInt -> CInt -> m CInt
SDL.Raw.Mixer.fadeInMusic Ptr Music
p CInt
t' (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
ms)
  where
    t' :: CInt
t' = case Times
times of
      Times
Forever -> (-CInt
1)
      Times CInt
t -> CInt -> CInt -> CInt
forall a. Ord a => a -> a -> a
max CInt
1 CInt
t

-- | Gradually fade out the 'Music' over a given number of 'Milliseconds'.
--
-- The 'Music' is set to fade out only when it is playing and not fading
-- already.
--
-- Returns whether the 'Music' was successfully set to fade out.
fadeOutMusic :: MonadIO m => Milliseconds -> m Bool
fadeOutMusic :: Int -> m Bool
fadeOutMusic = (CInt -> Bool) -> m CInt -> m Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
== CInt
1) (m CInt -> m Bool) -> (Int -> m CInt) -> Int -> m Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CInt -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> m CInt
SDL.Raw.Mixer.fadeOutMusic (CInt -> m CInt) -> (Int -> CInt) -> Int -> m CInt
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral

-- | A position in milliseconds within a piece of 'Music'.
type Position = Milliseconds

-- | Set the 'Position' for currently playing 'Music'.
--
-- Note: this only works for @OGG@ and @MP3@ 'Music'.
setMusicPosition :: MonadIO m => Position -> m ()
setMusicPosition :: Int -> m ()
setMusicPosition Int
at = do
  m ()
forall (m :: * -> *). MonadIO m => m ()
rewindMusic -- Due to weird behaviour for MP3s...
  Text -> Text -> m CInt -> m ()
forall (m :: * -> *) a.
(MonadIO m, Num a, Ord a) =>
Text -> Text -> m a -> m ()
throwIfNeg_ Text
"SDL.Mixer.setMusicPosition" Text
"Mix_SetMusicPosition" (m CInt -> m ()) -> m CInt -> m ()
forall a b. (a -> b) -> a -> b
$
    CDouble -> m CInt
forall (m :: * -> *). MonadIO m => CDouble -> m CInt
SDL.Raw.Mixer.setMusicPosition (CDouble -> m CInt) -> CDouble -> m CInt
forall a b. (a -> b) -> a -> b
$ Int -> CDouble
forall a b. (Real a, Fractional b) => a -> b
realToFrac Int
at CDouble -> CDouble -> CDouble
forall a. Fractional a => a -> a -> a
/ CDouble
1000.0

-- | Similar to 'setMusicPosition', but works only with @MOD@ 'Music'.
--
-- Pass in the pattern number.
setMusicPositionMOD :: MonadIO m => Int -> m ()
setMusicPositionMOD :: Int -> m ()
setMusicPositionMOD Int
n = do
  Text -> Text -> m CInt -> m ()
forall (m :: * -> *) a.
(MonadIO m, Num a, Ord a) =>
Text -> Text -> m a -> m ()
throwIfNeg_ Text
"SDL.Mixer.setMusicPositionMOD" Text
"Mix_SetMusicPosition" (m CInt -> m ()) -> m CInt -> m ()
forall a b. (a -> b) -> a -> b
$
    CDouble -> m CInt
forall (m :: * -> *). MonadIO m => CDouble -> m CInt
SDL.Raw.Mixer.setMusicPosition (CDouble -> m CInt) -> CDouble -> m CInt
forall a b. (a -> b) -> a -> b
$ Int -> CDouble
forall a b. (Real a, Fractional b) => a -> b
realToFrac Int
n

-- | Same as 'fadeInMusic', but with a custom starting `Music`'s 'Position'.
--
-- Note that this only works on 'Music' that 'setMusicPosition' works on.
fadeInMusicAt :: MonadIO m => Position -> Milliseconds -> Times -> Music -> m ()
fadeInMusicAt :: Int -> Int -> Times -> Music -> m ()
fadeInMusicAt Int
at Int
ms Times
times (Music Ptr Music
p) =
  Text -> Text -> m CInt -> m ()
forall (m :: * -> *) a.
(MonadIO m, Num a, Ord a) =>
Text -> Text -> m a -> m ()
throwIfNeg_ Text
"SDL.Mixer.fadeInMusicAt" Text
"Mix_FadeInMusicPos" (m CInt -> m ()) -> m CInt -> m ()
forall a b. (a -> b) -> a -> b
$
    Ptr Music -> CInt -> CInt -> CDouble -> m CInt
forall (m :: * -> *).
MonadIO m =>
Ptr Music -> CInt -> CInt -> CDouble -> m CInt
SDL.Raw.Mixer.fadeInMusicPos
      Ptr Music
p
      CInt
t'
      (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
ms)
      (Int -> CDouble
forall a b. (Real a, Fractional b) => a -> b
realToFrac Int
at CDouble -> CDouble -> CDouble
forall a. Fractional a => a -> a -> a
/ CDouble
1000.0)
  where
    t' :: CInt
t' = case Times
times of
      Times
Forever -> (-CInt
1)
      Times CInt
t -> CInt -> CInt -> CInt
forall a. Ord a => a -> a -> a
max CInt
1 CInt
t

-- | Same as 'fadeInMusicAt', but works with @MOD@ 'Music'.
--
-- Instead of milliseconds, specify the position with a pattern number.
fadeInMusicAtMOD :: MonadIO m => Int -> Milliseconds -> Times -> Music -> m ()
fadeInMusicAtMOD :: Int -> Int -> Times -> Music -> m ()
fadeInMusicAtMOD Int
at Int
ms Times
times (Music Ptr Music
p) =
  Text -> Text -> m CInt -> m ()
forall (m :: * -> *) a.
(MonadIO m, Num a, Ord a) =>
Text -> Text -> m a -> m ()
throwIfNeg_ Text
"SDL.Mixer.fadeInMusicAtMOD" Text
"Mix_FadeInMusicPos" (m CInt -> m ()) -> m CInt -> m ()
forall a b. (a -> b) -> a -> b
$
    Ptr Music -> CInt -> CInt -> CDouble -> m CInt
forall (m :: * -> *).
MonadIO m =>
Ptr Music -> CInt -> CInt -> CDouble -> m CInt
SDL.Raw.Mixer.fadeInMusicPos
      Ptr Music
p
      CInt
t'
      (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
ms)
      (Int -> CDouble
forall a b. (Real a, Fractional b) => a -> b
realToFrac Int
at)
  where
    t' :: CInt
t' = case Times
times of
      Times
Forever -> (-CInt
1)
      Times CInt
t -> CInt -> CInt -> CInt
forall a. Ord a => a -> a -> a
max CInt
1 CInt
t

-- | Returns the `Music`'s 'Fading' status.
fadingMusic :: MonadIO m => m Fading
fadingMusic :: m Fading
fadingMusic = Fading -> Fading
wordToFading (Fading -> Fading) -> m Fading -> m Fading
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> m Fading
forall (m :: * -> *). MonadIO m => m Fading
SDL.Raw.Mixer.fadingMusic

-- | Gets the current 'Volume' setting for 'Music'.
getMusicVolume :: MonadIO m => m Volume
getMusicVolume :: m Int
getMusicVolume = CInt -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (CInt -> Int) -> m CInt -> m Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CInt -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> m CInt
SDL.Raw.Mixer.volumeMusic (-CInt
1)

-- | Sets the 'Volume' for 'Music'.
--
-- Note that this won't work if any 'Music' is currently fading.
setMusicVolume :: MonadIO m => Volume -> m ()
setMusicVolume :: Int -> m ()
setMusicVolume Int
v = m CInt -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m CInt -> m ()) -> (CInt -> m CInt) -> CInt -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CInt -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> m CInt
SDL.Raw.Mixer.volumeMusic (CInt -> m ()) -> CInt -> m ()
forall a b. (a -> b) -> a -> b
$ Int -> CInt
volumeToCInt Int
v

-- | A `Music`'s type.
data MusicType
  = CMD
  | WAV
  | MOD
  | MID
  | OGG
  | MP3
  | FLAC
  deriving stock (MusicType -> MusicType -> Bool
(MusicType -> MusicType -> Bool)
-> (MusicType -> MusicType -> Bool) -> Eq MusicType
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: MusicType -> MusicType -> Bool
$c/= :: MusicType -> MusicType -> Bool
== :: MusicType -> MusicType -> Bool
$c== :: MusicType -> MusicType -> Bool
Eq, Int -> MusicType -> ShowS
[MusicType] -> ShowS
MusicType -> String
(Int -> MusicType -> ShowS)
-> (MusicType -> String)
-> ([MusicType] -> ShowS)
-> Show MusicType
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [MusicType] -> ShowS
$cshowList :: [MusicType] -> ShowS
show :: MusicType -> String
$cshow :: MusicType -> String
showsPrec :: Int -> MusicType -> ShowS
$cshowsPrec :: Int -> MusicType -> ShowS
Show, ReadPrec [MusicType]
ReadPrec MusicType
Int -> ReadS MusicType
ReadS [MusicType]
(Int -> ReadS MusicType)
-> ReadS [MusicType]
-> ReadPrec MusicType
-> ReadPrec [MusicType]
-> Read MusicType
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [MusicType]
$creadListPrec :: ReadPrec [MusicType]
readPrec :: ReadPrec MusicType
$creadPrec :: ReadPrec MusicType
readList :: ReadS [MusicType]
$creadList :: ReadS [MusicType]
readsPrec :: Int -> ReadS MusicType
$creadsPrec :: Int -> ReadS MusicType
Read, Eq MusicType
Eq MusicType
-> (MusicType -> MusicType -> Ordering)
-> (MusicType -> MusicType -> Bool)
-> (MusicType -> MusicType -> Bool)
-> (MusicType -> MusicType -> Bool)
-> (MusicType -> MusicType -> Bool)
-> (MusicType -> MusicType -> MusicType)
-> (MusicType -> MusicType -> MusicType)
-> Ord MusicType
MusicType -> MusicType -> Bool
MusicType -> MusicType -> Ordering
MusicType -> MusicType -> MusicType
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: MusicType -> MusicType -> MusicType
$cmin :: MusicType -> MusicType -> MusicType
max :: MusicType -> MusicType -> MusicType
$cmax :: MusicType -> MusicType -> MusicType
>= :: MusicType -> MusicType -> Bool
$c>= :: MusicType -> MusicType -> Bool
> :: MusicType -> MusicType -> Bool
$c> :: MusicType -> MusicType -> Bool
<= :: MusicType -> MusicType -> Bool
$c<= :: MusicType -> MusicType -> Bool
< :: MusicType -> MusicType -> Bool
$c< :: MusicType -> MusicType -> Bool
compare :: MusicType -> MusicType -> Ordering
$ccompare :: MusicType -> MusicType -> Ordering
$cp1Ord :: Eq MusicType
Ord, MusicType
MusicType -> MusicType -> Bounded MusicType
forall a. a -> a -> Bounded a
maxBound :: MusicType
$cmaxBound :: MusicType
minBound :: MusicType
$cminBound :: MusicType
Bounded)

wordToMusicType :: SDL.Raw.Mixer.MusicType -> Maybe MusicType
wordToMusicType :: Fading -> Maybe MusicType
wordToMusicType = \case
  Fading
SDL.Raw.Mixer.MUS_NONE -> Maybe MusicType
forall a. Maybe a
Nothing
  Fading
SDL.Raw.Mixer.MUS_CMD -> MusicType -> Maybe MusicType
forall a. a -> Maybe a
Just MusicType
CMD
  Fading
SDL.Raw.Mixer.MUS_WAV -> MusicType -> Maybe MusicType
forall a. a -> Maybe a
Just MusicType
WAV
  Fading
SDL.Raw.Mixer.MUS_MOD -> MusicType -> Maybe MusicType
forall a. a -> Maybe a
Just MusicType
MOD
  Fading
SDL.Raw.Mixer.MUS_MID -> MusicType -> Maybe MusicType
forall a. a -> Maybe a
Just MusicType
MID
  Fading
SDL.Raw.Mixer.MUS_OGG -> MusicType -> Maybe MusicType
forall a. a -> Maybe a
Just MusicType
OGG
  Fading
SDL.Raw.Mixer.MUS_MP3 -> MusicType -> Maybe MusicType
forall a. a -> Maybe a
Just MusicType
MP3
  Fading
SDL.Raw.Mixer.MUS_FLAC -> MusicType -> Maybe MusicType
forall a. a -> Maybe a
Just MusicType
FLAC
  Fading
_ -> Maybe MusicType
forall a. Maybe a
Nothing

-- | Gets the 'MusicType' of a given 'Music'.
musicType :: Music -> Maybe MusicType
musicType :: Music -> Maybe MusicType
musicType (Music Ptr Music
p) =
  Fading -> Maybe MusicType
wordToMusicType (Fading -> Maybe MusicType) -> Fading -> Maybe MusicType
forall a b. (a -> b) -> a -> b
$ IO Fading -> Fading
forall a. IO a -> a
unsafePerformIO (Ptr Music -> IO Fading
forall (m :: * -> *). MonadIO m => Ptr Music -> m Fading
SDL.Raw.Mixer.getMusicType Ptr Music
p)

-- | Gets the 'MusicType' of currently playing 'Music', if any.
playingMusicType :: MonadIO m => m (Maybe MusicType)
playingMusicType :: m (Maybe MusicType)
playingMusicType = Fading -> Maybe MusicType
wordToMusicType (Fading -> Maybe MusicType) -> m Fading -> m (Maybe MusicType)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Ptr Music -> m Fading
forall (m :: * -> *). MonadIO m => Ptr Music -> m Fading
SDL.Raw.Mixer.getMusicType Ptr Music
forall a. Ptr a
nullPtr

-- More quackery, but this time for the music finished callback.
{-# NOINLINE musicFinishedFunPtr #-}
musicFinishedFunPtr :: IORef (FunPtr (IO ()))
musicFinishedFunPtr :: IORef (FunPtr (IO ()))
musicFinishedFunPtr = IO (IORef (FunPtr (IO ()))) -> IORef (FunPtr (IO ()))
forall a. IO a -> a
unsafePerformIO (IO (IORef (FunPtr (IO ()))) -> IORef (FunPtr (IO ())))
-> IO (IORef (FunPtr (IO ()))) -> IORef (FunPtr (IO ()))
forall a b. (a -> b) -> a -> b
$ FunPtr (IO ()) -> IO (IORef (FunPtr (IO ())))
forall a. a -> IO (IORef a)
newIORef FunPtr (IO ())
forall a. FunPtr a
nullFunPtr

-- | Sets a callback that gets invoked each time a 'Music' finishes playing.
--
-- __Note: don't call other 'SDL.Mixer' functions within this callback.__
whenMusicFinished :: MonadIO m => IO () -> m ()
whenMusicFinished :: IO () -> m ()
whenMusicFinished IO ()
callback = IO () -> m ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> IO () -> m ()
forall a b. (a -> b) -> a -> b
$ do
  FunPtr (IO ())
callbackRaw <- IO () -> IO (FunPtr (IO ()))
SDL.Raw.Mixer.wrapMusicCallback IO ()
callback
  FunPtr (IO ()) -> IO ()
forall (m :: * -> *). MonadIO m => FunPtr (IO ()) -> m ()
SDL.Raw.Mixer.hookMusicFinished FunPtr (IO ())
callbackRaw
  FunPtr (IO ())
lastFunPtr <- IORef (FunPtr (IO ())) -> IO (FunPtr (IO ()))
forall a. IORef a -> IO a
readIORef IORef (FunPtr (IO ()))
musicFinishedFunPtr
  Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (FunPtr (IO ())
lastFunPtr FunPtr (IO ()) -> FunPtr (IO ()) -> Bool
forall a. Eq a => a -> a -> Bool
/= FunPtr (IO ())
forall a. FunPtr a
nullFunPtr) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ FunPtr (IO ()) -> IO ()
forall a. FunPtr a -> IO ()
freeHaskellFunPtr FunPtr (IO ())
lastFunPtr
  IORef (FunPtr (IO ())) -> FunPtr (IO ()) -> IO ()
forall a. IORef a -> a -> IO ()
writeIORef IORef (FunPtr (IO ()))
musicFinishedFunPtr FunPtr (IO ())
callbackRaw

-- | A post-processing effect as a function operating on a mutable stream.
--
-- __Note that, at the moment, this is a stream of bytes. Depending on the__
-- __'Audio' 'Format' you're using, you're probably going to want to treat is__
-- __as a stream of 16-bit values instead.__
type Effect = Channel -> IOVector Word8 -> IO () -- TODO: Don't hardcode Word8.

-- | A function called when a processor is finished being used.
--
-- This allows you to clean up any state you might have had.
type EffectFinished = Channel -> IO ()

-- | A way to refer to the special 'Channel' used for post-processing effects.
--
-- You can only use this value with 'effect' and the other in-built effect
-- functions such as 'effectPan' and 'effectDistance'.
pattern PostProcessing :: Channel
pattern $bPostProcessing :: Channel
$mPostProcessing :: forall r. Channel -> (Void# -> r) -> (Void# -> r) -> r
PostProcessing = SDL.Raw.Mixer.CHANNEL_POST

-- | Adds a post-processing 'Effect' to a certain 'Channel'.
--
-- A `Channel`'s 'Effect's are called in the order they were added.
--
-- Returns an action that, when executed, removes this 'Effect'. __Note: do__
-- __execute this returned action more than once.__
effect :: MonadIO m => Channel -> EffectFinished -> Effect -> m (m ())
effect :: Channel -> (Channel -> IO ()) -> Effect -> m (m ())
effect (Channel CInt
channel) Channel -> IO ()
fin Effect
ef = do
  FunPtr Effect
ef' <- IO (FunPtr Effect) -> m (FunPtr Effect)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (FunPtr Effect) -> m (FunPtr Effect))
-> IO (FunPtr Effect) -> m (FunPtr Effect)
forall a b. (a -> b) -> a -> b
$
    Effect -> IO (FunPtr Effect)
SDL.Raw.Mixer.wrapEffect (Effect -> IO (FunPtr Effect)) -> Effect -> IO (FunPtr Effect)
forall a b. (a -> b) -> a -> b
$ \CInt
c Ptr ()
p CInt
len Ptr ()
_ -> do
      ForeignPtr Word8
fp <- ForeignPtr () -> ForeignPtr Word8
forall a b. ForeignPtr a -> ForeignPtr b
castForeignPtr (ForeignPtr () -> ForeignPtr Word8)
-> IO (ForeignPtr ()) -> IO (ForeignPtr Word8)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Ptr () -> IO (ForeignPtr ())
forall a. Ptr a -> IO (ForeignPtr a)
newForeignPtr_ Ptr ()
p
      Effect
ef (CInt -> Channel
Channel CInt
c) (IOVector Word8 -> IO ())
-> (Int -> IOVector Word8) -> Int -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ForeignPtr Word8 -> Int -> IOVector Word8
forall a s. Storable a => ForeignPtr a -> Int -> MVector s a
unsafeFromForeignPtr0 ForeignPtr Word8
fp (Int -> IO ()) -> Int -> IO ()
forall a b. (a -> b) -> a -> b
$ CInt -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral CInt
len

  FunPtr EffectFinished
fin' <- IO (FunPtr EffectFinished) -> m (FunPtr EffectFinished)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (FunPtr EffectFinished) -> m (FunPtr EffectFinished))
-> IO (FunPtr EffectFinished) -> m (FunPtr EffectFinished)
forall a b. (a -> b) -> a -> b
$
    EffectFinished -> IO (FunPtr EffectFinished)
SDL.Raw.Mixer.wrapEffectFinished (EffectFinished -> IO (FunPtr EffectFinished))
-> EffectFinished -> IO (FunPtr EffectFinished)
forall a b. (a -> b) -> a -> b
$ \CInt
c Ptr ()
_ ->
      Channel -> IO ()
fin (Channel -> IO ()) -> Channel -> IO ()
forall a b. (a -> b) -> a -> b
$ CInt -> Channel
Channel CInt
c

  CInt
result <- CInt -> FunPtr Effect -> FunPtr EffectFinished -> Ptr () -> m CInt
forall (m :: * -> *).
MonadIO m =>
CInt -> FunPtr Effect -> FunPtr EffectFinished -> Ptr () -> m CInt
SDL.Raw.Mixer.registerEffect CInt
channel FunPtr Effect
ef' FunPtr EffectFinished
fin' Ptr ()
forall a. Ptr a
nullPtr

  if CInt
result CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
== CInt
0
    then do
      IO (m ()) -> m (m ())
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (m ()) -> m (m ())) -> IO (m ()) -> m (m ())
forall a b. (a -> b) -> a -> b
$ do
        FunPtr Effect -> IO ()
forall a. FunPtr a -> IO ()
freeHaskellFunPtr FunPtr Effect
ef' IO () -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> FunPtr EffectFinished -> IO ()
forall a. FunPtr a -> IO ()
freeHaskellFunPtr FunPtr EffectFinished
fin'
        Text
err <- IO Text
forall (m :: * -> *). MonadIO m => m Text
getError
        SDLException -> IO (m ())
forall e a. Exception e => e -> IO a
throwIO (SDLException -> IO (m ())) -> SDLException -> IO (m ())
forall a b. (a -> b) -> a -> b
$ Text -> Text -> Text -> SDLException
SDLCallFailed Text
"SDL.Raw.Mixer.addEffect" Text
"Mix_RegisterEffect" Text
err
    else m () -> m (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> m (m ())) -> (IO () -> m ()) -> IO () -> m (m ())
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IO () -> m ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m (m ())) -> IO () -> m (m ())
forall a b. (a -> b) -> a -> b
$ do
      -- The unregister action.
      CInt
removed <- CInt -> FunPtr Effect -> IO CInt
forall (m :: * -> *). MonadIO m => CInt -> FunPtr Effect -> m CInt
SDL.Raw.Mixer.unregisterEffect CInt
channel FunPtr Effect
ef'
      FunPtr Effect -> IO ()
forall a. FunPtr a -> IO ()
freeHaskellFunPtr FunPtr Effect
ef' IO () -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> FunPtr EffectFinished -> IO ()
forall a. FunPtr a -> IO ()
freeHaskellFunPtr FunPtr EffectFinished
fin'
      Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (CInt
removed CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
== CInt
0) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
        Text
err <- IO Text
forall (m :: * -> *). MonadIO m => m Text
getError
        SDLException -> IO ()
forall e a. Exception e => e -> IO a
throwIO (SDLException -> IO ()) -> SDLException -> IO ()
forall a b. (a -> b) -> a -> b
$
          Text -> Text -> Text -> SDLException
SDLCallFailed Text
"SDL.Raw.Mixer.removeEffect" Text
"Mix_UnregisterEffect" Text
err

-- | Applies an in-built effect implementing panning.
--
-- Sets the left-channel and right-channel 'Volume' to the given values.
--
-- This only works when `Audio`'s 'Output' is 'Stereo', which is the default.
--
-- Returns an action that, when executed, removes this effect. That action
-- simply calls 'effectPan' with 'Volumes' 128 and 128.
effectPan :: MonadIO m => Channel -> Volume -> Volume -> m (m ())
effectPan :: Channel -> Int -> Int -> m (m ())
effectPan channel :: Channel
channel@(Channel CInt
c) Int
lVol Int
rVol = do
  m CInt -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m CInt -> m ()) -> (m CInt -> m CInt) -> m CInt -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text -> m CInt -> m CInt
forall a (m :: * -> *).
(Eq a, MonadIO m, Num a) =>
Text -> Text -> m a -> m a
throwIf0 Text
"SDL.Raw.Mixer.effectPan" Text
"Mix_SetPanning" (m CInt -> m ()) -> m CInt -> m ()
forall a b. (a -> b) -> a -> b
$
    CInt -> Word8 -> Word8 -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> Word8 -> Word8 -> m CInt
SDL.Raw.Mixer.setPanning CInt
c (Int -> Word8
wordVol Int
lVol) (Int -> Word8
wordVol Int
rVol)
  m () -> m (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> m (m ())) -> (m (m ()) -> m ()) -> m (m ()) -> m (m ())
forall b c a. (b -> c) -> (a -> b) -> a -> c
. m (m ()) -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m (m ()) -> m (m ())) -> m (m ()) -> m (m ())
forall a b. (a -> b) -> a -> b
$ Channel -> Int -> Int -> m (m ())
forall (m :: * -> *).
MonadIO m =>
Channel -> Int -> Int -> m (m ())
effectPan Channel
channel Int
128 Int
128

wordVol :: Volume -> Word8
wordVol :: Int -> Word8
wordVol = CInt -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (CInt -> Word8) -> (Int -> CInt) -> Int -> Word8
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CInt -> CInt -> CInt
forall a. Ord a => a -> a -> a
min CInt
255 (CInt -> CInt) -> (Int -> CInt) -> Int -> CInt
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (CInt -> CInt -> CInt
forall a. Num a => a -> a -> a
* CInt
2) (CInt -> CInt) -> (Int -> CInt) -> Int -> CInt
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> CInt
volumeToCInt

-- | Applies a different volume based on the distance (as 'Word8') specified.
--
-- The volume is loudest at distance 0, quietest at distance 255.
--
-- Returns an action that, when executed, removes this effect. That action
-- simply calls 'effectDistance' with a distance of 0.
effectDistance :: MonadIO m => Channel -> Word8 -> m (m ())
effectDistance :: Channel -> Word8 -> m (m ())
effectDistance channel :: Channel
channel@(Channel CInt
c) Word8
dist = do
  m CInt -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m CInt -> m ()) -> (m CInt -> m CInt) -> m CInt -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text -> m CInt -> m CInt
forall a (m :: * -> *).
(Eq a, MonadIO m, Num a) =>
Text -> Text -> m a -> m a
throwIf0 Text
"SDL.Raw.Mixer.effectDistance" Text
"Mix_SetDistance" (m CInt -> m ()) -> m CInt -> m ()
forall a b. (a -> b) -> a -> b
$
    CInt -> Word8 -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> Word8 -> m CInt
SDL.Raw.Mixer.setDistance CInt
c Word8
dist
  m () -> m (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> m (m ())) -> (m (m ()) -> m ()) -> m (m ()) -> m (m ())
forall b c a. (b -> c) -> (a -> b) -> a -> c
. m (m ()) -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m (m ()) -> m (m ())) -> m (m ()) -> m (m ())
forall a b. (a -> b) -> a -> b
$ Channel -> Word8 -> m (m ())
forall (m :: * -> *). MonadIO m => Channel -> Word8 -> m (m ())
effectDistance Channel
channel Word8
0

-- | Simulates a simple 3D audio effect.
--
-- Accepts the angle in degrees (as 'Int16') in relation to the source of the
-- sound (0 is directly in front, 90 directly to the right, and so on) and a
-- distance (as 'Word8') from the source of the sound (where 255 is very far
-- away, and 0 extremely close).
--
-- Returns an action that, when executed, removes this effect. That action
-- simply calls 'effectPosition' with both angle and distance set to 0.
effectPosition :: MonadIO m => Channel -> Int16 -> Word8 -> m (m ())
effectPosition :: Channel -> Int16 -> Word8 -> m (m ())
effectPosition channel :: Channel
channel@(Channel CInt
c) Int16
angle Word8
dist = do
  m CInt -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m CInt -> m ()) -> (m CInt -> m CInt) -> m CInt -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text -> m CInt -> m CInt
forall a (m :: * -> *).
(Eq a, MonadIO m, Num a) =>
Text -> Text -> m a -> m a
throwIf0 Text
"SDL.Raw.Mixer.effectPosition" Text
"Mix_SetPosition" (m CInt -> m ()) -> m CInt -> m ()
forall a b. (a -> b) -> a -> b
$
    CInt -> Int16 -> Word8 -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> Int16 -> Word8 -> m CInt
SDL.Raw.Mixer.setPosition CInt
c Int16
angle Word8
dist
  m () -> m (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> m (m ())) -> (m (m ()) -> m ()) -> m (m ()) -> m (m ())
forall b c a. (b -> c) -> (a -> b) -> a -> c
. m (m ()) -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m (m ()) -> m (m ())) -> m (m ()) -> m (m ())
forall a b. (a -> b) -> a -> b
$ Channel -> Int16 -> Word8 -> m (m ())
forall (m :: * -> *).
MonadIO m =>
Channel -> Int16 -> Word8 -> m (m ())
effectPosition Channel
channel Int16
0 Word8
0

-- | Swaps the left and right channel sound.
--
-- If given 'True', will swap the sound channels.
--
-- Returns an action that, when executed, removes this effect. That action
-- simply calls 'effectReverseStereo' with 'False'.
effectReverseStereo :: MonadIO m => Channel -> Bool -> m (m ())
effectReverseStereo :: Channel -> Bool -> m (m ())
effectReverseStereo channel :: Channel
channel@(Channel CInt
c) Bool
rev = do
  m CInt -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m CInt -> m ()) -> (m CInt -> m CInt) -> m CInt -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text -> m CInt -> m CInt
forall a (m :: * -> *).
(Eq a, MonadIO m, Num a) =>
Text -> Text -> m a -> m a
throwIf0 Text
"SDL.Raw.Mixer.effectReverseStereo" Text
"Mix_SetReverseStereo" (m CInt -> m ()) -> m CInt -> m ()
forall a b. (a -> b) -> a -> b
$
    CInt -> CInt -> m CInt
forall (m :: * -> *). MonadIO m => CInt -> CInt -> m CInt
SDL.Raw.Mixer.setReverseStereo CInt
c (if Bool
rev then CInt
1 else CInt
0)
  m () -> m (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> m (m ())) -> (m (m ()) -> m ()) -> m (m ()) -> m (m ())
forall b c a. (b -> c) -> (a -> b) -> a -> c
. m (m ()) -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m (m ()) -> m (m ())) -> m (m ()) -> m (m ())
forall a b. (a -> b) -> a -> b
$ Channel -> Bool -> m (m ())
forall (m :: * -> *). MonadIO m => Channel -> Bool -> m (m ())
effectReverseStereo Channel
channel Bool
False

-- Music
-- TODO: hookMusic
-- TODO: setMusicCMD
-- TODO: getMusicHookData

-- Effects
-- TODO: setPostMix

-- SoundFonts
-- TODO: setSynchroValue
-- TODO: getSynchroValue
-- TODO: setSoundFonts
-- TODO: getSoundFonts
-- TODO: eachSoundFont