packages feed

hommage-ds-0.0.5: Sound/Hommage/DSPlayer/DSPlayer.hs

{- 2009 Daniel van den Eijkel <dvde@gmx.net> -}

module Sound.Hommage.DSPlayer.DSPlayer 
 ( DSPlayer (..)
 , startDSPlayer
 )
 where

import qualified Sound.Win32.DirectSound as DS

import Control.Monad
import Data.Maybe
import Foreign
import System.IO
---------------------------------------------------------------------------------------------------
-- | The DSPlayer object is given to the @startDSPlayer@ action. It contains all necessary 
--  information like sampling rate, buffer size, a stereo flag and the fillbuffer callback function.
data DSPlayer = DSPlayer { sampleRateDS :: Int  -- ^ 44100 for example
                         , bufferSizeDS :: Int  -- ^ 1024 for example
                         , isStereoDS :: Bool   
                         , fillBufferCallbackDS :: DS.FillBufferCallback Int16 -- ^ @Ptr a -> Word32 -> IO ()@  Length: bufferSizeDS multiplied with 2(stereo) or 1(mono)
                         }
---------------------------------------------------------------------------------------------------
-- | Opens the Audio Driver, creates a Sound Buffer and starts playback. 
--   Returns a stop-action. NOTE: Stopping does not work well: 
--   Restarting the sound after stopping does not work currently.
--
--   After calling this action, the fillbufferCallbackDS function will be called iteratively.
--   For stereo output, the values are interleaved.
startDSPlayer :: DSPlayer -> IO (IO ()) 
startDSPlayer dsplayer = do 
  ds <- openAudioDriver 
  sb <- openSoundBuffer ds (sampleRateDS dsplayer) (bufferSizeDS dsplayer) (isStereoDS dsplayer)
  stopaudio <- DS.playWithDoubleBuffering sb (fillBufferCallbackDS dsplayer)
  return stopaudio
---------------------------------------------------------------------------------------------------
openSoundBuffer :: DS.DirectSound -> Int -> Int -> Bool -> IO DS.SoundBuffer
openSoundBuffer ds sampleRate bufSize True  = openStereoSoundBuffer ds sampleRate bufSize
openSoundBuffer ds sampleRate bufSize False = openMonoSoundBuffer ds sampleRate bufSize

openStereoSoundBuffer :: DS.DirectSound -> Int -> Int -> IO DS.SoundBuffer
openStereoSoundBuffer ds sampleRate bufSize = do
  let waveFormatX = DS.makeWaveFormatX sampleRate 2 DS.SampleInt16
  sb <- DS.createSoundBuffer ds waveFormatX (2*bufSize) >>= \msb -> case msb of
    Left err -> error err 
    Right sb -> return sb
  return sb

openMonoSoundBuffer :: DS.DirectSound -> Int -> Int -> IO DS.SoundBuffer
openMonoSoundBuffer ds sampleRate bufSize = do
  let waveFormatX = DS.makeWaveFormatX sampleRate 1 DS.SampleInt16
  sb <- DS.createSoundBuffer ds waveFormatX (2*bufSize) >>= \msb -> case msb of     -- 1*bufsize ???
    Left err -> error err 
    Right sb -> return sb
  return sb
-------------------------------------------------------------------------------
openAudioDriver :: IO DS.DirectSound
openAudioDriver = do  
  drvlist <- DS.enumerateDrivers
  drv <- case drvlist of
    [] -> error "no audio device found"
    [drv] -> return drv
    _ -> selectAudioDriver drvlist (\d -> return (DS.drv_desc d))  
  hwnd <- DS.getConsoleHWND_hack
 -- putStrLn $ "hwnd = " ++ show hwnd
  ds <- DS.directSoundCreate (Just drv) hwnd >>= \mds -> case mds of
    Left err -> error err
    Right ds -> return ds
  return ds
  
selectAudioDriver :: [b] -> (b -> IO [Char]) -> IO b
selectAudioDriver srclist getName = do
  names <- mapM getName srclist
  forM_ (zip [1..] names) $ \(i,name) -> putStrLn $ show (i :: Int) ++ ": " ++ name
  let nsrc = length srclist
  src <- case srclist of
    []  -> error "no devices found"
    [x] -> return x
    _   -> do
      putStrLn "please select a device"
      l <- getLine
      let k = case maybeRead l of
        { Nothing -> nsrc
        ; Just m  -> if m<1 || m>nsrc then nsrc else m
        }
      putStrLn $ "device #" ++ show k ++ " selected."
      return $ srclist!!(k-1)
  return src      

maybeRead :: Read a => String -> Maybe a
maybeRead s = case reads s of 
  [(x,"")] -> Just x
  _        -> Nothing