packages feed

opusfile-0.1.0.0: examples/openal-playfile/Main.hs

module Main where

import Control.Concurrent (threadDelay)
import Data.StateVar (($=))
import System.Environment (getArgs)
import Text.Printf (printf)

import qualified Data.ByteString as ByteString
import qualified Foreign
import qualified Sound.OpenAL as OpenAL
import qualified Sound.OpusFile as OpusFile

main :: IO ()
main = getArgs >>= \case
  [] ->
    putStrLn "Usage: openal-playfile <filename.opus>"
  files ->
    withAL $
      mapM_ playFile files

playFile :: FilePath -> IO ()
playFile filename = do
  (time, source) <- sourceOpusFile filename
  OpenAL.play [source]
  putStrLn $ printf "Playing %s (%.1f s)" (show filename) time
  threadDelay $ truncate (time * 1000000)

sourceOpusFile :: FilePath -> IO (Double, OpenAL.Source)
sourceOpusFile filePath = do
  Right file <- ByteString.readFile filePath >>= OpusFile.openMemoryBS
  pcm <- OpusFile.decodeInt16 file
  OpusFile.free file

  buffer <- OpenAL.genObjectName
  Foreign.withForeignPtr (OpusFile.pcmData pcm) \ptr -> do
    let
      mreg =
        OpenAL.MemoryRegion ptr (fromIntegral $ OpusFile.pcmSize pcm)

      alFormat =
        case OpusFile.pcmChannels pcm of
          Right OpusFile.Stereo ->
            OpenAL.Stereo16
          Right OpusFile.Mono ->
            OpenAL.Mono16
          Left n ->
            error $ "unexpected channels: " <> show n

    OpenAL.bufferData buffer $= OpenAL.BufferData mreg alFormat 48000

  source <- OpenAL.genObjectName
  OpenAL.buffer source $= Just buffer
  pure (OpusFile.pcmTime pcm, source)

withAL :: IO () -> IO ()
withAL action = do
  OpenAL.openDevice Nothing >>= \case
    Nothing ->
      error  "boo. no device"
    Just device -> do
      OpenAL.createContext device [] >>= \case
        Nothing ->
          error "OpenAL.createContext failed"
        Just ctx ->
          OpenAL.currentContext $= Just ctx
      () <- action
      OpenAL.closeDevice device >>= print