packages feed

openal-ffi-0.0.1: test/Spec.hs

{-# 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