-------------------------------------------------------------------------------- -- | -- Module : Sound.OpenAL.ALC.Device -- Copyright : (c) Sven Panne 2003-2016 -- License : BSD3 -- -- Maintainer : Sven Panne -- Stability : stable -- Portability : portable -- -- This module corresponds to section 6.1 (Managing Devices) of the OpenAL -- Specification and Reference (version 1.1). -- -- ALC introduces the notion of a device. A device can be, depending on the -- implementation, a hardware device, or a daemon\/OS service\/actual -- server. This mechanism also permits different drivers (and hardware) to -- coexist within the same system, as well as allowing several applications to -- share system resources for audio, including a single hardware output -- device. The details are left to the implementation, which has to map the -- available backends to unique device specifiers. -- -------------------------------------------------------------------------------- module Sound.OpenAL.ALC.Device ( Device, openDevice, closeDevice, defaultDeviceSpecifier, deviceSpecifier, allDeviceSpecifiers ) where import Control.Monad.IO.Class ( MonadIO(..) ) import Data.StateVar ( get, GettableStateVar, makeGettableStateVar ) import Foreign.Ptr ( Ptr, nullPtr ) import Foreign.Marshal.Utils ( maybePeek ) import Sound.OpenAL.ALC.BasicTypes import Sound.OpenAL.ALC.QueryUtils import Sound.OpenAL.ALC.String import Sound.OpenAL.Config -------------------------------------------------------------------------------- -- | 'openDevice' allows the application (i.e. the client program) to connect to -- a device (i.e. the server). If the function returns 'Nothing', then no sound -- driver\/device has been found. The argument to 'openDevice' specifies a -- certain device or device configuration. If it is 'Nothing', the -- implementation will provide an implementation specific default, see -- 'defaultDeviceSpecifier'. Otherwise it is 'Just' an implementation-dependent -- string. You can use 'allDeviceSpecifiers' to get a list of the known OpenAL -- devices. -- -- /Notes for Windows:/ There are 3 possible device strings, each having a -- deprecated equivalent for legacy applications only: -- -- * @\"Generic Hardware\"@ (legacy string: @\"DirectSound3D\"@) -- -- * @\"Generic Software\"@ (legacy string: @\"DirectSound\"@) -- -- * @\"Generic Software Fallback\"@ (legacy string: @\"MMSYSTEM\"@) -- -- /Notes for Linux\/\*nix:/ If an @.openalrc@ file is present in the user\'s -- home directory, it is loaded first, otherwise @\/etc\/openalrc@ is tried. -- The bindings (if any) of @devices@, @direction@, @sampling-rate@, and -- @speaker-num@ (see below) after loading one of these files take precedence -- over any bindings done via the argument to 'openDevice'. -- -- The syntax of these files is lisp-based and a sequence of expressions, where -- an expression is one the following: -- -- * A boolean value of the form @\#f@ or @\#t@, which evaluate to /false/ and -- /true/, respectively. -- -- * An non-negative integer value, i.e. a sequence of decimal digits, -- evaluating to itself. -- -- * A (signed) floating point value, as recoginzed by C\'s @atof()@, evaluating -- to itself. -- -- * A pointer value of the form @\#p/num/@, where /num/ can be an octal, -- decimal or hexadecimal value, as recoginzed by C\`s @strtol()@, evaluating -- to itself. This kind of expression is currently unused. -- -- * A string, i.e. a sequence of printable\/whitespace characters between -- double quotes, evaluating to itself. -- -- * A symbol, i.e. a sequence of almost all characters which don\'t form a -- simple expression like the ones mentioned below, e.g. @foo@, @bar1@, @3baz@, -- ... The symbol evaluates to the value currently bound to it. -- -- * A function application of the form @(/symbol/ /expression/...)@. The -- function bound to the symbol is applied to the evaluated arguments. -- -- * A quotation of the form @(quote /expression/)@ or @\'/expression/@, -- evaluating to the unevaluated /expression/ itself. -- -- * A definition of the form @(define /symbol/ /expression/)@, binding /symbol/ -- to the value of /expression/. The whole expression evaluates to the value of -- /expression/, too. -- -- * A conjunction of boolean expressions of the form @(and /expression/...)@. -- Each /expression/ is evaluated in turn, and if one of them evaluates to -- /false/, the value of the whole expression is /false/. Otherwise the value is -- /true/. -- -- * An extension loading mechanism of the form @(load-extension -- /libraryName/)@, where /libraryName/ has to evaluate to a string. This tries -- to load the dynamic library with up to 3 special entry points: -- @alExtension_03282000@ (pointing to a mandatory NULL-terminated sequence of -- pairs of pointers to names and extension functions), @alExtInit_03282000@ (an -- optional initialization function), and @alExtFini_03282000@ (an optional -- cleanup function). If the extension could be loaded successfully, the whole -- expression evaluates to /true/, otherwise to /false/. -- -- Some symbols have a special meaning for OpenAL: -- -- [@devices@] Has the form @(/devspec/...)@, where /devspec/ is either a -- symbol\/string specifying a device or @(/device/ /device-param/...)@, -- specifying a device with additional parameters. These optional device -- parameters are stored in a variable @device-params@, but are currently -- unused. The first device which can successfully opened is used. -- -- [@direction@] Type string or symbol: @\"read\"@ specifies that the device -- should be an input device, everything else means output device (default). -- -- [@sampling-rate@] Type integer or float: Specifies the internal mixing -- frequency, default is 44.1kHz. -- -- [@speaker-num@] Type integer or float: Specifies the number of speakers, -- which can be 1, 2 (default), or 4. -- -- [@alsa-device@] Type string, @alsa@ backend only: Specifies both -- @alsa-out-device@ and @alsa-in-device@, default @\"plughw:0,0\"@. -- -- [@alsa-out-device@] Type string, @alsa@ backend only: Specifies the ALSA -- output device, defaults to the value of @alsa-device@. -- -- [@alsa-in-device@] Type string, @alsa@ backend only: Specifies the ALSA -- input device, defaults to the value of @alsa-device@. -- -- [@native-in-device@] Type string, @native@ backend on IRIX only. -- -- [@native-out-device@] Type string, @native@ backend on IRIX only. -- -- [@native-rear-out-device@] Type string, @native@ backend on IRIX only. -- -- [@native-use-select@] Type boolean, @native@ backend on Linux only: If @\#t@, -- wait up to 0.8sec for the device to become ready for writing. If @\#f@, just -- try to write and hope it won\'t hang forever. The latter might be necessary -- for some drivers which don\'t implement @select()@ , like some Aureal -- drivers. -- -- [@lin-dsp-path@] Type string, @native@ backend on Linux only: Path to DSP -- device for writing, tried before @\/dev\/sound\/dsp@ and @\/dev\/sound@ if -- set. -- -- [@lin-dsp-read-path@] Type string, @native@ backend on Linux only: Path to -- DSP device for reading, tried before @\/dev\/sound\/dsp@ and @\/dev\/sound@ -- if set. Defaults to the value of @lin-dsp-path@. -- -- [@native-backend-debug@] Type boolean, @native@ backend on Darwin only: If -- set to @\#f@, be a bit verbose on stderr about what\'s going on in the -- backend. -- -- [@source-rolloff-factor@] Type integer or float: Value of the initial rolloff -- factor for sources, default is @1.0@. -- -- [@listener-position@] List of 3 integers or floats: Value of the initial -- listener position, default is @(0 0 0)@. -- -- [@listener-velocity@] List of 3 integers or floats: Value of the initial -- listener velocity, default is @(0 0 0)@. -- -- [@listener-orientation@] List of 6 integers or floats: Value of the initial -- listener orientation (at\/up), default is @(0 0 -1 0 1 0)@. -- -- The string given to 'openDevice' has to be of the form @\'((/symbol/ -- /expression/) ...)@, which means basically a sequence of @define@ -- expressions. Example: -- -- @ -- \"\'((sampling-rate 8000) (native-backend-debug \#f))\" -- @ -- -- /Note:/ The information above has been reverse-engineered from the OpenAL SI -- and could be inaccurate. Any corrections and\/or additions are highly -- welcome. openDevice :: MonadIO m => Maybe String -> m (Maybe Device) openDevice maybeDeviceSpec = liftIO $ fmap unmarshalDevice $ maybe (alcOpenDevice nullPtr) -- use preferred device (flip withALCString alcOpenDevice) maybeDeviceSpec foreign import ccall unsafe "alcOpenDevice" alcOpenDevice :: Ptr ALCchar -> IO ALCdevice -------------------------------------------------------------------------------- -- | Contains 'Just' the specifier string for the default device or 'Nothing' if -- there is no sound support at all. defaultDeviceSpecifier :: GettableStateVar (Maybe String) defaultDeviceSpecifier = makeGettableStateVar $ getStringRaw Nothing DefaultDeviceSpecifier >>= maybePeek peekALCString -------------------------------------------------------------------------------- -- | Contains the specifier string for the given device. deviceSpecifier :: Device -> GettableStateVar String deviceSpecifier device = makeGettableStateVar $ getString (Just device) DeviceSpecifier -------------------------------------------------------------------------------- -- | Contains a list of specifiers for all available devices. allDeviceSpecifiers :: GettableStateVar [String] allDeviceSpecifiers = makeGettableStateVar $ do enumExtPresent <- get (alcIsExtensionPresent Nothing "ALC_ENUMERATION_EXT") if enumExtPresent then getStringRaw Nothing DeviceSpecifier >>= peekALCStrings else get defaultDeviceSpecifier >>= (return . maybe [] (\s -> [s]))