module Engine.Sound.Source where

import RIO

import Sound.OpenAL.FFI.AL qualified as AL
import UnliftIO.Resource qualified as Resource
import Foreign qualified

class HasSource a where
  getSource :: a -> AL.Source

instance HasSource AL.Source where
  getSource :: Source -> Source
getSource = Source -> Source
forall a. a -> a
id

instance HasSource (a, AL.Source) where
  getSource :: (a, Source) -> Source
getSource = (a, Source) -> Source
forall a b. (a, b) -> b
snd

allocateCollectionWith
  :: ( Resource.MonadResource m
     , Traversable t
     , HasSource o
     )
  => (i -> m o)
  -> t i
  -> m (Resource.ReleaseKey, t o)
allocateCollectionWith :: (i -> m o) -> t i -> m (ReleaseKey, t o)
allocateCollectionWith i -> m o
action t i
collection = do
  t o
loaded <- (i -> m o) -> t i -> m (t o)
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse i -> m o
action t i
collection

  ReleaseKey
key <- IO () -> m ReleaseKey
forall (m :: * -> *). MonadResource m => IO () -> m ReleaseKey
Resource.register do
    -- let sources = map getSource $ toList loaded
    -- stop loaded
    -- for_ sources \source ->
    --   OpenAL.buffer source OpenAL.$= Nothing
    -- OpenAL.deleteObjectNames sources
    [Source] -> (Int -> Ptr Source -> IO ()) -> IO ()
forall a b. Storable a => [a] -> (Int -> Ptr a -> IO b) -> IO b
Foreign.withArrayLen ((o -> Source) -> [o] -> [Source]
forall a b. (a -> b) -> [a] -> [b]
map o -> Source
forall a. HasSource a => a -> Source
getSource ([o] -> [Source]) -> [o] -> [Source]
forall a b. (a -> b) -> a -> b
$ t o -> [o]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList t o
loaded) \Int
num ->
      CInt -> Ptr Source -> IO ()
AL.alDeleteSources (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
num)

  pure (ReleaseKey
key, t o
loaded)

{-# INLINE play1 #-}
play1 :: (HasSource a, MonadIO m) => a -> m ()
play1 :: a -> m ()
play1 a
src = [a] -> m ()
forall (t :: * -> *) a (m :: * -> *).
(Foldable t, HasSource a, MonadIO m) =>
t a -> m ()
play [a
src]

{-# INLINE play #-}
play :: (Foldable t, HasSource a, MonadIO m) => t a -> m ()
play :: t a -> m ()
play (t a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList -> [a]
sources) =
  IO () -> m ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> IO () -> m ()
forall a b. (a -> b) -> a -> b
$
    [Source] -> (Int -> Ptr Source -> IO ()) -> IO ()
forall a b. Storable a => [a] -> (Int -> Ptr a -> IO b) -> IO b
Foreign.withArrayLen ((a -> Source) -> [a] -> [Source]
forall a b. (a -> b) -> [a] -> [b]
map a -> Source
forall a. HasSource a => a -> Source
getSource [a]
sources) \Int
num ->
      CInt -> Ptr Source -> IO ()
AL.alSourcePlayv (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
num)

{-# INLINE stop1 #-}
stop1 :: (HasSource a, MonadIO m) => a -> m ()
stop1 :: a -> m ()
stop1 a
src = [a] -> m ()
forall (t :: * -> *) a (m :: * -> *).
(Foldable t, HasSource a, MonadIO m) =>
t a -> m ()
stop [a
src]

{-# INLINE stop #-}
stop :: (Foldable t, HasSource a, MonadIO m) => t a -> m ()
stop :: t a -> m ()
stop (t a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList -> [a]
sources) =
  IO () -> m ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> IO () -> m ()
forall a b. (a -> b) -> a -> b
$
    [Source] -> (Int -> Ptr Source -> IO ()) -> IO ()
forall a b. Storable a => [a] -> (Int -> Ptr a -> IO b) -> IO b
Foreign.withArrayLen ((a -> Source) -> [a] -> [Source]
forall a b. (a -> b) -> [a] -> [b]
map a -> Source
forall a. HasSource a => a -> Source
getSource [a]
sources) \Int
num ->
      CInt -> Ptr Source -> IO ()
AL.alSourceStopv (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
num)

{-# INLINE toggle #-}
toggle :: (HasSource a, MonadIO m) => Bool -> a -> m ()
toggle :: Bool -> a -> m ()
toggle Bool
active a
src =
  if Bool
active then
    a -> m ()
forall a (m :: * -> *). (HasSource a, MonadIO m) => a -> m ()
play1 a
src
  else
    a -> m ()
forall a (m :: * -> *). (HasSource a, MonadIO m) => a -> m ()
stop1 a
src