packages feed

openal-ffi (empty) → 0.0.1

raw patch · 9 files changed

+678/−0 lines, 9 filesdep +basedep +openal-ffisetup-changed

Dependencies added: base, openal-ffi

Files

+ ChangeLog.md view
@@ -0,0 +1,7 @@+# Changelog for openal-ffi++## [0.0.1]++- Initial import.++[0.0.1]: https://gitlab.com/dpwiz/openal-ffi/-/tree/v0.0.1
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Author name here (c) 2021++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Author name here nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,16 @@+# openal-ffi++> Very low-level bindings to the [OpenAL] library.++- No OpenGL dependencies.+- No point/vector/tensor/spaces dependencies.+- No state and no resource management.+- Modest DLL size.++## Examples++- [C API overview](https://ffainelli.github.io/openal-example/)+- [Buffer-playing example](https://gitlab.com/dpwiz/openal-ffi/-/blob/main/test/Spec.hs)+- [Opus-playing example](https://gitlab.com/dpwiz/opusfile/-/blob/master/examples/openal-playfile/Main.hs)++[OpenAL]: https://openal.org/
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ openal-ffi.cabal view
@@ -0,0 +1,55 @@+cabal-version: 1.12++-- This file has been generated from package.yaml by hpack version 0.34.4.+--+-- see: https://github.com/sol/hpack++name:           openal-ffi+version:        0.0.1+synopsis:       Low-level bindings to OpenAL.+category:       Sound+author:         IC Rainbow+maintainer:     aenor.realm@gmail.com+copyright:      2021 IC Rainbow+license:        BSD3+license-file:   LICENSE+build-type:     Simple+extra-source-files:+    README.md+    ChangeLog.md++source-repository head+  type: git+  location: https://gitlab.com/dpwiz/openal-ffi++library+  exposed-modules:+      Sound.OpenAL.FFI.AL+      Sound.OpenAL.FFI.ALC+      Sound.OpenAL.FFI.Utils+  other-modules:+      Paths_openal_ffi+  hs-source-dirs:+      src+  ghc-options: -Wall+  extra-libraries:+      openal+  build-depends:+      base >=4.7 && <5+  if os(darwin) || os(ios)+    frameworks:+        OpenAL+  default-language: Haskell2010++test-suite openal-ffi-test+  type: exitcode-stdio-1.0+  main-is: Spec.hs+  other-modules:+      Paths_openal_ffi+  hs-source-dirs:+      test+  ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N+  build-depends:+      base >=4.7 && <5+    , openal-ffi+  default-language: Haskell2010
+ src/Sound/OpenAL/FFI/AL.hs view
@@ -0,0 +1,304 @@+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE GeneralisedNewtypeDeriving #-}++module Sound.OpenAL.FFI.AL where++import Foreign (FunPtr, Ptr, Storable)+import Foreign.C.Types+import Foreign.C.String (CString)++import Sound.OpenAL.FFI.Utils (Dynamic)++-- * Source++newtype Source = Source CUInt+  deriving stock (Eq, Ord, Show)+  deriving newtype (Storable)++foreign import ccall unsafe "alGenSources"+  alGenSources :: CInt -> Ptr Source -> IO ()++foreign import ccall unsafe "alDeleteSources"+  alDeleteSources :: CInt -> Ptr Source -> IO ()++foreign import ccall unsafe "alIsSource"+  alIsSource :: Source -> IO CChar++foreign import ccall unsafe "alGetSourcefv"+  alGetSourcefv :: Source -> CInt -> Ptr CFloat -> IO ()++foreign import ccall unsafe "alSourcefv"+  alSourcefv :: Source -> CInt -> Ptr CFloat -> IO ()++foreign import ccall unsafe "alGetSourceiv"+  alGetSourceiv :: Source ->  CInt -> Ptr CInt -> IO ()++foreign import ccall unsafe "alSourcei"+  alSourcei :: Source -> CInt -> CInt -> IO ()++foreign import ccall unsafe "alSourceQueueBuffers"+  alSourceQueueBuffers :: Source -> CInt -> Ptr Buffer -> IO ()++foreign import ccall unsafe "alSourceUnqueueBuffers"+  alSourceUnqueueBuffers :: Source -> CInt -> Ptr Buffer -> IO ()++foreign import ccall unsafe "alSourcePlayv"+  alSourcePlayv :: CInt -> Ptr Source -> IO ()++foreign import ccall unsafe "alSourcePausev"+  alSourcePausev :: CInt -> Ptr Source -> IO ()++foreign import ccall unsafe "alSourceStopv"+  alSourceStopv :: CInt -> Ptr Source -> IO ()++foreign import ccall unsafe "alSourceRewindv"+  alSourceRewindv :: CInt -> Ptr Source -> IO ()++-- * Buffer++newtype Buffer = Buffer CUInt+  deriving stock (Eq, Ord, Show)+  deriving newtype (Storable)++foreign import ccall unsafe "alGenBuffers"+  alGenBuffers :: CInt -> Ptr Buffer -> IO ()++foreign import ccall unsafe "alDeleteBuffers"+  alDeleteBuffers :: CInt -> Ptr Buffer -> IO ()++foreign import ccall unsafe "alBufferData"+  alBufferData :: Buffer -> CInt -> Ptr () -> CInt -> CInt -> IO ()++foreign import ccall unsafe "alGetBufferi"+  alGetBufferi :: Buffer -> CInt -> Ptr CInt -> IO ()++foreign import ccall unsafe "alIsBuffer"+  alIsBuffer :: Buffer -> IO CChar++-- * Errors++foreign import ccall unsafe "alGetError"+  alGetError :: IO CInt++pattern INVALID_NAME :: (Eq a, Num a) => a+pattern INVALID_NAME = 0xA001++pattern INVALID_ENUM :: (Eq a, Num a) => a+pattern INVALID_ENUM = 0xA002++pattern INVALID_VALUE :: (Eq a, Num a) => a+pattern INVALID_VALUE = 0xA003++pattern INVALID_OPERATION :: (Eq a, Num a) => a+pattern INVALID_OPERATION = 0xA004++pattern OUT_OF_MEMORY :: (Eq a, Num a) => a+pattern OUT_OF_MEMORY = 0xA005++-- * Listener++foreign import ccall unsafe "alListenerf"+  alListenerf :: CInt -> CFloat -> IO ()++foreign import ccall unsafe "alListenerfv"+  alListenerfv :: CInt -> Ptr () -> IO ()++foreign import ccall unsafe "alGetListenerfv"+  alGetListenerfv :: CInt -> Ptr CFloat -> IO ()++-- * Queries++foreign import ccall unsafe "alGetEnumValue"+  alGetEnumValue :: CString -> IO CInt++foreign import ccall unsafe "alGetFloat"+  alGetFloat :: CInt -> IO CFloat++foreign import ccall unsafe "alGetIntegerv"+  alGetIntegerv :: CInt -> Ptr CInt -> IO ()++foreign import ccall unsafe "alDistanceModel"+  alDistanceModel :: CInt -> IO ()++-- ** Strings++foreign import ccall unsafe "alGetString"+  alGetString :: CInt -> IO CString++pattern VERSION :: (Eq a, Num a) => a+pattern VERSION = 0xB002++pattern RENDERER :: (Eq a, Num a) => a+pattern RENDERER = 0xB003++pattern VENDOR :: (Eq a, Num a) => a+pattern VENDOR = 0xB001++pattern EXTENSIONS :: (Eq a, Num a) => a+pattern EXTENSIONS = 0xB004++-- ** Extensions++foreign import ccall unsafe "alIsExtensionPresent"+  alIsExtensionPresent_ :: CString -> IO CBool++foreign import ccall unsafe "alGetProcAddress"+  alGetProcAddress :: CString -> IO (FunPtr a)++foreign import ccall unsafe "dynamic"+  invokeWithFloat :: Dynamic (CFloat -> IO ())++-- * Enums++pattern DISTANCE_MODEL :: (Eq a, Num a) => a+pattern DISTANCE_MODEL = 0xD000++pattern DOPPLER_FACTOR :: (Eq a, Num a) => a+pattern DOPPLER_FACTOR = 0xC000++pattern SPEED_OF_SOUND :: (Eq a, Num a) => a+pattern SPEED_OF_SOUND = 0xC003+++pattern INVERSE_DISTANCE :: (Eq a, Num a) => a+pattern INVERSE_DISTANCE = 0xD001++pattern INVERSE_DISTANCE_CLAMPED :: (Eq a, Num a) => a+pattern INVERSE_DISTANCE_CLAMPED = 0xD002++pattern LINEAR_DISTANCE :: (Eq a, Num a) => a+pattern LINEAR_DISTANCE = 0xD003++pattern LINEAR_DISTANCE_CLAMPED :: (Eq a, Num a) => a+pattern LINEAR_DISTANCE_CLAMPED = 0xD004++pattern EXPONENT_DISTANCE :: (Eq a, Num a) => a+pattern EXPONENT_DISTANCE = 0xD005++pattern EXPONENT_DISTANCE_CLAMPED :: (Eq a, Num a) => a+pattern EXPONENT_DISTANCE_CLAMPED = 0xD006+++pattern POSITION :: (Eq a, Num a) => a+pattern POSITION = 0x1004++pattern VELOCITY :: (Eq a, Num a) => a+pattern VELOCITY = 0x1006++pattern GAIN :: (Eq a, Num a) => a+pattern GAIN = 0x100A+++pattern ORIENTATION :: (Eq a, Num a) => a+pattern ORIENTATION = 0x100F+++pattern SOURCE_RELATIVE :: (Eq a, Num a) => a+pattern SOURCE_RELATIVE = 0x0202++pattern SOURCE_TYPE :: (Eq a, Num a) => a+pattern SOURCE_TYPE = 0x1027++pattern LOOPING :: (Eq a, Num a) => a+pattern LOOPING = 0x1007++pattern BUFFER :: (Eq a, Num a) => a+pattern BUFFER = 0x1009++pattern BUFFERS_QUEUED :: (Eq a, Num a) => a+pattern BUFFERS_QUEUED = 0x1015++pattern BUFFERS_PROCESSED :: (Eq a, Num a) => a+pattern BUFFERS_PROCESSED = 0x1016++pattern MIN_GAIN :: (Eq a, Num a) => a+pattern MIN_GAIN = 0x100D++pattern MAX_GAIN :: (Eq a, Num a) => a+pattern MAX_GAIN = 0x100E++pattern REFERENCE_DISTANCE :: (Eq a, Num a) => a+pattern REFERENCE_DISTANCE = 0x1020++pattern ROLLOFF_FACTOR :: (Eq a, Num a) => a+pattern ROLLOFF_FACTOR = 0x1021++pattern MAX_DISTANCE :: (Eq a, Num a) => a+pattern MAX_DISTANCE = 0x1023++pattern PITCH :: (Eq a, Num a) => a+pattern PITCH = 0x1003++pattern DIRECTION :: (Eq a, Num a) => a+pattern DIRECTION = 0x1005++pattern CONE_INNER_ANGLE :: (Eq a, Num a) => a+pattern CONE_INNER_ANGLE = 0x1001++pattern CONE_OUTER_ANGLE :: (Eq a, Num a) => a+pattern CONE_OUTER_ANGLE = 0x1002++pattern CONE_OUTER_GAIN :: (Eq a, Num a) => a+pattern CONE_OUTER_GAIN = 0x1022++pattern SEC_OFFSET :: (Eq a, Num a) => a+pattern SEC_OFFSET = 0x1024++pattern SAMPLE_OFFSET :: (Eq a, Num a) => a+pattern SAMPLE_OFFSET = 0x1025++pattern BYTE_OFFSET :: (Eq a, Num a) => a+pattern BYTE_OFFSET = 0x1026++pattern SOURCE_STATE :: (Eq a, Num a) => a+pattern SOURCE_STATE = 0x1010+++pattern UNDETERMINED :: (Eq a, Num a) => a+pattern UNDETERMINED = 0x1030++pattern STATIC :: (Eq a, Num a) => a+pattern STATIC = 0x1028++pattern STREAMING :: (Eq a, Num a) => a+pattern STREAMING = 0x1029+++pattern INITIAL :: (Eq a, Num a) => a+pattern INITIAL = 0x1011++pattern PLAYING :: (Eq a, Num a) => a+pattern PLAYING = 0x1012++pattern PAUSED :: (Eq a, Num a) => a+pattern PAUSED  = 0x1013++pattern STOPPED :: (Eq a, Num a) => a+pattern STOPPED = 0x1014+++pattern FREQUENCY :: (Eq a, Num a) => a+pattern FREQUENCY = 0x2001++pattern SIZE :: (Eq a, Num a) => a+pattern SIZE = 0x2004++pattern BITS :: (Eq a, Num a) => a+pattern BITS = 0x2002++pattern CHANNELS :: (Eq a, Num a) => a+pattern CHANNELS = 0x2003+++pattern FORMAT_MONO8 :: (Eq a, Num a) => a+pattern FORMAT_MONO8 = 0x1100++pattern FORMAT_MONO16 :: (Eq a, Num a) => a+pattern FORMAT_MONO16 = 0x1101++pattern FORMAT_STEREO8 :: (Eq a, Num a) => a+pattern FORMAT_STEREO8 = 0x1102++pattern FORMAT_STEREO16 :: (Eq a, Num a) => a+pattern FORMAT_STEREO16 = 0x1103
+ src/Sound/OpenAL/FFI/ALC.hs view
@@ -0,0 +1,151 @@+{-# LANGUAGE PatternSynonyms #-}++module Sound.OpenAL.FFI.ALC where++import Foreign (FunPtr, Ptr, nullPtr)+import Foreign.C.Types (CChar(..), CInt(..), CUInt(..))+import Foreign.C.String (CString)+import Sound.OpenAL.FFI.Utils (Dynamic)++-- * Context++newtype Context = Context (Ptr Context)+  deriving (Eq, Ord, Show)++foreign import ccall unsafe "alcCreateContext"+  alcCreateContext :: Device -> Ptr CInt -> IO Context++foreign import ccall unsafe "alcDestroyContext"+  alcDestroyContext :: Context -> IO ()++foreign import ccall unsafe "alcGetCurrentContext"+  alcGetCurrentContext :: IO Context++foreign import ccall unsafe "alcMakeContextCurrent"+  alcMakeContextCurrent :: Context -> IO CChar++foreign import ccall unsafe "alcSuspendContext"+  alcSuspendContext :: Context -> IO ()++foreign import ccall unsafe "alcProcessContext"+  alcProcessContext :: Context -> IO ()++foreign import ccall unsafe "alcGetContextsDevice"+  alcGetContextsDevice :: Context -> IO Device++-- * Device++newtype Device = Device (Ptr Device)+  deriving (Eq, Ord, Show)++nullDevice :: Device+nullDevice = Device nullPtr++foreign import ccall unsafe "alcOpenDevice"+  alcOpenDevice :: CString -> IO Device++foreign import ccall unsafe "alcCloseDevice"+  alcCloseDevice :: Device -> IO CChar++-- * Errors++foreign import ccall unsafe "alcGetError"+  alcGetError :: Device -> IO CInt++pattern INVALID_DEVICE :: (Eq a, Num a) => a+pattern INVALID_DEVICE = 0xA001++pattern INVALID_CONTEXT :: (Eq a, Num a) => a+pattern INVALID_CONTEXT = 0xA002++pattern INVALID_ENUM :: (Eq a, Num a) => a+pattern INVALID_ENUM = 0xA003++pattern INVALID_VALUE :: (Eq a, Num a) => a+pattern INVALID_VALUE = 0xA004++pattern INVALID_OPERATION :: (Eq a, Num a) => a+pattern INVALID_OPERATION = 0xA006++pattern OUT_OF_MEMORY :: (Eq a, Num a) => a+pattern OUT_OF_MEMORY = 0xA005++-- * Extensions++foreign import ccall unsafe "alcGetProcAddress"+  alcGetProcAddress :: Device -> CString -> IO (FunPtr a)++foreign import ccall unsafe "alcGetEnumValue"+  alcGetEnumValue :: Device -> CString -> IO CInt++-- * Queries++foreign import ccall unsafe "alcGetString"+  alcGetString :: Device -> CInt -> IO CString++foreign import ccall unsafe "alcGetIntegerv"+  alcGetIntegerv :: Device -> CInt -> CInt -> Ptr CInt -> IO ()++foreign import ccall unsafe "alcIsExtensionPresent"+  alcIsExtensionPresent_ :: Device -> CString -> IO CChar++-- * Capture++foreign import ccall unsafe "dynamic"+  invokeCaptureOpenDevice :: Dynamic (CString -> CUInt -> CInt -> CInt -> IO Device)++foreign import ccall unsafe "dynamic"+  invokeCaptureStartStop :: Dynamic (Device -> IO ())++foreign import ccall unsafe "dynamic"+  invokeCaptureSamples :: Dynamic (Device -> Ptr a -> CInt -> IO ())++foreign import ccall unsafe "dynamic"+  invokeCaptureCloseDevice :: Dynamic (Device -> IO CChar)++-- * Enums++pattern FREQUENCY :: (Eq a, Num a) => a+pattern FREQUENCY = 0x1007++pattern REFRESH :: (Eq a, Num a) => a+pattern REFRESH = 0x1008++pattern SYNC :: (Eq a, Num a) => a+pattern SYNC = 0x1009++pattern MONO_SOURCES :: (Eq a, Num a) => a+pattern MONO_SOURCES = 0x1010++pattern STEREO_SOURCES :: (Eq a, Num a) => a+pattern STEREO_SOURCES = 0x1011++pattern DEFAULT_DEVICE_SPECIFIER :: (Eq a, Num a) => a+pattern DEFAULT_DEVICE_SPECIFIER = 0x1004++pattern DEVICE_SPECIFIER :: (Eq a, Num a) => a+pattern DEVICE_SPECIFIER = 0x1005++pattern EXTENSIONS :: (Eq a, Num a) => a+pattern EXTENSIONS = 0x1006++pattern CAPTURE_DEFAULT_DEVICE_SPECIFIER :: (Eq a, Num a) => a+pattern CAPTURE_DEFAULT_DEVICE_SPECIFIER = 0x0311++pattern CAPTURE_DEVICE_SPECIFIER :: (Eq a, Num a) => a+pattern CAPTURE_DEVICE_SPECIFIER = 0x0310++pattern ATTRIBUTES_SIZE :: (Eq a, Num a) => a+pattern ATTRIBUTES_SIZE = 0x1002++pattern ALL_ATTRIBUTES :: (Eq a, Num a) => a+pattern ALL_ATTRIBUTES = 0x1003++pattern MAJOR_VERSION :: (Eq a, Num a) => a+pattern MAJOR_VERSION = 0x1000++pattern MINOR_VERSION :: (Eq a, Num a) => a+pattern MINOR_VERSION = 0x1001++pattern CAPTURE_SAMPLES :: (Eq a, Num a) => a+pattern CAPTURE_SAMPLES = 0x0312
+ src/Sound/OpenAL/FFI/Utils.hs view
@@ -0,0 +1,20 @@+module Sound.OpenAL.FFI.Utils+  ( Dynamic+  , peekCStrings+  ) where++import Foreign (FunPtr, lengthArray0, plusPtr)+import Foreign.C.String (CString, peekCAString)++type Dynamic a = FunPtr a -> a++peekCStrings :: CString -> IO [String]+peekCStrings = go []+  where+    go acc ptr = do+      str <- peekCAString ptr+      if null str then+        pure $ reverse acc+      else do+        len <- lengthArray0 0 ptr+        go (str : acc) $ plusPtr ptr (len + 1)
+ test/Spec.hs view
@@ -0,0 +1,93 @@+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE TupleSections #-}++import Control.Concurrent (threadDelay)+import Control.Monad (when)+import Data.Int (Int16)+import Foreign.C.String (peekCString, withCString)+import qualified Foreign++import qualified Sound.OpenAL.FFI.AL as AL+import qualified Sound.OpenAL.FFI.ALC as ALC+import Sound.OpenAL.FFI.Utils (peekCStrings)++main :: IO ()+main = do+  extensions <- fmap words $+    ALC.alcGetString ALC.nullDevice ALC.EXTENSIONS >>= peekCString+  putStrLn $ "ALC: " <> show extensions++  enumerationExt <- fmap (== 1) . withCString "ALC_ENUMERATION_EXT" $+    ALC.alcIsExtensionPresent_ ALC.nullDevice+  putStrLn $ "Enumeration available: " <> show enumerationExt++  when enumerationExt do+    allDevices <- ALC.alcGetString ALC.nullDevice ALC.DEVICE_SPECIFIER >>= peekCStrings+    putStrLn $ "All devices: " <> show allDevices++  defaultDevice <- ALC.alcGetString ALC.nullDevice ALC.DEFAULT_DEVICE_SPECIFIER >>= peekCString+  putStrLn $ "Default device: " <> show defaultDevice++  device <- ALC.alcOpenDevice Foreign.nullPtr+  context <- ALC.alcCreateContext device Foreign.nullPtr++  ok <- fmap (== 1) $ ALC.alcMakeContextCurrent context+  putStrLn $ "Context OK: " <> show ok+  when ok do+    buf@(AL.Buffer bufId) <- Foreign.alloca \bufPtr -> do+      AL.alGenBuffers 1 bufPtr+      Foreign.peek bufPtr+    AL.alGetError >>= print . (buf,)++    Foreign.withArrayLen soundData \bufSize bufData ->+      AL.alBufferData+        buf+        AL.FORMAT_MONO16+        (Foreign.castPtr bufData)+        (fromIntegral bufSize)+        48000+    AL.alGetError >>= print . (buf,)++    source <- Foreign.alloca \sourcePtr -> do+      AL.alGenSources 1 sourcePtr+      Foreign.peek sourcePtr+    AL.alGetError >>= print . (source,)++    AL.alSourcei source AL.BUFFER (fromIntegral bufId) -- XXX: unsigned to signed conversion+    AL.alGetError >>= print . (buf,)++    Foreign.with source $+      AL.alSourcePlayv 1++    threadDelay 1000000++    Foreign.with source $ AL.alDeleteSources 1+    Foreign.with buf $ AL.alDeleteBuffers 1++  ALC.alcDestroyContext context+  ALC.alcCloseDevice device >>= print++-- | Some funky sample+soundData :: [Int16]+soundData =+  map convert $+  zipWith+    (+)+    (sine 220) $+  zipWith+    (*)+    (sine 440)+    (sine 3)+  where+    convert d = round $ range * 0.5 * d+    range = fromIntegral (maxBound :: Int16)++    duration = 10.0+    rate = 48000++    sine freq =+      take (round $ duration * rate) $+        map sin [0.0 :: Double, step ..]+      where+        step =+          freq * 2 * pi / rate