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 " 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