如何从跨平台的Haskell代码播放音频文件

时间:2020-12-16 12:16:03

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

我正在编写一个在Linux,Windows和OS X上运行的Haskell命令行应用程序。我现在必须从中播放音频文件(.wav,.ogg和.mp3)。我将如何实现一个功能

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.)

(我很高兴调用常用的命令行工具,也不介意将它们捆绑在Windows发行版中。)

1 个解决方案

#1


3  

This is the code I came up with, using SDL-1.2:

这是我提出的代码,使用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

该程序最终工作正常,但是

  • compiling the SDL bindings under Windows is tricky. I followed this nice explanation on how to do it
  • 在Windows下编译SDL绑定很棘手。我按照这个很好的解释如何做到这一点

  • the SDL bindings for SDL-1.2 seem to be unmaintained and do not even compile with GHC-7.8 or newer. I didn’t notice at first, because my distribution (Debian) patches around such issues, but it means that my users cannot easily cabal install the dependencies any more.
  • SDL-1.2的SDL绑定似乎没有维护,甚至不用GHC-7.8或更新版本进行编译。起初我没有注意到,因为我的发行版(Debian)修补了这些问题,但这意味着我的用户不能轻易地再次安装依赖项。

  • there are bindings for SDL-2, but none for the SDL_mixer, which I need here (I believe).
  • SDL-2有绑定,但SDL_mixer没有绑定,我在这里需要(我相信)。

So I’ll happily read better answers.

所以我很乐意阅读更好的答案。

#1


3  

This is the code I came up with, using SDL-1.2:

这是我提出的代码,使用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

该程序最终工作正常,但是

  • compiling the SDL bindings under Windows is tricky. I followed this nice explanation on how to do it
  • 在Windows下编译SDL绑定很棘手。我按照这个很好的解释如何做到这一点

  • the SDL bindings for SDL-1.2 seem to be unmaintained and do not even compile with GHC-7.8 or newer. I didn’t notice at first, because my distribution (Debian) patches around such issues, but it means that my users cannot easily cabal install the dependencies any more.
  • SDL-1.2的SDL绑定似乎没有维护,甚至不用GHC-7.8或更新版本进行编译。起初我没有注意到,因为我的发行版(Debian)修补了这些问题,但这意味着我的用户不能轻易地再次安装依赖项。

  • there are bindings for SDL-2, but none for the SDL_mixer, which I need here (I believe).
  • SDL-2有绑定,但SDL_mixer没有绑定,我在这里需要(我相信)。

So I’ll happily read better answers.

所以我很乐意阅读更好的答案。