I’m writing a Haskell command line application that runs on Linux, Windows and OS X. I now have to play audio files (.wav
, .ogg
and .mp3
) from it. How would I go about implementing a function
playAudioFile :: FilePath -> IO ()
or even better
playAudio :: ByteString -> IO ()
that simply works on all system?
(I’m happy to invoke common command line tools and also don’t mind bundling them for the Windows distribution.)
This is the code I came up with, using SDL-1.2:
module PlaySound (withSound, playSound) where
import Control.Monad
import System.IO
import System.Directory
import Data.Foldable
import Control.Exception
import qualified Data.ByteString.Lazy as B
import Foreign.ForeignPtr
import Graphics.UI.SDL as SDL
import Graphics.UI.SDL.Mixer as Mix
withSound :: IO a -> IO a
withSound = bracket_ init cleanup
where
init = do
SDL.init [SDL.InitAudio]
getError >>= traverse_ putStrLn
ok <- Mix.tryOpenAudio Mix.defaultFrequency Mix.AudioS16LSB 2 4096
unless ok $
putStrLn "Failed to open SDL audio device"
cleanup = do
Mix.closeAudio
SDL.quit
playSound :: B.ByteString -> IO ()
playSound content = do
dir <- getTemporaryDirectory
(tmp, h) <- openTempFile dir "sdl-input"
B.hPutStr h content
hClose h
mus <- Mix.loadMUS tmp
Mix.playMusic mus 1
wait
-- This would double-free the Music, as it is also freed via a
-- finalizer
--Mix.freeMusic mus
finalizeForeignPtr mus
removeFile tmp
wait :: IO ()
wait = do
SDL.delay 50
stillPlaying <- Mix.playingMusic
when stillPlaying wait
The program in the end works fine, but
cabal install
the dependencies any more.So I’ll happily read better answers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With