-------------------------------------------------------------------------------- -- | -- Module : Sound.OpenAL.AL.Buffer -- Copyright : (c) Sven Panne 2003-2005 -- License : BSD-style (see the file libraries/OpenAL/LICENSE) -- -- Maintainer : sven.panne@aedion.de -- Stability : provisional -- Portability : portable -- -- This module corresponds to chapter 6 (Buffers) of the OpenAL Specification -- and Reference (version 1.1). -- -- A buffer encapsulates OpenAL state related to storing sample data. The -- application can request and release buffer objects, and fill them with -- data. Data can be supplied compressed and encoded as long as the format is -- supported. Buffers can, internally, contain waveform data as uncompressed or -- compressed samples. -- -- Unlike source (see "Sound.OpenAL.AL.Source") and listener (see -- "Sound.OpenAL.AL.Listener") objects, buffer objects can be shared among AL -- contexts. Buffers are referenced by sources. A single buffer can be referred -- to by multiple sources. This separation allows drivers and hardware to -- optimize storage and processing where applicable. -- -- The simplest supported format for buffer data is PCM. PCM data is assumed to -- use the processor\'s native byte order. Other formats use the byte order -- native to that format. -- -- At this time, buffer states are defined for purposes of discussion. The -- states described in this section are not exposed through the API (can not be -- queried, or be set directly), and the state description used in the -- implementation might differ from this. -- -- A buffer is considered to be in one of the following states, with respect to -- all sources: -- -- [/unused/] The buffer is not included in any queue for any source. In -- particular, the buffer is neither pending nor current for any source. The -- buffer name can be deleted at this time. -- -- [/processed/] The buffer is listed in the queue of at least one source, but -- is neither pending nor current for any source. The buffer can be deleted as -- soon as it has been unqueued for all sources it is queued with. -- -- [/pending/] There is at least one source for which the buffer has been queued, -- for which the buffer data has not yet been dereferenced. The buffer can only -- be unqueued for those sources that have dereferenced the data in the buffer -- in its entirety, and cannot be deleted or changed. -- -- The buffer state is dependent on the state of all sources that is has been -- queued for. A single queue occurrence of a buffer propagates the buffer state -- (over all sources) from /unused/ to /processed/ or higher. Sources that are -- in the 'Stopped' or 'Initial' states still have queue entries that cause -- buffers to be /processed/. -- -- A single queue entry with a single source for which the buffer is not yet -- /processed/ propagates the buffer\'s queuing state to /pending/. -- -- Buffers that are /processed/ for a given source can be unqueued from that -- source\'s queue. Buffers that have been unqueued from all sources are -- /unused/. Buffers that are /unused/ can be deleted, or changed by writing -- 'bufferData'. -- -------------------------------------------------------------------------------- module Sound.OpenAL.AL.Buffer ( Buffer, MemoryRegion(..), Format(..), BufferData(..), bufferData ) where import Foreign.Marshal.Alloc ( alloca ) import Foreign.Ptr ( Ptr, nullPtr ) import Graphics.Rendering.OpenGL.GL.StateVar ( StateVar, makeStateVar ) import Sound.OpenAL.AL.BufferInternal ( Buffer ) import Sound.OpenAL.AL.BasicTypes ( ALint, ALsizei, ALenum ) import Sound.OpenAL.AL.Format ( Format(..), marshalFormat ) import Sound.OpenAL.AL.PeekPoke ( peek1 ) import Sound.OpenAL.ALC.Context ( Frequency ) import Sound.OpenAL.Constants ( al_FREQUENCY, al_SIZE, al_BITS, al_CHANNELS ) #ifdef __HADDOCK__ import Sound.OpenAL.AL.SourceState ( SourceState(..) ) import Sound.OpenAL.AL.Errors ( ALErrorCategory(..) ) #endif -------------------------------------------------------------------------------- data MemoryRegion a = MemoryRegion (Ptr a) ALsizei #ifdef __HADDOCK__ -- Help Haddock a bit, because it doesn't do any instance inference. instance Eq (MemoryRegion a) instance Ord (MemoryRegion a) instance Show (MemoryRegion a) #else deriving ( Eq, Ord, Show ) #endif data BufferData a = BufferData (MemoryRegion a) Format Frequency #ifdef __HADDOCK__ -- Help Haddock a bit, because it doesn't do any instance inference. instance Eq (BufferData a) instance Ord (BufferData a) instance Show (BufferData a) #else deriving ( Eq, Ord, Show ) #endif -------------------------------------------------------------------------------- -- | A special case of buffer state is the actual sound sample data stored in -- association with the buffer. Applications can specify sample data using -- 'bufferData'. -- -- The data specified is copied to an internal software, or if possible, -- hardware buffer. The implementation is free to apply decompression, -- conversion, resampling, and filtering as needed. The internal format of the -- buffer is not exposed to the application, and not accessible. -- -- Buffers containing audio data with more than one channel will be played -- without 3D spatialization features, these formats are normally used for -- background music. Applications should always check for an error condition -- after attempting to specify buffer data in case an implementation has to -- generate an 'ALOutOfMemory' or a conversion related 'ALInvalidValue' error. -- The application is free to reuse the memory specified by the data pointer -- once 'bufferData' is set. The implementation has to dereference, e.g. copy, -- the data while accessing 'bufferData' execution. bufferData :: Buffer -> StateVar (BufferData a) bufferData buffer = makeStateVar (getBufferData buffer) (setBufferData buffer) getBufferData :: Buffer -> IO (BufferData a) getBufferData buffer = do channels <- getBufferi buffer Channels bits <- getBufferi buffer Bits raw <- return nullPtr -- ToDo: AL_DATA query missing!!! size <- getBufferi buffer Size frequency <- getBufferi buffer Frequency return $ BufferData (MemoryRegion raw size) (makeFormat channels bits) frequency setBufferData :: Buffer -> BufferData a -> IO () setBufferData buffer (BufferData (MemoryRegion raw size) format frequency) = alBufferData buffer (marshalFormat format) raw size (round frequency) foreign import CALLCONV unsafe "alBufferData" alBufferData :: Buffer -> ALenum -> Ptr a -> ALsizei -> ALsizei -> IO () -------------------------------------------------------------------------------- -- ToDo: What about IMAADPCMMono16, IMAADPCMStereo16, Vorbis...? makeFormat :: ALint -> ALint -> Format makeFormat 1 8 = Mono8 makeFormat 2 8 = Stereo8 makeFormat 1 16 = Mono16 makeFormat 2 16 = Stereo16 makeFormat channels bits = error ("makeFormat: illegal values " ++ show (channels, bits)) -------------------------------------------------------------------------------- data BufferQuery = Frequency | Size | Bits | Channels marshalBufferQuery :: BufferQuery -> ALenum marshalBufferQuery x = case x of Frequency -> al_FREQUENCY Size -> al_SIZE Bits -> al_BITS Channels -> al_CHANNELS -------------------------------------------------------------------------------- getBufferi :: Num a => Buffer -> BufferQuery -> IO a getBufferi buffer query = alloca $ \buf -> do alGetBufferi buffer (marshalBufferQuery query) buf peek1 fromIntegral buf foreign import CALLCONV unsafe "alGetBufferi" alGetBufferi :: Buffer -> ALenum -> Ptr ALint -> IO ()