LoginSignup
1
1

More than 5 years have passed since last update.

Network.VoiceText と Sound.ALUT で音声データを再生する

Posted at

以前作ったVoiceText Web APIをHaskellから呼び出すためのライブラリを使って、Haskellプログラムで音声データの再生を試してみた。

Sound.ALUTというのを使うとよさそう。

インストール

Sound.ALUT と 拙作 Network.VoiceText を cabal sandbox 環境にインストールする。

cabal sandbox 環境のセットアップ

> mkdir voicetext
> cd voicetext
> cabal sandvox init
> cabal init
> vi voicetext.cabal

voicetext.cabal はこんな感じに編集する。(一部抜粋)

voicetext.cabal
executable voicetext
  main-is:        Main.hs
  build-depends:  base >=4.7 && <4.8
                , network-voicetext >= 0.0
                , bytestring >=0.10
                , OpenAL >=1.6
                , ALUT >=2.3

依存ライブラリのインストール

> cabal install network-voicetext-0.0.0.1.tar.gz
> cabal install --only-dependencies
Resolving dependencies...
Notice: installing into a sandbox located at
/Users/zaneli/ws/haskell/voicetext/.cabal-sandbox
Downloading OpenAL-1.6.0.1...
Configuring OpenAL-1.6.0.1...
Building OpenAL-1.6.0.1...
Installed OpenAL-1.6.0.1
Downloading ALUT-2.3.0.2...
Configuring ALUT-2.3.0.2...
Failed to install ALUT-2.3.0.2
Last 10 lines of the build log ( /Users/zaneli/ws/haskell/voicetext/.cabal-sandbox/logs/ALUT-2.3.0.2.log ):
Configuring ALUT-2.3.0.2...
setup-Cabal-1.18.1.4-x86_64-osx-ghc-7.8.3: Missing dependency on a foreign
library:
* Missing C library: alut
This problem can usually be solved by installing the system package that
provides this library (you may need the "-dev" version). If the library is
already installed but in a non-standard location then you can use the flags
--extra-include-dirs= and --extra-lib-dirs= to specify where it is.
cabal: Error: some packages failed to install:
ALUT-2.3.0.2 failed during the configure step. The exception was:
ExitFailure 1

むむ、Cのライブラリが無いと言われて失敗した。
まず alut を入れる。Mac の場合、brew で入れられた。

> brew update
> brew install alut
==> Installing freealut
==> Downloading http://connect.creativelabs.com/openal/Downloads/ALUT/freealut-1.1.0.tar.gz
Already downloaded: /Library/Caches/Homebrew/freealut-1.1.0.tar.gz
==> Patching
patching file configure.ac
==> ./autogen.sh
==> ./configure --prefix=/usr/local/Cellar/freealut/1.1.0 --mandir=/usr/local/Cellar/freealut/
==> make install
?  /usr/local/Cellar/freealut/1.1.0: 11 files, 180K, built in 27 seconds

もう一度 cabal install --only-dependencies したが同じエラー。
--extra-lib-dirs オプションで freealutインストールディレクトリを指定したところ、成功した。

> cabal install --only-dependencies --extra-lib-dirs=/usr/local/Cellar/freealut/1.1.0/lib
Resolving dependencies...
Notice: installing into a sandbox located at
/Users/zaneli/ws/haskell/voicetext/.cabal-sandbox
Configuring ALUT-2.3.0.2...
Building ALUT-2.3.0.2...
Installed ALUT-2.3.0.2

実行

音声ファイルを再生

今回の目的からは少し外れているけど、一旦ファイルを作って、それを再生するにはこういう風にする。

Main.hs
import Network.VoiceText
import Sound.ALUT
import System.Environment (getArgs)

main = playVoice

playVoice :: IO ()
playVoice = withProgNameAndArgs runALUTUsingCurrentContext $ \_ _ ->
  do
    message <- getArgs
    let tmpFileName = "./tmp.wav"
    ttsToFile tmpFileName (basicAuth "basic_auth_username" "") (ttsParams (head message) Show)
    (Just device) <- openDevice Nothing
    context <- createContext device []
    currentContext $= context
    buffer <- createBuffer $ File tmpFileName
    [source] <- genObjectNames 1
    queueBuffers source [buffer]
    play [source]
    sleep 1
    closeDevice device
    return ()

cabal run "Hello, world" とか実行すると、「はろ〜わ〜るど」とおしゃべりしてくれる。
(毎回tmp.wavファイルが作成されるが…)

音声データを再生

Network.VoiceText には音声ファイルを作成する ttsToFile 関数とは別に、レスポンスデータを ByteString として返す tts 関数を用意しているので、できればファイルを作らず ByteString を直接再生したい。

Main.hs
import Data.ByteString.Internal (toForeignPtr)
import Data.ByteString.Lazy (toStrict)
import Foreign.ForeignPtr (castForeignPtr)
import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
import Network.VoiceText (basicAuth, tts, ttsParams, Speaker(Show))
import Sound.ALUT (
  ($=),
  closeDevice,
  createBuffer,
  createContext,
  currentContext,
  genObjectNames,
  openDevice,
  play,
  queueBuffers,
  runALUTUsingCurrentContext,
  sleep,
  SoundDataSource(FileImage),
  withProgNameAndArgs)
import System.Environment (getArgs)
import qualified Foreign.C.Types as CT
import qualified Sound.OpenAL.AL.Buffer as AL

main = do
  mr <- createVoice
  playVoice mr

createVoice :: IO (AL.MemoryRegion a)
createVoice = do
  [message] <- getArgs
  (Right bytes) <- tts (basicAuth "basic_auth_username" "") (ttsParams message Show)
  let (fptrw, _, ptrLength) = toForeignPtr $ toStrict bytes
  let fptri = castForeignPtr fptrw
  let size = CT.CInt $ fromIntegral ptrLength
  let ptr = unsafeForeignPtrToPtr fptri
  return $ AL.MemoryRegion ptr size

playVoice :: AL.MemoryRegion a -> IO ()
playVoice mr = withProgNameAndArgs runALUTUsingCurrentContext $ \_ _ -> do
  (Just device) <- openDevice Nothing
  context <- createContext device []
  currentContext $= context
  buffer <- createBuffer $ FileImage mr
  [source] <- genObjectNames 1
  queueBuffers source [buffer]
  play [source]
  sleep 1
  closeDevice device
  return ()

後述する参考URLを元に書いてみて、うまく動作することは確認できたが型の変換の辺りよく理解できていない…。

しかしこれで、Haskellとおしゃべりできたぞ!

参考URL

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1