如何在 Haskell 代码中跨平台播放音频文件

7

我正在编写一款Haskell命令行应用程序,可以在Linux、Windows和OS X上运行。现在我需要播放音频文件(.wav.ogg.mp3)。我该如何实现一个函数呢?

playAudioFile :: FilePath -> IO ()

甚至更好的是

playAudio :: ByteString -> IO ()

要在所有系统上简单工作的是什么?

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


这与https://dev59.com/I2Yr5IYBdhLWcg3wB16h有关,但我需要播放除“.wav”之外的常见文件格式。 - Joachim Breitner
如果这是真的可能的话,我会感到惊喜(愉快)。我的意思是,没有手动编写绑定到现有的C库... - MathematicalOrchid
嗯,也许https://dev59.com/I2Yr5IYBdhLWcg3wB16h#14011063实际上指向了正确的方向;SDL似乎支持[Graphics.UI.SDL.Mixer.Music](http://hackage.haskell.org/package/SDL-mixer-0.6.1/docs/Graphics-UI-SDL-Mixer-Music.html)中的MP3和Ogg。 - Joachim Breitner
gstreamer 可在 OSX、Windows 和 Linux 上运行,并支持许多不同的格式,因此这可能值得一试。 - ocharles
我有点担心能否得到一个良好的、友好的 Windows 自包含软件包,但至少这是可能的。 - Joachim Breitner
显示剩余2条评论
2个回答

3

这是我使用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

程序最终运行良好,但在Windows下编译SDL绑定很棘手。我遵循了这篇很好的解释
SDL-1.2的SDL绑定似乎未得到维护,甚至无法在GHC-7.8或更新版本中编译。一开始我没有注意到,因为我的发行版(Debian)会修补此类问题,但这意味着我的用户不能轻松地cabal install依赖项。
虽然有SDL-2的绑定,但没有我需要的SDL_mixer绑定(我认为)。所以我很乐意阅读更好的答案。

最终,我使用了一个专用的二进制文件,使用 SDL,以避免依赖于未维护的 SDL-1.2 Haskell 绑定。那个代码是用C编写的,仅仅是因为随着 SDL_mixer 的示例代码几乎足够好。 - Joachim Breitner
我认为能够简单地播放mp3或wav文件并不需要依赖SDL和SDL_mixer,或者至少隐藏这些细节会很好。我刚刚向SDL_mixer的维护者发送了一个补丁,让它能够在GHC 7.8下编译(它给我报了一个链接错误)。我很好奇:你遇到了什么问题?SDL2有mixer绑定,请参见https://github.com/jdeseno/hs-sdl2-mixer。 我在Keera Studios的Magic Cookies!中使用它们,并且它们工作正常(需稍加调整才能将其编译为Android)。 - Ivan Perez
我不记得了,但可能与http://comments.gmane.org/gmane.comp.lang.haskell.beginners/13663中提到的相同。关于“sd2-mixer”:它还没有在hackage上,这必须阻止我使用它们。 - Joachim Breitner
SDL-mixer的新版本(SDL 1.2)现在已经发布在Hackage上了:https://hackage.haskell.org/package/SDL-mixer我可以证实它可以与GHC 7.8兼容。 - Ivan Perez
如果有帮助的话,这里提供一个简单的例子,使用git克隆此gist。https://gist.github.com/ivanperez-keera/4155135931eb48201b6f - Ivan Perez
现在,2018年,SDL_mixer有SDL2绑定。使用此处的文件名包装示例代码播放(wav,mp3,ogg,flac):https://github.com/Qqwy/Haskell-sound-playing(该存储库包括所需的依赖项,只需克隆它并运行“stack build”)。 - Qqwy

0
一个新的选择是proteaaudio Haskell库。

https://hackage.haskell.org/package/proteaaudio

ProteaAudio是一个极简的立体声音频混音/播放库,适用于Linux、MacOS和Windows操作系统。支持的音频格式包括Wav、Ogg和原始线性PCM。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接