alsa-mixer-0.3.0: Sound/ALSA/Mixer/Internal.chs
{-# LANGUAGE CPP, ForeignFunctionInterface #-}
module Sound.ALSA.Mixer.Internal
( Mixer()
, SimpleElement()
, SimpleElementId()
, Channel(..)
, allChannels
, elements
, withMixer
, isPlaybackMono
, isCaptureMono
, hasPlaybackChannel
, hasCaptureChannel
, hasCommonVolume
, hasPlaybackVolume
, hasPlaybackVolumeJoined
, hasCaptureVolume
, hasCaptureVolumeJoined
, hasCommonSwitch
, hasPlaybackSwitch
, hasPlaybackSwitchJoined
, hasCaptureSwitch
, hasCaptureSwitchJoined
, getPlaybackVolume
, getCaptureVolume
, getPlaybackDb
, getCaptureDb
, getPlaybackSwitch
, getCaptureSwitch
, setPlaybackVolume
, setCaptureVolume
, setPlaybackDb
, setCaptureDb
, setPlaybackVolumeAll
, setCaptureVolumeAll
, setPlaybackDbAll
, setCaptureDbAll
, setPlaybackSwitch
, setCaptureSwitch
, setPlaybackSwitchAll
, setCaptureSwitchAll
, getPlaybackVolumeRange
, getPlaybackDbRange
, getCaptureVolumeRange
, getCaptureDbRange
, setPlaybackVolumeRange
, setCaptureVolumeRange
, getName
, getIndex
) where
import Control.Monad (liftM, when)
import Control.Exception (bracket)
import Foreign
import Foreign.C.Error ( eNOENT )
import Foreign.C.String
import Foreign.C.Types
import Sound.ALSA.Exception ( checkResult_, throw )
import System.Posix.Process (getProcessID)
#include "alsa/asoundlib.h"
{#context lib = "asoundlib" #}
{#pointer *snd_mixer_t as Mixer newtype#}
{#pointer *snd_mixer_elem_t as Element#}
{#pointer *snd_mixer_selem_id_t as SimpleElementId foreign#}
type SimpleElement = (Mixer, Element)
{#enum snd_mixer_selem_channel_id_t as Channel
{ SND_MIXER_SCHN_UNKNOWN as Unknown
, SND_MIXER_SCHN_FRONT_LEFT as FrontLeft
, SND_MIXER_SCHN_FRONT_RIGHT as FrontRight
, SND_MIXER_SCHN_REAR_LEFT as RearLeft
, SND_MIXER_SCHN_REAR_RIGHT as RearRight
, SND_MIXER_SCHN_FRONT_CENTER as FrontCenter
, SND_MIXER_SCHN_WOOFER as Woofer
, SND_MIXER_SCHN_SIDE_LEFT as SideLeft
, SND_MIXER_SCHN_SIDE_RIGHT as SideRight
, SND_MIXER_SCHN_REAR_CENTER as RearCenter
, SND_MIXER_SCHN_LAST as Last
} deriving (Eq, Read, Show) #}
allChannels :: [Channel]
allChannels = map toEnum $ enumFromTo (fromEnum FrontLeft) (fromEnum RearCenter)
-----------------------------------------------------------------------
-- open
-- --------------------------------------------------------------------
foreign import ccall safe "alsa/asoundlib.h snd_mixer_open"
open_ :: Ptr (Ptr Mixer) -> CInt -> IO CInt
open :: IO Mixer
open = withPtr $ \ppm ->
do open_ ppm (fromIntegral 0) >>= checkResult_ "snd_mixer_open"
liftM Mixer $ peek ppm
withPtr :: (Ptr (Ptr a) -> IO a) -> IO a
withPtr = bracket malloc free
foreign import ccall "alsa/asoundlib.h snd_mixer_close"
freeMixer :: Ptr Mixer -> IO ()
-----------------------------------------------------------------------
-- attach
-- --------------------------------------------------------------------
{#fun snd_mixer_attach as attach
{ id `Mixer', `String' } -> `Int' checkAttach*- #}
checkAttach = checkResult_ "snd_mixer_attach"
-----------------------------------------------------------------------
-- load
-- --------------------------------------------------------------------
{#fun snd_mixer_load as ^
{ id `Mixer' } -> `Int' checkSndMixerLoad*- #}
checkSndMixerLoad = checkResult_ "snd_mixer_load"
{#fun snd_mixer_selem_register as ^
{ id `Mixer'
, id `Ptr ()'
, id `Ptr (Ptr ())' } -> `Int' checkSndMixerSelemRegister*- #}
checkSndMixerSelemRegister = checkResult_ "snd_mixer_selem_register"
load :: Mixer -> IO ()
load fmix = do
sndMixerSelemRegister fmix nullPtr nullPtr
sndMixerLoad fmix
-----------------------------------------------------------------------
-- getId
-- --------------------------------------------------------------------
{#fun snd_mixer_selem_id_malloc as ^
{ alloca- `SimpleElementId' peekSimpleElementId* } -> `()' #}
{#fun snd_mixer_selem_get_id as ^
{ id `Element', withForeignPtr* `SimpleElementId' } -> `()' #}
peekSimpleElementId pid = peek pid >>= newForeignPtr snd_mixer_selem_id_free
foreign import ccall "alsa/asoundlib.h &snd_mixer_selem_id_free"
snd_mixer_selem_id_free :: FunPtr (Ptr () -> IO ())
getId :: Element -> IO SimpleElementId
getId e = do
newSid <- sndMixerSelemIdMalloc
sndMixerSelemGetId e newSid
return newSid
-----------------------------------------------------------------------
-- elements
-- --------------------------------------------------------------------
{#fun snd_mixer_first_elem as ^
{ id `Mixer' } -> `Element' id #}
{#fun snd_mixer_last_elem as ^
{ id `Mixer' } -> `Element' id #}
{#fun snd_mixer_elem_next as ^
{ id `Element' } -> `Element' id #}
elements :: Mixer -> IO [(SimpleElementId, SimpleElement)]
elements fMix = do
pFirst <- sndMixerFirstElem fMix
pLast <- sndMixerLastElem fMix
es <- elements' pFirst [] pLast
mapM (simpleElement fMix) es
where elements' pThis xs pLast | pThis == pLast = return $ pThis : xs
| otherwise = do
pNext <- sndMixerElemNext pThis
elements' pNext (pThis : xs) pLast
-----------------------------------------------------------------------
-- simpleElement
-- --------------------------------------------------------------------
{#fun snd_mixer_find_selem as ^
{ id `Mixer'
, withForeignPtr* `SimpleElementId' } -> `Element' id #}
simpleElement :: Mixer -> Element -> IO (SimpleElementId, SimpleElement)
simpleElement fMix pElem = do
fId <- getId pElem
pSElem <- sndMixerFindSelem fMix fId
if pSElem == nullPtr
then throw "snd_mixer_find_selem" eNOENT
else return (fId, (fMix, pSElem))
-----------------------------------------------------------------------
-- getName
-- --------------------------------------------------------------------
{#fun snd_mixer_selem_id_get_name as getName
{ withForeignPtr* `SimpleElementId' } -> `String' #}
-----------------------------------------------------------------------
-- getIndex
-- --------------------------------------------------------------------
{#fun snd_mixer_selem_id_get_index as getIndex
{ withForeignPtr* `SimpleElementId' } -> `CUInt' #}
-----------------------------------------------------------------------
-- getMixerByName
-- --------------------------------------------------------------------
-- | Perform an 'IO' action with the named mixer. An exception of type
-- 'Sound.ALSA.Exception.T' will be thrown if the named mixer cannot be
-- found. A mixer named \"default\" should always exist.
withMixer :: String -> (Mixer -> IO a) -> IO a
withMixer name f = bracket (do m <- open
attach m name
load m
pid <- getProcessID
return (pid, m))
(\(creatorPID, Mixer m) ->
do myPID <- getProcessID
when (myPID == creatorPID) $ freeMixer m)
(f . snd)
-----------------------------------------------------------------------
-- utilities
-- --------------------------------------------------------------------
cToBool = toBool
cFromBool = fromBool
withSimpleElement :: SimpleElement -> (Element -> IO a) -> IO a
withSimpleElement (m, s) f = f s
channelToC = toEnum . fromEnum
negOne f = f $! negate 1
-----------------------------------------------------------------------
-- has
-- --------------------------------------------------------------------
{#fun snd_mixer_selem_is_playback_mono as isPlaybackMono
{ withSimpleElement* `SimpleElement' } -> `Bool' #}
{#fun snd_mixer_selem_is_capture_mono as isCaptureMono
{ withSimpleElement* `SimpleElement' } -> `Bool' #}
{#fun snd_mixer_selem_has_common_volume as hasCommonVolume
{ withSimpleElement* `SimpleElement' } -> `Bool' #}
{#fun snd_mixer_selem_has_playback_volume as hasPlaybackVolume
{ withSimpleElement* `SimpleElement' } -> `Bool' #}
{#fun snd_mixer_selem_has_playback_volume_joined as hasPlaybackVolumeJoined
{ withSimpleElement* `SimpleElement' } -> `Bool' #}
{#fun snd_mixer_selem_has_capture_volume as hasCaptureVolume
{ withSimpleElement* `SimpleElement' } -> `Bool' #}
{#fun snd_mixer_selem_has_capture_volume_joined as hasCaptureVolumeJoined
{ withSimpleElement* `SimpleElement' } -> `Bool' #}
{#fun snd_mixer_selem_has_common_switch as hasCommonSwitch
{ withSimpleElement* `SimpleElement' } -> `Bool' #}
{#fun snd_mixer_selem_has_playback_switch as hasPlaybackSwitch
{ withSimpleElement* `SimpleElement' } -> `Bool' #}
{#fun snd_mixer_selem_has_playback_switch_joined as hasPlaybackSwitchJoined
{ withSimpleElement* `SimpleElement' } -> `Bool' #}
{#fun snd_mixer_selem_has_capture_switch as hasCaptureSwitch
{ withSimpleElement* `SimpleElement' } -> `Bool' #}
{#fun snd_mixer_selem_has_capture_switch_joined as hasCaptureSwitchJoined
{ withSimpleElement* `SimpleElement' } -> `Bool' #}
{#fun snd_mixer_selem_has_playback_channel as hasPlaybackChannel
{ withSimpleElement* `SimpleElement'
, channelToC `Channel' } -> `Bool' #}
{#fun snd_mixer_selem_has_capture_channel as hasCaptureChannel
{ withSimpleElement* `SimpleElement'
, channelToC `Channel' } -> `Bool' #}
-----------------------------------------------------------------------
-- get
-- --------------------------------------------------------------------
{#fun snd_mixer_selem_get_playback_volume as getPlaybackVolume
{ withSimpleElement* `SimpleElement'
, channelToC `Channel'
, alloca- `CLong' peek* } -> `Int' checkGetPlaybackVolume*- #}
checkGetPlaybackVolume = checkResult_ "snd_mixer_selem_get_playback_volume"
{#fun snd_mixer_selem_get_capture_volume as getCaptureVolume
{ withSimpleElement* `SimpleElement'
, channelToC `Channel'
, alloca- `CLong' peek* } -> `Int' checkGetCaptureVolume*- #}
checkGetCaptureVolume = checkResult_ "snd_mixer_selem_get_capture_volume"
{#fun snd_mixer_selem_get_playback_dB as getPlaybackDb
{ withSimpleElement* `SimpleElement'
, channelToC `Channel'
, alloca- `CLong' peek* } -> `Int' checkPlaybackDb*- #}
checkPlaybackDb = checkResult_ "snd_mixer_selem_get_playback_dB"
{#fun snd_mixer_selem_get_capture_dB as getCaptureDb
{ withSimpleElement* `SimpleElement'
, channelToC `Channel'
, alloca- `CLong' peek* } -> `Int' checkCaptureDb*- #}
checkCaptureDb = checkResult_ "snd_mixer_selem_get_capture_dB"
peekBool = (>>= return . cToBool) . peek
{#fun snd_mixer_selem_get_playback_switch as getPlaybackSwitch
{ withSimpleElement* `SimpleElement'
, channelToC `Channel'
, alloca- `Bool' peekBool* } -> `Int' checkPlaybackSwitch*- #}
checkPlaybackSwitch = checkResult_ "snd_mixer_selem_get_playback_switch"
{#fun snd_mixer_selem_get_capture_switch as getCaptureSwitch
{ withSimpleElement* `SimpleElement'
, channelToC `Channel'
, alloca- `Bool' peekBool* } -> `Int' checkCaptureSwitch*- #}
checkCaptureSwitch = checkResult_ "snd_mixer_selem_get_capture_switch"
{#fun snd_mixer_selem_get_playback_volume_range as getPlaybackVolumeRange
{ withSimpleElement* `SimpleElement'
, alloca- `CLong' peek*
, alloca- `CLong' peek* } -> `Int' checkGetPlaybackVolumeRange*- #}
checkGetPlaybackVolumeRange = checkResult_ "snd_mixer_selem_get_playback_volume_range"
{#fun snd_mixer_selem_get_capture_volume_range as getCaptureVolumeRange
{ withSimpleElement* `SimpleElement'
, alloca- `CLong' peek*
, alloca- `CLong' peek* } -> `Int' checkGetCaptureVolumeRange*- #}
checkGetCaptureVolumeRange = checkResult_ "snd_mixer_selem_get_capture_volume_range"
{#fun snd_mixer_selem_get_playback_dB_range as getPlaybackDbRange
{ withSimpleElement* `SimpleElement'
, alloca- `CLong' peek*
, alloca- `CLong' peek* } -> `Int' checkGetPlaybackDbRange*- #}
checkGetPlaybackDbRange = checkResult_ "snd_mixer_selem_get_playback_dB_range"
{#fun snd_mixer_selem_get_capture_dB_range as getCaptureDbRange
{ withSimpleElement* `SimpleElement'
, alloca- `CLong' peek*
, alloca- `CLong' peek* } -> `Int' checkGetCaptureDbRange*- #}
checkGetCaptureDbRange = checkResult_ "snd_mixer_selem_get_capture_dB_range"
-----------------------------------------------------------------------
-- set
-- --------------------------------------------------------------------
{#fun snd_mixer_selem_set_playback_volume as setPlaybackVolume
{ withSimpleElement* `SimpleElement'
, channelToC `Channel'
, `CLong' } -> `Int' checkSetPlaybackVolume*- #}
checkSetPlaybackVolume = checkResult_ "snd_mixer_selem_set_playback_volume"
{#fun snd_mixer_selem_set_capture_volume as setCaptureVolume
{ withSimpleElement* `SimpleElement'
, channelToC `Channel'
, `CLong' } -> `Int' checkSetCaptureVolume*- #}
checkSetCaptureVolume = checkResult_ "snd_mixer_selem_set_capture_volume"
{#fun snd_mixer_selem_set_playback_dB as setPlaybackDb
{ withSimpleElement* `SimpleElement'
, channelToC `Channel'
, `CLong'
, negOne- `Int' } -> `Int' checkSetPlaybackDb*- #}
checkSetPlaybackDb = checkResult_ "snd_mixer_selem_set_playback_dB"
{#fun snd_mixer_selem_set_capture_dB as setCaptureDb
{ withSimpleElement* `SimpleElement'
, channelToC `Channel'
, `CLong'
, negOne- `Int' } -> `Int' checkSetCaptureDb*- #}
checkSetCaptureDb = checkResult_ "snd_mixer_selem_set_capture_dB"
{#fun snd_mixer_selem_set_playback_volume_all as setPlaybackVolumeAll
{ withSimpleElement* `SimpleElement'
, `CLong' } -> `Int' checkSetPlaybackVolumeAll*- #}
checkSetPlaybackVolumeAll = checkResult_ "snd_mixer_selem_set_playback_volume_all"
{#fun snd_mixer_selem_set_capture_volume_all as setCaptureVolumeAll
{ withSimpleElement* `SimpleElement'
, `CLong' } -> `Int' checkSetCaptureVolumeAll*- #}
checkSetCaptureVolumeAll = checkResult_ "snd_mixer_selem_set_capture_volume_all"
{#fun snd_mixer_selem_set_playback_dB_all as setPlaybackDbAll
{ withSimpleElement* `SimpleElement'
, `CLong'
, negOne- `Int' } -> `Int' checkSetPlaybackDbAll*- #}
checkSetPlaybackDbAll = checkResult_ "snd_mixer_selem_set_playback_dB_all"
{#fun snd_mixer_selem_set_capture_dB_all as setCaptureDbAll
{ withSimpleElement* `SimpleElement'
, `CLong'
, negOne- `Int' } -> `Int' checkSetCaptureDbAll*- #}
checkSetCaptureDbAll = checkResult_ "snd_mixer_selem_set_capture_dB_all"
{#fun snd_mixer_selem_set_playback_switch as setPlaybackSwitch
{ withSimpleElement* `SimpleElement'
, channelToC `Channel'
, `Bool' } -> `Int' checkSetPlaybackSwitch*- #}
checkSetPlaybackSwitch = checkResult_ "snd_mixer_selem_set_playback_switch"
{#fun snd_mixer_selem_set_capture_switch as setCaptureSwitch
{ withSimpleElement* `SimpleElement'
, channelToC `Channel'
, `Bool' } -> `Int' checkSetCaptureSwitch*- #}
checkSetCaptureSwitch = checkResult_ "snd_mixer_selem_set_capture_switch"
{#fun snd_mixer_selem_set_playback_switch_all as setPlaybackSwitchAll
{ withSimpleElement* `SimpleElement'
, `Bool' } -> `Int' checkSetPlaybackSwitchAll*- #}
checkSetPlaybackSwitchAll = checkResult_ "snd_mixer_selem_set_playback_switch_all"
{#fun snd_mixer_selem_set_capture_switch_all as setCaptureSwitchAll
{ withSimpleElement* `SimpleElement'
, `Bool' } -> `Int' checkSetCaptureSwitchAll*- #}
checkSetCaptureSwitchAll = checkResult_ "snd_mixer_selem_set_capture_switch_all"
{#fun snd_mixer_selem_set_playback_volume_range as setPlaybackVolumeRange'
{ withSimpleElement* `SimpleElement'
, `CLong'
, `CLong' } -> `Int' checkSetPlaybackVolumeRange*- #}
checkSetPlaybackVolumeRange = checkResult_ "snd_mixer_selem_set_playback_volume_range"
{#fun snd_mixer_selem_set_capture_volume_range as setCaptureVolumeRange'
{ withSimpleElement* `SimpleElement'
, `CLong'
, `CLong' } -> `Int' checkSetCaptureVolumeRange*- #}
checkSetCaptureVolumeRange = checkResult_ "snd_mixer_selem_set_capture_volume_range"
setPlaybackVolumeRange m = uncurry (setPlaybackVolumeRange' m)
setCaptureVolumeRange m = uncurry (setCaptureVolumeRange' m)