{-# LANGUAGE LambdaCase #-} {-# LANGUAGE RankNTypes #-} -- | -- Copyright: (c) 2021 Tristan de Cacqueray -- SPDX-License-Identifier: Apache-2.0 -- Maintainer: Tristan de Cacqueray -- -- Pipes for simple-pulse audio module Pipes.PulseSimple ( readPulse, writePulse, ) where import Control.Monad.IO.Class (MonadIO, liftIO) import Data.ByteString (ByteString) import Data.Maybe (fromMaybe) import Pipes (Consumer', Producer', await, yield) import Pipes.Safe (MonadSafe, bracket) import Sound.Pulse.Simple ( BufferAttr (..), Direction (..), Endian (..), SampleFormat (..), SampleSpec (..), Simple, simpleFree, simpleNew, simpleReadRaw, simpleWriteRaw, ) defaultSampleSpec :: SampleSpec defaultSampleSpec = SampleSpec (S16 LittleEndian) 44100 1 -- | Return the bits count of a single sample sampleSize :: SampleFormat -> Int sampleSize = \case U8 _ -> 1 S16 _ -> 2 S24 _ -> 3 S2432 _ -> 4 S32 _ -> 4 F32 _ -> 4 -- | Return the bits count of an one second buffer bufferSize :: SampleSpec -> Int bufferSize (SampleSpec fmt sampling chan) = sampleSize fmt * sampling * chan -- | Compute the sample spec and buffer size for a given fps specSize :: Maybe SampleSpec -> Int -> (SampleSpec, Int) specSize specm fps = (spec, size) where spec = fromMaybe defaultSampleSpec specm size = bufferSize spec `div` fps newClient :: MonadIO m => String -> Direction -> SampleSpec -> Int -> m Simple newClient name dir spec size = liftIO create where create = simpleNew Nothing name dir Nothing "pulse-pipe" spec Nothing bufAttr bufAttr = Just (BufferAttr (Just size) (Just size) (Just size) (Just size) (Just size)) freeClient :: MonadIO m => Simple -> m () freeClient = liftIO . simpleFree -- | Create an audio pipe producer that yields `fps` bytestring buffer per second readPulse :: MonadSafe m => -- | The client name String -> -- | The sample spec, default to mono 44100 Hz 16 bit signed integer Maybe SampleSpec -> -- | Frames per second Int -> -- | The pipe producing bytestring Producer' ByteString m () readPulse name specm fps = bracket (newClient name Record spec size) freeClient producePulse where (spec, size) = specSize specm fps producePulse s = do buf <- liftIO (simpleReadRaw s size) yield buf producePulse s {-# INLINEABLE readPulse #-} -- | Create an audio pipe consumer that awaits `fps` bytestring buffer per second writePulse :: MonadSafe m => -- | The client name String -> -- | The sample spec, default to mono 44100 Hz 16 bit signed integer Maybe SampleSpec -> -- | Frames per second Int -> -- | The pipe consuming bytestring Consumer' ByteString m () writePulse name specm fps = bracket (newClient name Play spec size) freeClient consumePulse where (spec, size) = specSize specm fps consumePulse s = do buf <- await liftIO (simpleWriteRaw s buf) consumePulse s {-# INLINEABLE writePulse #-}