{-# LANGUAGE DeriveAnyClass #-} {-# OPTIONS -Wall #-} -- | Bindings for types used in @raudio@ module Raylib.Types.Core.Audio ( -- * Enumerations MusicContextType (..), AudioBufferUsage (..), -- * Structures Wave (..), RAudioBuffer (..), RAudioProcessor (..), AudioStream (..), Sound (..), Music (..), -- * Pointer utilities p'wave'frameCount, p'wave'sampleRate, p'wave'sampleSize, p'wave'channels, p'wave'data, p'rAudioBuffer'converter, p'rAudioBuffer'callback, p'rAudioBuffer'processor, p'rAudioBuffer'volume, p'rAudioBuffer'pitch, p'rAudioBuffer'pan, p'rAudioBuffer'playing, p'rAudioBuffer'paused, p'rAudioBuffer'looping, p'rAudioBuffer'usage, p'rAudioBuffer'isSubBufferProcessed, p'rAudioBuffer'sizeInFrames, p'rAudioBuffer'frameCursorPos, p'rAudioBuffer'framesProcessed, p'rAudioBuffer'data, p'rAudioBuffer'next, p'rAudioBuffer'prev, p'rAudioProcessor'process, p'rAudioProcessor'next, p'rAudioProcessor'prev, p'audioStream'buffer, p'audioStream'processor, p'audioStream'sampleRate, p'audioStream'sampleSize, p'audioStream'channels, p'sound'stream, p'sound'frameCount, p'music'stream, p'music'frameCount, p'music'looping, p'music'ctxType, p'music'ctxData, -- * Callbacks AudioCallback, C'AudioCallback, ) where import Data.Maybe (fromMaybe) import Foreign ( FunPtr, Ptr, Storable (alignment, peek, poke, sizeOf), Word8, castPtr, fromBool, malloc, newArray, nullFunPtr, nullPtr, peekArray, plusPtr, toBool, ) import Foreign.C ( CBool, CFloat, CInt (..), CShort, CUChar, CUInt, ) import Raylib.Internal.Foreign (Freeable (rlFreeDependents), c'free, peekMaybe, peekStaticArray, pokeMaybe, pokeStaticArray) --------------------------------------- -- audio enums ------------------------ --------------------------------------- data MusicContextType = MusicAudioNone | MusicAudioWAV | MusicAudioOGG | MusicAudioFLAC | MusicAudioMP3 | MusicAudioQOA | MusicModuleXM | MusicModuleMOD deriving (Eq, Show, Enum) instance Storable MusicContextType where sizeOf _ = 4 alignment _ = 4 peek ptr = do val <- peek (castPtr ptr) return $ toEnum $ fromEnum (val :: CInt) poke ptr v = poke (castPtr ptr) (fromIntegral (fromEnum v) :: CInt) data AudioBufferUsage = AudioBufferUsageStatic | AudioBufferUsageStream deriving (Eq, Show, Enum) instance Storable AudioBufferUsage where sizeOf _ = 4 alignment _ = 4 peek ptr = do val <- peek (castPtr ptr) return $ toEnum $ fromEnum (val :: CInt) poke ptr v = poke (castPtr ptr) (fromIntegral (fromEnum v) :: CInt) --------------------------------------- -- audio structures ------------------- --------------------------------------- data Wave = Wave { wave'frameCount :: Integer, wave'sampleRate :: Integer, wave'sampleSize :: Integer, wave'channels :: Integer, wave'data :: [Int] } deriving (Eq, Show) instance Storable Wave where sizeOf _ = 24 alignment _ = 4 peek _p = do frameCount <- fromIntegral <$> peek (p'wave'frameCount _p) sampleRate <- fromIntegral <$> peek (p'wave'sampleRate _p) sampleSize <- fromIntegral <$> peek (p'wave'sampleSize _p) channels <- fromIntegral <$> peek (p'wave'channels _p) wDataPtr <- peek (p'wave'data _p) wData <- map fromIntegral <$> peekArray (fromInteger $ frameCount * channels) wDataPtr return $ Wave frameCount sampleRate sampleSize channels wData poke _p (Wave frameCount sampleRate sampleSize channels wData) = do poke (p'wave'frameCount _p) (fromIntegral frameCount) poke (p'wave'sampleRate _p) (fromIntegral sampleRate) poke (p'wave'sampleSize _p) (fromIntegral sampleSize) poke (p'wave'channels _p) (fromIntegral channels) poke (p'wave'data _p) =<< newArray (map fromIntegral wData) return () p'wave'frameCount :: Ptr Wave -> Ptr CUInt p'wave'frameCount = (`plusPtr` 0) p'wave'sampleRate :: Ptr Wave -> Ptr CUInt p'wave'sampleRate = (`plusPtr` 4) p'wave'sampleSize :: Ptr Wave -> Ptr CUInt p'wave'sampleSize = (`plusPtr` 8) p'wave'channels :: Ptr Wave -> Ptr CUInt p'wave'channels = (`plusPtr` 12) -- array (wave'frameCount * wave'channels) p'wave'data :: Ptr Wave -> Ptr (Ptr CShort) p'wave'data = (`plusPtr` 16) instance Freeable Wave where rlFreeDependents _ ptr = do dataPtr <- peek (p'wave'data ptr) c'free $ castPtr dataPtr -- RAudioBuffer/Processor are bound weirdly. They are currently used as `Ptr`s -- because peeking/poking them every time an audio function is called doesn't -- work properly (they are stored in a linked list in C, which makes it very -- difficult to properly marshal them). -- -- The types defined here are actually unnecessary because the pointers are -- never dereferenced. data RAudioBuffer = RAudioBuffer { rAudioBuffer'converter :: [Int], -- Implemented as an array of 78 integers because the entire `ma_data_converter` type is too complex rAudioBuffer'callback :: Maybe C'AudioCallback, rAudioBuffer'processor :: Maybe RAudioProcessor, rAudioBuffer'volume :: Float, rAudioBuffer'pitch :: Float, rAudioBuffer'pan :: Float, rAudioBuffer'playing :: Bool, rAudioBuffer'paused :: Bool, rAudioBuffer'looping :: Bool, rAudioBuffer'usage :: AudioBufferUsage, rAudioBuffer'isSubBufferProcessed :: [Bool], rAudioBuffer'sizeInFrames :: Integer, rAudioBuffer'frameCursorPos :: Integer, rAudioBuffer'framesProcessed :: Integer, rAudioBuffer'data :: [Word8], rAudioBuffer'next :: Maybe RAudioBuffer, rAudioBuffer'prev :: Maybe RAudioBuffer } deriving (Eq, Show, Freeable) instance Storable RAudioBuffer where sizeOf _ = 392 alignment _ = 8 peek _p = do base <- loadBase _p nextPtr <- peek (p'rAudioBuffer'next _p) next <- loadNext nextPtr prevPtr <- peek (p'rAudioBuffer'prev _p) prev <- loadPrev prevPtr return $ let p = base ((\a -> a {rAudioBuffer'prev = Just p}) <$> next) ((\a -> a {rAudioBuffer'next = Just p}) <$> prev) in p where getBytesPerSample = ([0, 1, 2, 3, 4, 4] !!) loadBase ptr = do converter <- map fromIntegral <$> peekStaticArray 78 (castPtr (p'rAudioBuffer'converter ptr) :: Ptr CInt) let formatIn = case converter of [] -> error "invalid miniaudio converter" x:_ -> x funPtr <- peek (p'rAudioBuffer'callback ptr) let callback = if funPtr == nullFunPtr then Nothing else Just funPtr processor <- peekMaybe (p'rAudioBuffer'processor ptr) volume <- realToFrac <$> peek (p'rAudioBuffer'volume ptr) pitch <- realToFrac <$> peek (p'rAudioBuffer'pitch ptr) pan <- realToFrac <$> peek (p'rAudioBuffer'pan ptr) playing <- toBool <$> peek (p'rAudioBuffer'playing ptr) paused <- toBool <$> peek (p'rAudioBuffer'paused ptr) looping <- toBool <$> peek (p'rAudioBuffer'looping ptr) usage <- peek (p'rAudioBuffer'usage ptr) isSubBufferProcessed <- map toBool <$> peekStaticArray 2 (p'rAudioBuffer'isSubBufferProcessed ptr) sizeInFrames <- fromIntegral <$> peek (p'rAudioBuffer'sizeInFrames ptr) frameCursorPos <- fromIntegral <$> peek (p'rAudioBuffer'frameCursorPos ptr) framesProcessed <- fromIntegral <$> peek (p'rAudioBuffer'framesProcessed ptr) bData <- map fromIntegral <$> (peekArray (fromIntegral $ sizeInFrames * 2 * getBytesPerSample formatIn) =<< peek (p'rAudioBuffer'data ptr)) return $ RAudioBuffer converter callback processor volume pitch pan playing paused looping usage isSubBufferProcessed sizeInFrames frameCursorPos framesProcessed bData loadNext ptr = if ptr == nullPtr then return Nothing else do base <- loadBase ptr nextPtr <- peek (p'rAudioBuffer'next ptr) next <- loadNext nextPtr let p = base ((\a -> a {rAudioBuffer'prev = Just p}) <$> next) Nothing in return (Just p) loadPrev ptr = if ptr == nullPtr then return Nothing else do base <- loadBase ptr prevPtr <- peek (p'rAudioBuffer'prev ptr) prev <- loadPrev prevPtr let p = base Nothing ((\a -> a {rAudioBuffer'next = Just p}) <$> prev) in return (Just p) poke _p a = do pokeBase _p a pokeNext _p $ rAudioBuffer'next a pokePrev _p $ rAudioBuffer'prev a return () where pokeBase ptr (RAudioBuffer converter callback processor volume pitch pan playing paused looping usage isSubBufferProcessed sizeInFrames frameCursorPos framesProcessed bData _ _) = do pokeStaticArray (castPtr (p'rAudioBuffer'converter ptr) :: Ptr CInt) (map fromIntegral converter) poke (p'rAudioBuffer'callback ptr) (fromMaybe nullFunPtr callback) pokeMaybe (p'rAudioBuffer'processor ptr) processor poke (p'rAudioBuffer'volume ptr) (realToFrac volume) poke (p'rAudioBuffer'pitch ptr) (realToFrac pitch) poke (p'rAudioBuffer'pan ptr) (realToFrac pan) poke (p'rAudioBuffer'playing ptr) (fromBool playing) poke (p'rAudioBuffer'paused ptr) (fromBool paused) poke (p'rAudioBuffer'looping ptr) (fromBool looping) poke (p'rAudioBuffer'usage ptr) usage pokeStaticArray (p'rAudioBuffer'isSubBufferProcessed ptr) (map fromBool isSubBufferProcessed) poke (p'rAudioBuffer'sizeInFrames ptr) (fromIntegral sizeInFrames) poke (p'rAudioBuffer'frameCursorPos ptr) (fromIntegral frameCursorPos) poke (p'rAudioBuffer'framesProcessed ptr) (fromIntegral framesProcessed) poke (p'rAudioBuffer'data ptr) =<< newArray (map fromIntegral bData :: [CUChar]) return () pokeNext basePtr pNext = case pNext of Nothing -> poke (p'rAudioBuffer'next basePtr) nullPtr Just val -> do nextPtr <- malloc pokeBase nextPtr val pokeNext nextPtr (rAudioBuffer'next val) poke (p'rAudioBuffer'prev nextPtr) basePtr poke (p'rAudioBuffer'next basePtr) nextPtr pokePrev basePtr pPrev = case pPrev of Nothing -> poke (p'rAudioBuffer'prev basePtr) nullPtr Just val -> do prevPtr <- malloc pokeBase prevPtr val poke (p'rAudioBuffer'next prevPtr) basePtr pokePrev prevPtr (rAudioBuffer'prev val) poke (p'rAudioBuffer'prev basePtr) prevPtr -- bytes (312) p'rAudioBuffer'converter :: Ptr RAudioBuffer -> Ptr () p'rAudioBuffer'converter = (`plusPtr` 0) -- maybe funptr p'rAudioBuffer'callback :: Ptr RAudioBuffer -> Ptr C'AudioCallback p'rAudioBuffer'callback = (`plusPtr` 312) -- maybe p'rAudioBuffer'processor :: Ptr RAudioBuffer -> Ptr (Ptr RAudioProcessor) p'rAudioBuffer'processor = (`plusPtr` 320) p'rAudioBuffer'volume :: Ptr RAudioBuffer -> Ptr CFloat p'rAudioBuffer'volume = (`plusPtr` 328) p'rAudioBuffer'pitch :: Ptr RAudioBuffer -> Ptr CFloat p'rAudioBuffer'pitch = (`plusPtr` 332) p'rAudioBuffer'pan :: Ptr RAudioBuffer -> Ptr CFloat p'rAudioBuffer'pan = (`plusPtr` 336) p'rAudioBuffer'playing :: Ptr RAudioBuffer -> Ptr CBool p'rAudioBuffer'playing = (`plusPtr` 340) p'rAudioBuffer'paused :: Ptr RAudioBuffer -> Ptr CBool p'rAudioBuffer'paused = (`plusPtr` 341) p'rAudioBuffer'looping :: Ptr RAudioBuffer -> Ptr CBool p'rAudioBuffer'looping = (`plusPtr` 342) p'rAudioBuffer'usage :: Ptr RAudioBuffer -> Ptr AudioBufferUsage p'rAudioBuffer'usage = (`plusPtr` 344) -- static array (2) p'rAudioBuffer'isSubBufferProcessed :: Ptr RAudioBuffer -> Ptr CBool p'rAudioBuffer'isSubBufferProcessed = (`plusPtr` 348) p'rAudioBuffer'sizeInFrames :: Ptr RAudioBuffer -> Ptr CUInt p'rAudioBuffer'sizeInFrames = (`plusPtr` 352) p'rAudioBuffer'frameCursorPos :: Ptr RAudioBuffer -> Ptr CUInt p'rAudioBuffer'frameCursorPos = (`plusPtr` 356) p'rAudioBuffer'framesProcessed :: Ptr RAudioBuffer -> Ptr CUInt p'rAudioBuffer'framesProcessed = (`plusPtr` 360) -- array (rAudioBuffer'sizeInFrames * 2 * ([0, 1, 2, 3, 4, 4] !! (peek (rAudioBuffer'converter :: Ptr CInt)))) p'rAudioBuffer'data :: Ptr RAudioBuffer -> Ptr (Ptr CUChar) p'rAudioBuffer'data = (`plusPtr` 368) -- maybe p'rAudioBuffer'next :: Ptr RAudioBuffer -> Ptr (Ptr RAudioBuffer) p'rAudioBuffer'next = (`plusPtr` 376) -- maybe p'rAudioBuffer'prev :: Ptr RAudioBuffer -> Ptr (Ptr RAudioBuffer) p'rAudioBuffer'prev = (`plusPtr` 384) data RAudioProcessor = RAudioProcessor { rAudioProcessor'process :: Maybe C'AudioCallback, rAudioProcessor'next :: Maybe RAudioProcessor, rAudioProcessor'prev :: Maybe RAudioProcessor } deriving (Eq, Show, Freeable) instance Storable RAudioProcessor where sizeOf _ = 24 alignment _ = 8 peek _p = do process <- loadProcess _p nextPtr <- peek (p'rAudioProcessor'next _p) next <- loadNext nextPtr prevPtr <- peek (p'rAudioProcessor'prev _p) prev <- loadPrev prevPtr return $ let p = RAudioProcessor process ((\a -> a {rAudioProcessor'prev = Just p}) <$> next) ((\a -> a {rAudioProcessor'next = Just p}) <$> prev) in p where loadProcess ptr = do funPtr <- peek (p'rAudioProcessor'process ptr) if funPtr == nullFunPtr then return Nothing else return (Just funPtr) loadNext ptr = if ptr == nullPtr then return Nothing else do process <- loadProcess ptr nextPtr <- peek (p'rAudioProcessor'next ptr) next <- loadNext nextPtr let p = RAudioProcessor process ((\a -> a {rAudioProcessor'prev = Just p}) <$> next) Nothing in return (Just p) loadPrev ptr = if ptr == nullPtr then return Nothing else do process <- loadProcess ptr prevPtr <- peek (p'rAudioProcessor'prev ptr) prev <- loadPrev prevPtr let p = RAudioProcessor process Nothing ((\a -> a {rAudioProcessor'next = Just p}) <$> prev) in return (Just p) poke _p (RAudioProcessor process next prev) = do poke (p'rAudioProcessor'process _p) (fromMaybe nullFunPtr process) pokeNext _p next pokePrev (castPtr _p) prev return () where pokeNext basePtr pNext = case pNext of Nothing -> poke (p'rAudioProcessor'next basePtr) nullPtr Just val -> do nextPtr <- malloc poke (p'rAudioProcessor'process nextPtr) (fromMaybe nullFunPtr (rAudioProcessor'process val)) pokeNext nextPtr (rAudioProcessor'next val) poke (p'rAudioProcessor'prev nextPtr) basePtr poke (p'rAudioProcessor'next basePtr) nextPtr pokePrev basePtr pPrev = case pPrev of Nothing -> poke (p'rAudioProcessor'prev basePtr) nullPtr Just val -> do prevPtr <- malloc poke (p'rAudioProcessor'process prevPtr) (fromMaybe nullFunPtr (rAudioProcessor'process val)) poke (p'rAudioProcessor'next prevPtr) basePtr pokePrev prevPtr (rAudioProcessor'prev val) poke (p'rAudioProcessor'prev basePtr) prevPtr -- maybe funptr p'rAudioProcessor'process :: Ptr RAudioProcessor -> Ptr C'AudioCallback p'rAudioProcessor'process = (`plusPtr` 0) -- maybe p'rAudioProcessor'next :: Ptr RAudioProcessor -> Ptr (Ptr RAudioProcessor) p'rAudioProcessor'next = (`plusPtr` 8) -- maybe p'rAudioProcessor'prev :: Ptr RAudioProcessor -> Ptr (Ptr RAudioProcessor) p'rAudioProcessor'prev = (`plusPtr` 16) data AudioStream = AudioStream { audioStream'buffer :: Ptr RAudioBuffer, audioStream'processor :: Ptr RAudioProcessor, audioStream'sampleRate :: Integer, audioStream'sampleSize :: Integer, audioStream'channels :: Integer } deriving (Eq, Show, Freeable) instance Storable AudioStream where sizeOf _ = 32 alignment _ = 8 peek _p = do buffer <- peek (p'audioStream'buffer _p) processor <- peek (p'audioStream'processor _p) sampleRate <- fromIntegral <$> peek (p'audioStream'sampleRate _p) sampleSize <- fromIntegral <$> peek (p'audioStream'sampleSize _p) channels <- fromIntegral <$> peek (p'audioStream'channels _p) return $ AudioStream buffer processor sampleRate sampleSize channels poke _p (AudioStream buffer processor sampleRate sampleSize channels) = do poke (p'audioStream'buffer _p) buffer poke (p'audioStream'processor _p) processor poke (p'audioStream'sampleRate _p) (fromIntegral sampleRate) poke (p'audioStream'sampleSize _p) (fromIntegral sampleSize) poke (p'audioStream'channels _p) (fromIntegral channels) return () -- maybe p'audioStream'buffer :: Ptr AudioStream -> Ptr (Ptr RAudioBuffer) p'audioStream'buffer = (`plusPtr` 0) -- maybe p'audioStream'processor :: Ptr AudioStream -> Ptr (Ptr RAudioProcessor) p'audioStream'processor = (`plusPtr` 8) p'audioStream'sampleRate :: Ptr AudioStream -> Ptr CUInt p'audioStream'sampleRate = (`plusPtr` 16) p'audioStream'sampleSize :: Ptr AudioStream -> Ptr CUInt p'audioStream'sampleSize = (`plusPtr` 20) p'audioStream'channels :: Ptr AudioStream -> Ptr CUInt p'audioStream'channels = (`plusPtr` 24) data Sound = Sound { sound'stream :: AudioStream, sound'frameCount :: Integer } deriving (Eq, Show, Freeable) instance Storable Sound where sizeOf _ = 40 alignment _ = 8 peek _p = do stream <- peek (p'sound'stream _p) frameCount <- fromIntegral <$> peek (p'sound'frameCount _p) return $ Sound stream frameCount poke _p (Sound stream frameCount) = do poke (p'sound'stream _p) stream poke (p'sound'frameCount _p) (fromIntegral frameCount) return () p'sound'stream :: Ptr Sound -> Ptr AudioStream p'sound'stream = (`plusPtr` 0) p'sound'frameCount :: Ptr Sound -> Ptr CUInt p'sound'frameCount = (`plusPtr` 32) data Music = Music { music'stream :: AudioStream, music'frameCount :: Integer, music'looping :: Bool, music'ctxType :: MusicContextType, music'ctxData :: Ptr () } deriving (Eq, Show, Freeable) instance Storable Music where sizeOf _ = 56 alignment _ = 4 peek _p = do stream <- peek (p'music'stream _p) frameCount <- fromIntegral <$> peek (p'music'frameCount _p) looping <- toBool <$> peek (p'music'looping _p) ctxType <- peek (p'music'ctxType _p) ctxData <- peek (p'music'ctxData _p) return $ Music stream frameCount looping ctxType ctxData poke _p (Music stream frameCount looping ctxType ctxData) = do poke (p'music'stream _p) stream poke (p'music'frameCount _p) (fromIntegral frameCount) poke (p'music'looping _p) (fromBool looping) poke (p'music'ctxType _p) ctxType poke (p'music'ctxData _p) ctxData return () p'music'stream :: Ptr Music -> Ptr AudioStream p'music'stream = (`plusPtr` 0) p'music'frameCount :: Ptr Music -> Ptr CUInt p'music'frameCount = (`plusPtr` 32) p'music'looping :: Ptr Music -> Ptr CBool p'music'looping = (`plusPtr` 36) p'music'ctxType :: Ptr Music -> Ptr MusicContextType p'music'ctxType = (`plusPtr` 40) -- bytes (?) p'music'ctxData :: Ptr Music -> Ptr (Ptr ()) p'music'ctxData = (`plusPtr` 48) --------------------------------------- -- audio callbacks -------------------- --------------------------------------- type AudioCallback = Ptr () -> Integer -> IO () type C'AudioCallback = FunPtr (Ptr () -> CUInt -> IO ())