Compare commits

...

2600 Commits

Author SHA1 Message Date
Chris Robinson b907c21f48 Release 1.16.0 2014-08-14 17:30:11 -07:00
Chris Robinson 8364aa5f6f ALC_SOFT_pause_device is finished 2014-08-12 08:45:11 -07:00
Chris Robinson ba3f1e7468 Assume SSE is available if building with support and no run-time checking 2014-08-11 14:45:28 -07:00
Chris Robinson 74c8de6372 Try the __cpuid intrinsic if GCC's __get_cpuid isn't available 2014-08-11 14:19:15 -07:00
Chris Robinson e3ee534c39 Check for GCC's __get_cpuid before using it 2014-08-11 13:38:43 -07:00
Chris Robinson 8ecbeb4945 Only support dsound and mmdevapi on Windows 2014-08-11 09:13:08 -07:00
Chris Robinson 960cdec0b5 Remove a couple unnecessary HAVE_*_H header defines
The xmmintrin.h and arm_neon.h headers are always available with SSE and Neon
support, respectively.
2014-08-11 09:09:00 -07:00
Chris Robinson 4a9545affa Don't try to generate .def or .lib files when building statically 2014-08-10 19:48:53 -07:00
Chris Robinson 6273a18de5 Add an option to build an import .lib with dlltool on mingw
Also make sure the ordinals are stripped from the .def file (using sed), so
that the generated .lib will link apps to the DLL using function names instead
of ordinals.
2014-08-10 19:40:29 -07:00
Chris Robinson a3c236598a Simplify some vector size range checks 2014-08-10 10:52:08 -07:00
Chris Robinson 936a79da7d Give NULL for null-vector iterators 2014-08-10 09:12:08 -07:00
Chris Robinson 94013216b6 Pass pointer-to-vector types as char* instead of void*
C aliasing rules only allow char* to alias an otherwise-incompatible type,
rather than void*.
2014-08-10 08:46:55 -07:00
Chris Robinson 22856844c6 Properly set FluidSynth's include dir for older cmake versions 2014-08-10 05:50:19 -07:00
Chris Robinson 357beb0570 Create a .def file when building with MinGW
Preferably we'd tell CMake to make it itself instead of specifiying compiler-
specific flags, but there doesn't seem to be a way to.
2014-08-10 05:10:11 -07:00
Chris Robinson f173a67870 Use VECTOR_FIND_IF and VECTOR_FOR_EACH instead of manual loops 2014-08-09 06:15:11 -07:00
Chris Robinson 1692dda4b9 Use the default input device for portaudio's default capture device 2014-08-08 19:48:45 -07:00
Chris Robinson 10c01110f3 Modify CPU extensions and effect options to be enabled when checked
It looks nicer and more featured for options to be checked when they're
allowed, rather than when disallowed.
2014-08-07 05:16:05 -07:00
Chris Robinson 6048d70232 Add a 48khz HRTF data set
Made by simply using makehrtf's resampling option with the 44.1hz KEMAR data
set.
2014-08-07 04:26:06 -07:00
Chris Robinson e64d0f83cd Disable the autowah effect
There's apparently some issues with it causing noise or killing the output. It
might be due to the per-sample changes being too harsh for the filter to keep
up with, but it's not something I can take care of in time for release.

This commit should be reverted after release when work on fixing it can resume.
2014-08-06 23:38:05 -07:00
Chris Robinson 23441be474 Make the DYNLOAD LoadFSynth function non-inline 2014-08-05 19:38:33 -07:00
Chris Robinson 951870b189 Enable large file support 2014-08-05 05:05:05 -07:00
Chris Robinson bda39c5038 Filter for relative HRTF data paths and add Add/Remove label texts 2014-08-05 04:56:51 -07:00
Chris Robinson 066a48c5fb Update alsoft-config
Adds Compressor effect and SSE 4.1 for exclusion, and alters the HRTF tables
tooltip to refer the the default data sets instead of the built-in set.
2014-08-05 02:43:04 -07:00
Chris Robinson 7b4a2335eb Load fluidsynth dynamically when possible 2014-08-05 01:48:30 -07:00
Chris Robinson 451b780e08 Fix some lock ordering to avoid potential deadlocks 2014-08-03 00:56:58 -07:00
Chris Robinson f5194a9d8e Use an ATOMIC_INIT macro instead of ATOMIC_LOAD_UNSAFE 2014-08-03 00:26:21 -07:00
Chris Robinson 659b340716 Use the right type for atomic compare-exchange 2014-08-01 02:47:46 -07:00
Chris Robinson 1c1e878be7 Add some casts for inline assembly atomics
And remove an unnecessary void cast
2014-08-01 02:40:25 -07:00
Chris Robinson 87423f046e Use atomics for the device and context list heads 2014-08-01 02:04:40 -07:00
Chris Robinson 15a58eb383 Make the source's buffer queue head and current queue item atomic 2014-07-31 07:20:36 -07:00
Chris Robinson cdfc5a4d31 Remove an unused function 2014-07-31 04:44:39 -07:00
Chris Robinson ce046d2f03 Rename ATOMIC_COMPARE_EXCHANGE to ATOMIC_COMPARE_EXCHANGE_STRONG 2014-07-31 04:34:46 -07:00
Chris Robinson 6ce464aed8 Avoid a CPU memory fence, and better order memory barriers to accesses
Code produced by GCC 4.9's C11 atomic implementation does not seem to add a
CPU memory fence even with memory_order_seq_cst. Unnecessary memory fences
will be a detriment to performance, so they should only be added if needed.
2014-07-31 04:19:33 -07:00
Chris Robinson 7c847e62c3 Change Windows-based atomics for non-C99 compilers 2014-07-27 19:34:32 -07:00
Chris Robinson 38e8dc8540 Use a full memory barrier for Windows 2014-07-27 18:31:10 -07:00
Chris Robinson 6864053eee Use pulseaudio's write callback to signal a mixer proc wakeup 2014-07-26 22:04:26 -07:00
Chris Robinson 0ad3f61351 Don't try to use internal visibility
A number of functions are passed as callbacks to external libraries, which
internal visiiblity doesn't allow.
2014-07-26 17:48:36 -07:00
Chris Robinson d3980bdebc Always set the active source's update method
If the source is stopped, changes its buffer, then played again quickly, the
source will never be removed from the active source list causing the update
method to remain as it was. If the buffer was changed between mono and multi-
channel, this causes it to use the wrong method.
2014-07-26 16:41:39 -07:00
Chris Robinson 531c0d8e6b Explicitly pass the address of atomics and parameters that can be modified 2014-07-26 03:00:49 -07:00
Chris Robinson a3dbe08c8b Support C11 atomics 2014-07-23 06:36:34 -07:00
Chris Robinson e4b779c492 Use generic atomics in more places 2014-07-22 18:57:51 -07:00
Chris Robinson a3b1d4a5e2 Implement RefCount as a generic atomic type 2014-07-22 18:18:14 -07:00
Chris Robinson 5a339a2a5b Add macros for generic atomic functionality 2014-07-22 00:20:28 -07:00
Chris Robinson 7b41ed7ec4 Check the current directory first for data files 2014-07-21 04:42:45 -07:00
Chris Robinson 0ff4a91442 Make some functions static 2014-07-20 01:27:33 -07:00
Chris Robinson 8592b9371a Use the UNUSED macro instead of a void cast 2014-07-19 20:46:41 -07:00
Chris Robinson 0c31ccd456 Stop trying to read samples if there was an error 2014-07-19 20:46:12 -07:00
Chris Robinson 9370fea359 Trace when a soundfont buffer goes unused 2014-07-19 14:18:52 -07:00
Chris Robinson 36381f3164 Load the default soundfont as a comma-separate list of filename
This allows multiple soundfont files to be "patched" together to create a
single soundfont. For instance a GM soundfont with a separate soundfont for
GS-only additions.
2014-07-19 13:44:02 -07:00
Chris Robinson 994c3f00e5 Keep existing presets when loading in a new soundfont
Note that alLoadSoundfontSOFT doesn't currently allow using a soundfont with
existing presets. This is for being able to load and stack multiple soundfonts
for the default soundfont.
2014-07-19 12:42:52 -07:00
Chris Robinson 85e135beea Use a macro to specify the fourcc formatters 2014-07-18 18:56:26 -07:00
Chris Robinson 17682cb0ce Explicitly include libavutil/channel_layout.h for the channel layout stuff 2014-07-18 01:21:42 -07:00
Chris Robinson 23cae7783d Properly scale the first HRTF pass-through coefficient
Coefficients are scaled by 32767. For pass-through, this is attenuated by
sqrt(0.5) to maintain a consistent perceived volume.
2014-07-17 18:02:23 -07:00
Chris Robinson 8635733d59 Fix typo (overriden -> overridden) 2014-07-17 10:22:58 -07:00
Chris Robinson 15d7ed2f0d Cleanup some HRTF code
Use loops instead of duplicating code, rewrite some lines to be clearer.
2014-07-16 13:26:47 -07:00
Chris Robinson 0d3b18b17d Fix omni-directional moving HRTF coeffs 2014-07-14 09:15:12 -07:00
Chris Robinson f4cdecebcf Add a source radius property that determines the directionality of a sound
At 0 distance from the listener, the sound is omni-directional. As the source
and listener become 'radius' units apart, the sound becomes more directional.

With HRTF, an omni-directional sound is handled using 0-delay, pass-through
filter coefficients, which is blended with the real delay and coefficients as
needed to become more directional.
2014-07-11 00:03:13 -07:00
Chris Robinson c5af088b5f Access the result macro parameter only once 2014-07-06 22:11:29 -07:00
Chris Robinson f4f1c1eeed Rename some functions 2014-07-06 09:21:38 -07:00
Chris Robinson 8dec92531b Store 4 modulators per map entry 2014-07-06 09:06:35 -07:00
Chris Robinson dc76013126 Regroup and reorganize some macros 2014-07-06 08:05:35 -07:00
Chris Robinson 23979ac648 Use VECTOR_FIND_IF instead of a manual loop 2014-07-06 05:16:44 -07:00
Chris Robinson f809d3c81a Allow ALsoundfont_deleteSoundfont to handle multiple buffers 2014-07-06 05:00:05 -07:00
Chris Robinson d0a64fe191 Don't require pre-declaring vector types 2014-07-06 03:27:39 -07:00
Chris Robinson 5de7271bcd AL_SOFT_source_length is complete 2014-07-05 04:25:37 -07:00
Chris Robinson ad92181a26 Don't set modulators that have no destination 2014-07-05 03:55:45 -07:00
Chris Robinson ff915534cd Avoid aliasing an int array 2014-07-05 02:01:36 -07:00
Chris Robinson b88aaaf1bc Fix a couple checks 2014-07-05 01:47:08 -07:00
Chris Robinson 47f5e710c2 Make some more functions static 2014-07-05 01:23:06 -07:00
Chris Robinson 5afc29f65d Use a helper function to check valid MIDI controller inputs 2014-07-04 22:08:24 -07:00
Chris Robinson f667782df2 Set the 'valid' field when we have a valid buffer pointer 2014-07-04 06:57:58 -07:00
Chris Robinson 3f45c00779 Move an inline definition to a more appropriate source 2014-07-04 00:22:27 -07:00
Chris Robinson 17f79ea43e Make a function static 2014-07-04 00:00:39 -07:00
Chris Robinson fda5bc2bd2 AL_SOFT_MSADPCM is functionally complete 2014-07-03 20:59:44 -07:00
Chris Robinson 5303ee2588 Avoid an undefined allocation of 0 bytes 2014-07-01 23:32:06 -07:00
Chris Robinson cb726d87ce Simplify setting a fontsound link 2014-07-01 23:24:11 -07:00
Chris Robinson fec1fec373 Check that a fontsound is NOT null before deleting it 2014-07-01 22:52:29 -07:00
Chris Robinson 347d8f94e8 Remove an unused variable 2014-07-01 22:52:06 -07:00
Chris Robinson 8b1b52a687 Remove an unnecessary cast 2014-07-01 22:27:53 -07:00
Chris Robinson 56bb8689d4 Fix preset zone instrument header check 2014-07-01 19:43:20 -07:00
Chris Robinson 3c6752e765 Print SF2 text INFO chunks 2014-07-01 00:36:44 -07:00
Chris Robinson c06bb88756 Warn about all unhandled soundfont generators 2014-06-30 04:03:50 -07:00
Chris Robinson 8577df76c4 Check the last preset and instrument properties within the loop 2014-06-30 03:52:10 -07:00
Chris Robinson 22982948cf Standardize some New/Delete methods 2014-06-30 00:10:40 -07:00
Chris Robinson 8f8898b7b0 Remove an unused macro 2014-06-29 04:35:35 -07:00
Chris Robinson 8176d2c058 Load soundfont samples into an ALbuffer
Also remove ALsoundfont's now-unneeded sample storage functions and struct
fields.
2014-06-29 02:04:05 -07:00
Chris Robinson 389a2f7b4b Store and use an ALbuffer for samples in an ALfontsound
The fontsound still maintains its own start, end, and loop offsets, so that the
same buffer may be shared between multiple/all fontsounds. Ideally a single
buffer should be used for all fontsounds to avoid memory fragmentation and help
CPU caching, although higher quality soundfonts may need more memory than a
single buffer can hold.
2014-06-29 00:47:29 -07:00
Chris Robinson d19e42a723 Don't require MIDI sysex data to have the high bit clear 2014-06-26 04:35:51 -07:00
Chris Robinson b9462bbe3c Only set the needed HRTF coefficients 2014-06-21 19:17:39 -07:00
Chris Robinson 81b3395b41 Avoid negating 'negative' variable names 2014-06-20 23:48:58 -07:00
Chris Robinson be903d67b8 Don't pass the device to HRTF methods 2014-06-20 16:43:14 -07:00
Chris Robinson fb25a70f95 Don't overwrite the loopback device format with the HRTF format 2014-06-20 15:48:09 -07:00
Chris Robinson 0fd215cb84 Remove unused variables 2014-06-13 22:50:14 -07:00
Chris Robinson 57c683f822 Get the mixer and resampler functions when needed 2014-06-13 16:07:25 -07:00
Chris Robinson a8deaf12f4 Combine the direct and send mixers 2014-06-13 13:34:19 -07:00
Chris Robinson c29eb63489 Combine some dry and wet path types 2014-06-13 11:42:04 -07:00
Chris Robinson 19ec7b2ad2 Pre-apply the crossfeed filter gain to the input sample coefficients 2014-06-12 09:40:30 -07:00
Chris Robinson 4c706f59d9 Use floats for the BS2B filter 2014-06-11 09:57:48 -07:00
Chris Robinson dfded9595c Make bs2b_cross_feed inline 2014-06-10 12:46:58 -07:00
Chris Robinson d0fac3fe1d Don't try to set fp precision with __control87_2 or _controlfp 2014-06-08 23:39:52 -07:00
Chris Robinson c07fb7b45c Print CPU extensions that are unavailable but requested 2014-06-08 06:31:36 -07:00
Chris Robinson 8ca1f4f371 Open and close the file in the main thread in alffplay
This avoid problems with the file being closed while a video refresh is still
scheduled.
2014-06-08 03:26:34 -07:00
Chris Robinson be66692f94 Require SSE for SSE2, and SSE2 for SSE4.1 2014-06-07 23:28:32 -07:00
Chris Robinson 09d5475aa6 Update the disable-cpu-exts config option description 2014-06-07 06:08:22 -07:00
Chris Robinson 2ff266ca83 Update a couple sources with the proper author 2014-06-06 07:15:22 -07:00
Chris Robinson 6d1622dc68 Move InitiatePositionArrays to mixer_defs.h 2014-06-06 07:15:22 -07:00
Timothy Arceri fc0be88c0f Add SSE2 and SSE4.1 linear resamplers
Currently the only way SSE 4.1 is detected is by using __get_cpuid, i.e. with
GCC. Windows' IsProcessorFeaturePresent does not report SSE4.1 capabilities.
2014-06-06 07:15:00 -07:00
Chris Robinson 3b2fcb3ef6 Avoid a loop when updating the source position variables 2014-06-02 19:19:22 -07:00
Chris Robinson d2c0a43498 Add an ffmpeg-based example player
This example shows proper timing and A/V synchronization with an OpenAL stream.
Requires the in-progress AL_SOFTX_source_length extension to avoid manually
keeping track of the buffer queue length.
2014-06-02 17:33:11 -07:00
Chris Robinson 933f51a8bf Only define struct timespec if _TIMESPEC_DEFINED isn't set
This matches what the mingw-w64 headers define with the struct.
2014-05-31 17:41:10 -07:00
Chris Robinson 1d45c439b8 Add extern "C" to common headers. 2014-05-29 04:22:30 -07:00
Chris Robinson 73614f228e Add methods to exchange and compare-exchange RefCount values 2014-05-27 15:48:15 -07:00
Chris Robinson fd62868c17 Avoid unnecessary local variables 2014-05-27 15:32:38 -07:00
Chris Robinson f0b65aa6b7 Implement condition variables for Windows 2014-05-27 05:22:53 -07:00
Chris Robinson 2d2bc25fd0 Add an extension string for the in-progress source length extension
This is the extension that allows you to get the full length of the source's
buffer queue.
2014-05-26 04:07:50 -07:00
Chris Robinson b6e1042e8c Implement condition variables (POSIX only!)
Windows requires Vista or newer to get the CONDITION_VARIABNLE API, but we
currently only require XP.
2014-05-26 03:52:55 -07:00
Chris Robinson fd5e7f1466 Don't print an ERR if pulse fails to get latency info due to no data
It just means it was called too quickly after starting.
2014-05-26 03:41:33 -07:00
Chris Robinson 0ec0f7561c Add an option to get the length of a source's full queue
This simplifies keeping track how much a source has buffered in its queue,
which reduces a bunch of unnecessary book keeping the app would have to do.
2014-05-25 16:16:55 -07:00
Chris Robinson 3d6ffa282d Don't resume the backend if there's no contexts 2014-05-24 17:19:52 -07:00
Chris Robinson 4ca561acd3 Move the active source's offset out of the direct params 2014-05-24 16:55:10 -07:00
Chris Robinson 13eac2552e Update the examples to use SDL2
This should make it easier to build the examples on 64-bit, where there are no
premade dev libs for SDL1 on 64-bit mingw. FindSDL_sound.cmake has been
updated to accept SDL or SDL2.
2014-05-23 12:14:51 -07:00
Chris Robinson 5ef5d218c4 Mark a few more functions as const 2014-05-23 10:00:58 -07:00
Chris Robinson 3fc9791747 Rename CONST_FUNC and PRINTF_STYLE, and fix non-GNU AL_PRINT 2014-05-23 08:30:33 -07:00
Chris Robinson 7d997277a0 AL_SOFT_block_alignment is now considered done 2014-05-22 12:27:07 -07:00
Chris Robinson a48f5d26fd Ensure a proper amount of new elements are being reserved when inserting 2014-05-22 12:16:22 -07:00
Chris Robinson 9008bcbe54 Include strings.h if it exists for strncasecmp 2014-05-22 11:33:09 -07:00
Chris Robinson 2729a1549b Add a needed header for the CoreAudio backend 2014-05-22 11:29:26 -07:00
Chris Robinson e32e3df3aa Use the first non-0-length buffer when starting a source 2014-05-22 11:08:21 -07:00
Chris Robinson 85e990e915 Don't try to use the non-standard alloca.h
Variable-length arrays are C99 standard and are preferred over alloca. The only
compiler that needs alloca (MSVC) has it in malloc.h.
2014-05-22 08:46:13 -07:00
Chris Robinson 2b3550b61c Use an unsigned type for the win32 size_t formatter 2014-05-22 08:02:39 -07:00
Chris Robinson 100c059157 Mark some functions as const 2014-05-22 07:40:22 -07:00
Chris Robinson ecdfcdbfa5 The lower value of the gain vector contains the closest target value 2014-05-21 21:20:44 -07:00
Chris Robinson 2e38de349c Don't clear the current and step gain values when updating a source 2014-05-21 16:32:24 -07:00
Chris Robinson 6ea050cc6c Use the current buffer item for static sources too 2014-05-21 15:37:22 -07:00
Chris Robinson 4b433c900e Copy samples if needed in the 'copy' resampler 2014-05-21 15:24:40 -07:00
Chris Robinson dd2e6b7902 Properly process all samples with a bandpass filter 2014-05-21 14:59:21 -07:00
Chris Robinson e650db98f7 Use the correct input for HRTF mixing 2014-05-21 14:48:46 -07:00
Chris Robinson 4a8d38c19a Update the active source's direct and send properties after each mix 2014-05-21 14:47:25 -07:00
Chris Robinson f313ce2a96 Add some asserts to verify block alignment for conversion 2014-05-20 09:16:54 -07:00
Chris Robinson cd983245f1 Return a sample pointer from resamplers
Both resampling and filtering now avoid copying samples when they no-op.
2014-05-19 05:46:01 -07:00
Chris Robinson 8e04a8a022 Put per-channel filter properties together 2014-05-19 02:24:31 -07:00
Chris Robinson 7b782f6afa Return a sample pointer from DoFilters
This allows us to avoid copying samples when no filtering is needed. Doing the
same for resampling would require another temporary buffer in the device.
2014-05-19 01:54:52 -07:00
Chris Robinson ef9dfe3772 Move an HRTF mixer parameter and shorten a couple variable names 2014-05-18 11:05:38 -07:00
Chris Robinson 9317ec59b9 Don't pass the SendParams to the wet-path mixer 2014-05-18 10:35:11 -07:00
Chris Robinson c9083d04fa Don't pass the DirectParams to the dry-path mixer 2014-05-18 10:24:07 -07:00
Chris Robinson 5a1abf6918 Use different parameters for HRTF mixers 2014-05-18 09:31:08 -07:00
Chris Robinson 29b5dae6aa Pass some DirectParams as function parameters 2014-05-18 08:59:10 -07:00
Chris Robinson bd2721dc7a Use _mm_setr_ps instead of _mm_set_ps
Apparently _mm_set_ps loads in reverse order compared to _mm_load_ps, so
_mm_setr_ps should give what we really want.
2014-05-18 06:35:33 -07:00
Chris Robinson a5631e05cc Add a ALfilterState method to process multiple samples at once 2014-05-18 06:23:20 -07:00
Chris Robinson cd53a4b74c Reset filter properties when setting a null filter type 2014-05-17 08:05:49 -07:00
Chris Robinson 190ab90c0d Support the band-pass filter 2014-05-17 08:04:14 -07:00
Chris Robinson 82dd2d875e Apply high-pass source filters as needed 2014-05-17 07:54:25 -07:00
Chris Robinson 3a26ebef0b Implementing handling high-pass filter properties 2014-05-17 07:29:50 -07:00
Chris Robinson 0b5b39d5e6 Add a flag to specify when the low-pass filter needs to apply 2014-05-17 07:17:48 -07:00
Chris Robinson 70f1e54068 Use logarithmic adjustment for the gain in the autowah effect 2014-05-17 03:08:04 -07:00
Chris Robinson 1efddac3db Document the different filter types, and combine some split lines 2014-05-17 02:09:43 -07:00
Chris Robinson c5b25ba2a7 Rename freq_scale parameter to freq_mult 2014-05-17 01:32:55 -07:00
Chris Robinson a11e1c0858 Ensure that LoopEnd > LoopStart
For the benefit of Clang's static analysis, which doesn't know that a
static source will always have a valid buffer and a buffer must have a
non-0 loop range.
2014-05-15 01:44:29 -07:00
Chris Robinson fdcdda2ed3 Initialize a pointer to NULL instead of a string 2014-05-15 01:39:42 -07:00
Chris Robinson 028bd9767c Don't bother stripping whitespace from comments 2014-05-15 01:37:01 -07:00
Chris Robinson 042003896f Early-out when outN is 0 2014-05-15 01:32:32 -07:00
Chris Robinson 6dcd4fec7d Remove some unnecessary casts 2014-05-15 01:21:22 -07:00
Chris Robinson ef1b34dcd2 Initialize some variables when declaring them 2014-05-15 01:14:31 -07:00
Chris Robinson f0797e27f3 Initialize newformat to AL_NONE instead of a valid format 2014-05-15 01:11:38 -07:00
Chris Robinson 16d3316f70 Use an enum for the chorus and flanger waveforms 2014-05-14 23:51:24 -07:00
Chris Robinson a6974a6c83 Don't try to read the internal capture buffer into itself 2014-05-14 21:20:28 -07:00
Chris Robinson a1fbb434e8 Remove a dead assignment 2014-05-14 05:27:16 -07:00
Chris Robinson debd5346fb Add a couple asserts to ensure a proper buffer
It should not be possible for a playing or paused source to not have a
valid buffer, but Clang's static analyzer doesn't know that. Hopefully
an assert will convince it.
2014-05-14 03:40:01 -07:00
Chris Robinson f311f74001 Avoid freeing an in-use capture buffer
When stopping, ALSA may capture into its own storage buffer. Do not free
the storage buffer if it first reads from it.
2014-05-14 03:22:42 -07:00
Chris Robinson 1d2504d12e Make RefCount a non-integer type
It should only be accessed through the appropriate functions to ensure proper
atomicity.
2014-05-14 02:47:07 -07:00
Chris Robinson 4454ae25c7 Add HF Reference as a filter property 2014-05-14 01:24:18 -07:00
Chris Robinson 3582a44dff Make LOWPASSFREQREF a float value 2014-05-11 10:09:52 -07:00
Chris Robinson 343200d229 Store the filter reference frequency in the source 2014-05-11 10:07:17 -07:00
Chris Robinson 58235bec17 Update the source send target gains properly 2014-05-11 09:45:07 -07:00
Chris Robinson b89cc3417b Avoid locking the mixer when unqueueing buffers 2014-05-11 03:52:22 -07:00
Chris Robinson 9f8615c670 Avoid accessing the source's buffer queue head multiple times 2014-05-11 03:11:35 -07:00
Chris Robinson 851a917b03 Use a struct to store the source's direct gain/gainhf properties 2014-05-11 01:40:44 -07:00
Chris Robinson e78acce652 Don't mix old- and new-style FIND_PACKAGE_HANDLE_STANDARD_ARGS calls
Some (all?) versions of cmake seem to get a bit confused when an old-style
FIND_PACKAGE_HANDLE_STANDARD_ARGS call fails, and the scripts attempt a new-
style call later on. It continues to think it's using old-style, acting as if
REQUIRED_VARS is the message and looks for a VERSION_VAR variable.
2014-05-10 09:34:16 -07:00
Chris Robinson ddd1550a8b Avoid locking the mixer while queueing buffers onto a source. 2014-05-10 08:55:28 -07:00
Chris Robinson e9297d9744 Mark the buffer queue pointers volatile 2014-05-10 08:03:53 -07:00
Chris Robinson 3e28fb33a3 Add a missing pointer init 2014-05-10 07:57:35 -07:00
Chris Robinson 9e25ea8666 Update the output buffer pointer in the Write_* methods 2014-05-10 07:52:02 -07:00
Chris Robinson 67b4bd1031 Add a couple consts 2014-05-10 07:25:05 -07:00
Chris Robinson 9642bf637d Explicit init the source queue and current_buffer pointers 2014-05-10 06:32:39 -07:00
Chris Robinson a455704148 Use a RWLock to help protect the source's buffer queue
In some instances this allows to to remove the device/mixer lock, or reduce how
long it's held.
2014-05-10 05:07:13 -07:00
Chris Robinson eebde08e65 Don't explicitly store the BuffersInQueue 2014-05-10 03:33:41 -07:00
Chris Robinson c4383b65e2 Store the current buffer queue item, rather than played buffer count 2014-05-10 03:21:40 -07:00
Chris Robinson eea46f268d Avoid checking the previous buffer multiple times 2014-05-10 02:01:38 -07:00
Chris Robinson bf97f58edb Append the number of bits, not bytes, to types
Also use ALuint64SOFT from alext.h for uint64
2014-05-08 00:28:34 -07:00
Chris Robinson 8b7c71e20e Always use unsigned int for RefCount 2014-05-07 23:49:06 -07:00
Chris Robinson bd88e1743a Build a common static lib for reusable code 2014-05-07 19:51:42 -07:00
Chris Robinson a2bddb7b40 Move RWLock and UIntMap implementations to common
This should make the code in common completely self-reliant.
2014-05-07 19:16:49 -07:00
Chris Robinson 3e274c3a57 Make sure FILETIME and ULARGE_INTEGER sizes match 2014-05-07 16:12:34 -07:00
Chris Robinson 1c01e94237 Move the static_assert definition to its own header 2014-05-07 02:28:25 -07:00
Chris Robinson d8d609b940 Use standard types for the RWLock 2014-05-07 01:03:06 -07:00
Chris Robinson 38b98de78e Check for C99 _Bool support 2014-05-06 23:10:50 -07:00
Chris Robinson 1aff37114a Move atomic method definitions to a separate common source 2014-05-06 19:07:40 -07:00
Chris Robinson fa12855d19 Move threads.c to a separate source dir
This will eventually serve to build a static lib of common wrapper methods,
such as threads, mutexes, atomics, etc.
2014-05-06 18:57:42 -07:00
Chris Robinson 0ea979a262 Move some headers to include/
Note, these are not installed. Only headers in include/AL/ are installed.
2014-05-06 18:29:53 -07:00
Chris Robinson 1a76a3238c Use gettimeofday if clock_gettime isn't available 2014-05-06 17:46:17 -07:00
Chris Robinson cbb94405db Test _Alignas with 16 instead of 4 2014-05-05 14:21:09 -07:00
Chris Robinson 2edd047178 C11's alignas is also sufficient for SSE aligning 2014-05-05 13:53:07 -07:00
Chris Robinson 2bc16e9947 Add a cast to silence an MSVC warning 2014-05-04 11:50:58 -07:00
Chris Robinson a48d235ef4 Trace skipped info sub-chunks 2014-05-04 01:19:09 -07:00
Chris Robinson bca7c682f1 Use ALuint for the amount to skip 2014-05-04 01:00:21 -07:00
Chris Robinson 2a7f3c015e Don't use the next power of two when duplicating config values
Speed isn't terribly important here, and reallocs should be extremely rare.
2014-05-04 00:37:25 -07:00
Chris Robinson db90985f6a Remove unnecessary ifdefs
mixer_sse.c and mixer_neon.c are only compiled when the relavent headers are
found anyway.
2014-05-04 00:19:55 -07:00
Chris Robinson a2bb4c3f13 Always use the current gains when mixing
The current gain gets explicitly set to the target when the stepping is
finished to ensure the target is still used. This way, however, will allow for
asynchronously 'canceling' a fade by setting the counter to 0.
2014-05-04 00:13:19 -07:00
Chris Robinson d066c7b124 Better pack HRTF mixing properties 2014-05-03 18:59:26 -07:00
Chris Robinson b2e533fbfc Clamp the current and target gain lower bound to epsilon
Should give a bit more wiggle room for the gain stepping to get lower than the
silence threshold.
2014-05-03 17:51:06 -07:00
Chris Robinson 34e96aef60 Make sure all gain steps are applied with the SSE and Neon mixers 2014-05-03 17:24:46 -07:00
Chris Robinson 2660ecfd4a Revert e9aee578a7
The vector iterator needs to be the start of the Data array even if the vector
itself is NULL, or else insertion can have a negative insert position.
2014-05-02 11:48:55 -07:00
Chris Robinson 3bdb3f9db7 Iniitialize some mmdevapi backend fields in the constructor 2014-05-02 10:47:31 -07:00
Chris Robinson d90f684f16 Use a backup in case pthread_mutex_timedlock isn't available 2014-05-01 14:15:31 -07:00
Chris Robinson 804b08e0ca Use ALboolean for al_string_empty's return type 2014-04-30 12:53:52 -07:00
Chris Robinson f150676873 Add a helper VECTOR_FOR_EACH macro 2014-04-30 12:30:02 -07:00
Chris Robinson f9d70aa9ef Use _mm_set_ps() to set an __m128 instead of {} 2014-04-26 06:31:31 -07:00
Chris Robinson 149df6d4db Convert the mmdevapi backend to the new backend API 2014-04-25 09:26:53 -07:00
Chris Robinson 28e9f0db8c Use a vector for mmdevapi device lists 2014-04-24 04:02:05 -07:00
Chris Robinson 83970369e0 More device list cleanup cleanup 2014-04-24 02:33:54 -07:00
Chris Robinson 7dd4a2348f Use a helper function to clear PulseAudio device lists 2014-04-24 01:38:24 -07:00
Chris Robinson a4cf378af8 Use a helper method to clear winmm device lists 2014-04-24 01:25:26 -07:00
Chris Robinson 9af8e10c20 Avoid using superfluous LP* types 2014-04-23 10:08:33 -07:00
Chris Robinson 0b527621a5 Use a RefCount for counting committed winmm buffers 2014-04-23 05:08:17 -07:00
Chris Robinson 8fd224f841 Avoid forward-declaring backend vtables 2014-04-23 03:52:05 -07:00
Chris Robinson 420599f8e3 Avoid using assert() in createBackend 2014-04-23 02:18:07 -07:00
Chris Robinson 7fed6383c0 Include FindALSA.cmake
Older versions of CMake don't seem to have it.
2014-04-23 02:12:34 -07:00
Chris Robinson ada4868d86 Properly handle HAVE_DYNLOAD in the dsound backend 2014-04-22 08:55:17 -07:00
Chris Robinson a4fbe158d4 Avoid some superfluous types 2014-04-22 08:44:06 -07:00
Chris Robinson 84afe4fb40 Simplify the cmake backend handling a bit 2014-04-22 07:58:25 -07:00
Chris Robinson d5ac11c87f Fix adding backend include directories 2014-04-22 07:21:19 -07:00
Chris Robinson f6ad3ef221 Use a helper to return mmdevapi message thread responses 2014-04-22 06:13:49 -07:00
Chris Robinson e9aee578a7 Avoid dereferencing a NULL pointer 2014-04-21 23:10:09 -07:00
Chris Robinson 0d2521e30e Use a vector to store the ALSA device lists 2014-04-21 23:01:50 -07:00
Chris Robinson df3c54a4bf Use al_string for ALSA's device map 2014-04-21 20:53:27 -07:00
Chris Robinson ad71565e45 Trace the device name being opened in ALSA and PulseAudio 2014-04-21 20:26:29 -07:00
Chris Robinson ca1008571e Use the althrd methods in winmm 2014-04-21 09:35:17 -07:00
Chris Robinson d856183c4b Only declare backend options when the backend is available 2014-04-20 22:42:53 -07:00
Chris Robinson dbe0897c7f Use the stored format block align instead of calculating it 2014-04-20 22:29:54 -07:00
Chris Robinson 5215b9cad2 Use a vector for the PulseAudio DevMaps 2014-04-20 06:29:36 -07:00
Chris Robinson b435a8e01b Use an al_string in the PulseAudio backend instead of char* 2014-04-20 05:55:52 -07:00
Chris Robinson 32a2f95885 Convert the DSound backend to the new API 2014-04-19 22:54:21 -07:00
Chris Robinson bb969c3ccc Add a SZFMT macro for a size_t string formatter 2014-04-19 09:42:03 -07:00
Chris Robinson 043e25b602 Don't link with libwinmm unless WinMM is enabled
We don't use timeGetTime for timing anymore, so winmm only needs to be linked
when the winmm backend is enabled.
2014-04-19 08:50:41 -07:00
Chris Robinson 96c559fbef Use a find module to look for DirectSound 2014-04-19 07:49:27 -07:00
Chris Robinson 887763e41c Use find cmake modules for more backends 2014-04-19 06:35:14 -07:00
Chris Robinson 1d266aa834 Add a GCC-specific STATIC_UPCAST macro that checks the object type
The check is compile time, and is functionally identical to the old/alternate
version.
2014-04-19 03:11:23 -07:00
Chris Robinson 59fc9aac0e Use C11 alignas when available 2014-04-19 02:11:04 -07:00
Chris Robinson 8badd3740e Dont bother trying _alloca
We prefer C99 variable-length arrays, and other systems (including MSVC9) don't
have an issue with alloca.
2014-04-19 01:10:12 -07:00
Chris Robinson 1d69ec9ce1 Set -D_GNU_SOURCE=1 -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 earlier 2014-04-19 01:02:36 -07:00
Chris Robinson 9b685bace5 Use FindALSA.cmake to look for ALSA
Also, only provide the ALSA_BACKEND_ALSA option if it's available
2014-04-19 00:52:19 -07:00
Chris Robinson 2eda15fc77 Only define SSE, Neon, and FluidSynth options if they're available 2014-04-18 23:20:27 -07:00
Chris Robinson c041a99f72 Simplify some error checking 2014-04-18 06:54:18 -07:00
Chris Robinson b8d56190d1 Check pthread_mutex_unlock for errors 2014-04-18 06:39:46 -07:00
Chris Robinson cb3315f9d3 Remove unnecessary includes 2014-04-18 02:35:16 -07:00
Chris Robinson b979a8005f Alter the al_fopen macro to not directly take parameters 2014-04-18 02:25:07 -07:00
Chris Robinson 1353cb0d88 Rename AL_ONCE_INIT to AL_ONCE_FLAG_INIT 2014-04-18 02:21:58 -07:00
Chris Robinson 579e7c10c2 Check TlsSetValue for error 2014-04-17 22:57:16 -07:00
Chris Robinson 6c8bf9ec42 Rename althread_once to be more C11-like 2014-04-17 21:39:51 -07:00
Chris Robinson 20e5ec18e1 Handle the lib name as UTF-8 2014-04-17 21:17:48 -07:00
Chris Robinson 5abefaed0a Use the thread ID for althrd_t on Windows 2014-04-17 21:01:54 -07:00
Chris Robinson 36df67f546 Rename SetThreadName to althrd_setname 2014-04-17 20:41:32 -07:00
Chris Robinson 528c8c5e77 Check explicitly for pthread_mutexattr_setkind_np before use 2014-04-17 19:01:12 -07:00
Chris Robinson 8a00c24012 Fix SetThreadName for 64-bit MSVC builds 2014-04-17 09:22:57 -07:00
Chris Robinson 4b5e8b8c40 Don't inline al_nssleep 2014-04-17 09:14:03 -07:00
Chris Robinson d1f1a1d056 Make and use a C11-like altimespec_get wrapper function 2014-04-17 09:03:57 -07:00
Chris Robinson 47f5c436c8 Fix a signed-unsigned comparison warning and clarify a comment 2014-04-17 01:24:03 -07:00
Chris Robinson fbb4cbbe01 Keep TlsDestructors within threads.c
This basically makes the threads implementation self-contained in threads.c and
threads.h, except for the UIntMap/RWLock implementations.
2014-04-17 01:09:25 -07:00
Chris Robinson e5d39a5f4c Don't use DllMain to call altss destructors 2014-04-17 00:56:02 -07:00
Chris Robinson c3b1c31d9b Rename althread_key_ wrappers to altss_ and move it to threads.h/c 2014-04-17 00:11:12 -07:00
Chris Robinson 8cc3d05949 Fix some almtx_ return values 2014-04-16 08:21:45 -07:00
Chris Robinson 2149ccd985 Remove almtx_normal and almtx_errorcheck 2014-04-16 08:00:54 -07:00
Chris Robinson c6dd479229 Make sure the duration given to althrd_sleep is valid 2014-04-16 07:52:43 -07:00
Chris Robinson 8a51995cfa Bump _WIN32_WINNT to 0x0502 (XP SP2) for GetThreadId 2014-04-16 07:30:18 -07:00
Chris Robinson 959d75edc8 Fix althrd_sleep return value 2014-04-16 07:18:28 -07:00
Chris Robinson b020dd13fd Avoid using a Sleep() wrapper 2014-04-16 06:59:44 -07:00
Chris Robinson 184cf30cf7 Use althrd_yield instead of alsched_yield 2014-04-16 06:18:24 -07:00
Chris Robinson d124aee4d7 Remove the old thread wrappers for the new ones 2014-04-16 06:11:40 -07:00
Chris Robinson 505ef82246 Move the threads and mutex wrappers to threads.c 2014-04-16 05:40:40 -07:00
Chris Robinson e8517d8600 Fix Windows' almtx_timedlock 2014-04-16 05:32:55 -07:00
Chris Robinson 18ab9cbbdd Implement a C11-like thread wrapper and use it in mmdevapi and pulseaudio 2014-04-16 05:19:34 -07:00
Chris Robinson 29cb5058c0 Use a C11-like mutex wrapper instead of CRITICAL_SECTIONs 2014-04-16 01:39:11 -07:00
Chris Robinson 9c70ca9da6 Avoid memory leak if preset ID lookup fails 2014-04-14 01:33:31 -07:00
Chris Robinson 7bc08bb08c Ensure VECTOR_ITER_BEGIN gives a pointer-to-type instead of an array-of-type 2014-04-14 00:58:45 -07:00
Chris Robinson e0d24f9da4 Use a helper to detect a path slash on Windows 2014-04-13 21:44:13 -07:00
Chris Robinson e615ea3cf9 Use VECTOR_INSERT to copy and append strings 2014-04-13 20:54:12 -07:00
Chris Robinson 219a0e6352 Add a GCC-specific VECTOR_INSERT
This version is capable of doing a compile-time type checks, to ensure the
objects being inserted are compatible with the vector data type. It should
otherwise be functionally identical.
2014-04-13 20:26:04 -07:00
Chris Robinson 0ef87227c1 Pass in the vector insertion point as a pointer 2014-04-13 18:42:41 -07:00
Chris Robinson cce9609b9e Avoid referencing the start iterator twice for vector insertion 2014-04-13 17:06:18 -07:00
Chris Robinson e76bc53d37 Insert all new effect slots into the active effect slots at once 2014-04-10 20:49:01 -07:00
Chris Robinson 90ae4b7c0f Add a VECTOR_INSERT method to insert a range of elements at once 2014-04-09 22:50:28 -07:00
Chris Robinson 023981acb9 Fix a NULL deref when cloning a 0-sized vector 2014-04-09 20:14:45 -07:00
Chris Robinson 1290f37750 Silence an MSVC warning about -1u 2014-04-07 13:55:22 -07:00
Chris Robinson add3013234 Fix the default XDG_DATA_DIRS paths 2014-04-07 13:24:51 -07:00
Chris Robinson 4f1104125b Use an al_string vector for winmm device names
Also now gets wchar device names, to properly convert to UTF-8
2014-04-07 12:43:19 -07:00
Chris Robinson c6821e5dd1 Use C11's static_assert when available 2014-04-07 11:48:28 -07:00
Chris Robinson f1a4b95b8c Use FIND_LIBRARY to look for the CoreAudio framework 2014-04-05 17:43:35 -07:00
Chris Robinson 5e0fc84f49 Make HRTF stepping values per-channel 2014-04-05 10:24:13 -07:00
Chris Robinson 569374da2c Properly compare al_strings 2014-04-03 13:46:09 -07:00
Chris Robinson 482fb37b68 Recognize NULL as an empty vector/string 2014-04-03 11:13:12 -07:00
Chris Robinson 570eb4e1a7 Don't pass the vector's capacity as a parameter to vector_reserve 2014-03-31 06:13:56 -07:00
Chris Robinson 48b472abb7 Use vectors for the dsound device lists 2014-03-31 02:48:01 -07:00
Chris Robinson 7b93e10f7a Use an al_string for the device name 2014-03-28 22:11:34 -07:00
Chris Robinson 7570195b09 strlen returns size_t 2014-03-28 08:19:45 -07:00
Chris Robinson d997be67fd Use al_string to handle mmdevapi and dsound device names 2014-03-28 07:59:47 -07:00
Chris Robinson af8fda8a4a Add an al_string type and use it for the device lists 2014-03-28 05:44:19 -07:00
Chris Robinson 2ca673cca2 Properly handle special folder names with extended characters 2014-03-28 01:21:05 -07:00
Chris Robinson 20886059fd Wrap fopen calls under Windows
The idea is that all filenames we deal with are encoded as UTF-8, but the
Windows functions that take a char string interpret it using the ANSI codepage.
So instead, we convert the UTF-8 string to a wchar string, and then use the
wchar functions for proper extended character filename support.
2014-03-28 00:37:42 -07:00
Chris Robinson 18620ab5e0 Check for ftw and _wfindfirst 2014-03-27 22:21:37 -07:00
Chris Robinson d11d061b2c Don't have VECTOR_INIT "return" anything 2014-03-27 17:21:23 -07:00
Chris Robinson 4f3e71e5b7 Reactivate the mmdevapi audio client and set the event handle on reset 2014-03-27 17:17:35 -07:00
Chris Robinson 045959e9c0 Use C99 VLA instead of alloca when available 2014-03-25 18:16:03 -07:00
Chris Robinson 03fd2b8266 Remove setState from the MidiSynth vtable 2014-03-23 18:34:49 -07:00
Chris Robinson de5c54db09 Remove an unused method 2014-03-23 17:47:58 -07:00
Chris Robinson cbb82647fb Remove an unnecessary conditional 2014-03-23 17:44:26 -07:00
Chris Robinson e2cb41dc61 Remove the last bits of the predictive sample processing 2014-03-23 17:33:57 -07:00
Chris Robinson 72986882f5 Remove the click removal buffers for auxiliary effect slots 2014-03-23 16:28:55 -07:00
Chris Robinson a25260ea8a Identity gain step is 1, not 0 2014-03-23 16:13:46 -07:00
Chris Robinson f5e10b9945 Fix Neon gain step loops 2014-03-23 16:13:13 -07:00
Chris Robinson 52deb557d5 Add gain stepping to the send mixers 2014-03-23 16:11:21 -07:00
Chris Robinson 83038c0dab Add some integer casts, and a range check 2014-03-23 15:22:37 -07:00
Chris Robinson 55f851093f Remove the now-unneeded click removal buffers for the device
They are still there for auxiliary sends. However, they should go away soon
enough too, and then we won't have to mess around with calculating extra
"predictive" samples in the mixer.
2014-03-23 07:31:33 -07:00
Chris Robinson 92433532fe Don't feed the HRTF mix to the click removal and pending click buffers
The coefficients (which control the volume and panning) already use stepping to
non-abruptly fade the mix.
2014-03-23 07:18:49 -07:00
Chris Robinson 81e049bd47 Step mixing gains per-sample for non-HRTF mixing
This fades the dry mixing gains using a logarithmic curve, which should produce
a smoother transition than a linear one. It functions similarly to a linear
fade except that

step = (target - current) / numsteps;
...
gain += step;

becomes

step = powf(target / current, 1.0f / numsteps);
...
gain *= step;

where 'target' and 'current' are clamped to a lower bound that is greater than
0 (which makes no sense on a logarithmic scale).

Consequently, the non-HRTF direct mixers do not do not feed into the click
removal and pending click buffers, as this per-sample fading would do an
adequate job of stopping clicks and pops caused by extreme gain changes. These
buffers should be removed shortly.
2014-03-23 06:57:00 -07:00
Chris Robinson 0ce0a88fd6 Move the step counter and moving flag to DirectParams 2014-03-23 03:03:03 -07:00
Chris Robinson b0d511a860 Store the HrtfState directly in the DirectParams 2014-03-23 02:45:50 -07:00
Chris Robinson cc599333a8 Don't try to use __restrict as a replacement for restrict
The compiler it's intended for, MSVC, can't use it anyway because of problems
it causes with __declspec(restrict).
2014-03-23 01:35:15 -07:00
Chris Robinson 9959972c7e Add a stub 'soft' MIDI synth handler
Eventually this one will be used to handle MIDI internally, using our own
mixers and resamplers.
2014-03-22 21:53:00 -07:00
Chris Robinson 842aeb888b Move some HRTF and mixer structs to alu.h 2014-03-22 02:39:57 -07:00
Chris Robinson 22f6eb18a2 Use a void* for the MidiSynth Delete method param 2014-03-22 01:49:03 -07:00
Chris Robinson d8bfd12d31 Use a void* for the backend Delete method param 2014-03-22 00:28:55 -07:00
Chris Robinson 0a030c2bd9 Use a void* for the effect state Delete method param 2014-03-21 23:56:18 -07:00
Chris Robinson f5ce73646c Avoid a size_t-to-ALuint conversion warning 2014-03-21 17:29:20 -07:00
Chris Robinson 8c33b4d742 Increase the vector reserve as needed when pushing in new items 2014-03-21 16:35:38 -07:00
Chris Robinson 983fa4630a Rename the vector's Max field to Capacity 2014-03-21 14:03:26 -07:00
Chris Robinson 40a0692a1c Use vectors for the GenModList 2014-03-21 02:41:46 -07:00
Chris Robinson ff63188cc2 Add a generic vector interface and use it for the active effect slots 2014-03-21 01:23:01 -07:00
Chris Robinson e6e3937fa9 Use flexible array members to pad the device and context structs
This helps avoid the convoluted math otherwise required to ensure the default
slot and listener, respectively, are aligned.
2014-03-20 14:49:46 -07:00
Chris Robinson 168149ce9d Keep track of the mix count
The purpose of this is to provide a safe way to be able to "swap" resources
used by the mixer from other threads without the need to block the mixer, as
well as a way to track when mixes have occurred. The idea is two-fold:

It provides a way to safely swap resources. If the mixer were to (atomically)
get a reference to an object to access it from, another thread would be able
allocate and prepare a new object then swap the reference to it with the stored
one. The other thread would then be able to wait until (count&1) is clear,
indicating the mixer is not running, before safely freeing the old object for
the mixer to use the new one.

It also provides a way to tell if the mixer has run. With this, a thread would
be able to read multiple values, which could be altered by the mixer, without
requiring a mixer lock. Comparing the before and after counts for inequality
would signify if the mixer has (started to) run, indicating the values may be
out of sync and should try getting them again. Of course, it will still need
something like a RWLock to ensure another (non-mixer) thread doesn't try to
write to the values at the same time.

Note that because of the possibility of overflow, the counter is not reliable
as an absolute count.
2014-03-19 19:00:54 -07:00
Chris Robinson 0c5cbafcd8 Use a union to combine HRTF and non-HRTF mixer params 2014-03-19 17:55:09 -07:00
Chris Robinson 213e3ba4af Select the mixer when setting the mixer-specific parameters 2014-03-19 16:10:09 -07:00
Chris Robinson 6ee54fb1f3 Store some source mixing parameters in the active source struct 2014-03-19 13:14:11 -07:00
Chris Robinson d6f7aac1bb Use a separate struct for tracking active sources 2014-03-18 19:56:25 -07:00
Chris Robinson 71b177918d Add another directory to .gitignore 2014-03-17 15:20:32 -07:00
Chris Robinson be2aeeb2fb Remove some unneeded code and unnecessary macros in the OpenSL backend 2014-03-17 15:17:56 -07:00
Chris Robinson dc74473025 Store the old-style backend funcs in the wrapper 2014-03-17 10:54:27 -07:00
Chris Robinson d346038102 Don't define HAVE_DYNLOAD with the IDE parser 2014-03-17 09:46:07 -07:00
Chris Robinson cf066fd306 Don't assume the default mmdevapi device is in the collection 2014-03-17 09:40:56 -07:00
Chris Robinson bb787ba8ee Avoid GCC's macro arg concat extension with IDE parsing 2014-03-17 09:16:33 -07:00
Chris Robinson 308d87b12a Return the original value from CompExchange* 2014-03-09 03:49:40 -07:00
Chris Robinson 372f3178f8 Remove the Apple-specific atomic function implementations.
I'm not sure if they're even used, but they were rather ugly and are set to get
even uglier since they don't follow normal conventions (missing exchange, and
cas not returning the original value).
2014-03-09 03:28:00 -07:00
Chris Robinson 74f2864c74 Update config file comments 2014-03-09 00:32:57 -08:00
Chris Robinson cde2f825d5 Use the correct array size 2014-03-08 22:48:36 -08:00
Chris Robinson 14a04020ff Improve int-to-float and uint-to-float conversions 2014-03-08 22:34:07 -08:00
Chris Robinson 2e279684d7 Only require MSADPCM block alignment to be a multiple of 2 2014-03-06 21:42:03 -08:00
Chris Robinson 7657fbb296 Only sign-expand the nibble when needed 2014-03-06 19:14:19 -08:00
Chris Robinson 143f786d1a Move the sample conversion routines to a separate file 2014-03-05 16:38:02 -08:00
Chris Robinson bf4ddfef6d Implement a simplistic MSADPCM encoder
It's not particularly good, but it's better than silence.
2014-03-05 07:27:22 -08:00
Chris Robinson 61569e09ed Allocate enough temp space for the ADPCM decoders and encoders 2014-03-05 06:18:55 -08:00
Chris Robinson a4126b66d4 Use specialized methods for converting ALshort to IMA4 and MSADPCM
As before, to avoid unnecessary direct copies
2014-03-05 06:13:00 -08:00
Chris Robinson f82f3f10ab Use specialized methods for converting IMA4 and MSADPCM to ALshort
Since the decoder methods already convert to ALshort, there's no need to use a
temp buffer that's just going to copy directly to the destination.
2014-03-05 04:59:24 -08:00
Chris Robinson 15b68fe694 Use maxi to clamp an int to a lower-bound 2014-03-05 04:37:55 -08:00
Chris Robinson fb1f9aad6e Expand the sign bit on the initial MSADPCM delta value 2014-03-05 00:30:23 -08:00
Chris Robinson a1c440bd09 Add an extension to support MSADPCM buffer formats 2014-03-04 22:44:30 -08:00
Chris Robinson f3a87d72b2 Add an extension to alter the block alignment for buffer unpack/pack ops
This is for unpacking (reading, e.g. alBufferData) and packing (writing, e.g.
alGetBufferSamplesSOFT) operations. The alignments are specified in sample
frames, with 0 meaning the default (65 for IMA4, 1 otherwise). IMA4 alignment
must be a multiple of 8, plus 1 (e.g. alignment = n*8 + 1), otherwise an error
will occur during (un)packing. Chenging the block alignment does not affect
already-loaded sample data, only future unpack/pack operations... so for
example, this is perfectly valid:

// Load mono IMA4 data with a block alignment of 1024 bytes, or 2041 sample
// frames.
alBufferi(buffer, AL_UNPACK_BLOCK_ALIGNMENT_SOFT, 2041);
alBufferData(buffer, AL_FORMAT_MONO_IMA4, data, data_len, srate);
alBufferi(buffer, AL_UNPACK_BLOCK_ALIGNMENT_SOFT, 0);
2014-03-04 05:53:35 -08:00
Chris Robinson b0a6b6427c Parameterize the block alignment 2014-03-04 04:29:35 -08:00
Chris Robinson a6eb38ea7f Use alloca for temp space decoding/encoding IMA4 blocks 2014-03-03 20:02:15 -08:00
Chris Robinson a2d9133ffc Reduce explicit template declarations and ignore IMA4-to-IMA4 conversions
Since we never store IMA4 data in buffers, we should only ever convert from
(user input) or to (user output) IMA4. Once we allow user-specified pack/unpack
block alignment, this wouldn't be a simple memcpy anyway.
2014-03-03 17:25:46 -08:00
Chris Robinson c442c93a93 Store the original frame size alignment in the buffer 2014-03-03 17:05:08 -08:00
Chris Robinson ea8a85b19c Move PATH_MAX fallback definitions to alMain.h 2014-02-27 18:17:20 -08:00
Chris Robinson 3867e3857d Add back the missing PATH_MAX fallback 2014-02-27 02:55:43 -08:00
Chris Robinson 6e7c0e15eb Use OpenDataFile to load soundfonts relative to data directories 2014-02-27 02:20:51 -08:00
Chris Robinson fd3acf19fe Add a note that absolute paths may be used for hrtf_tables 2014-02-27 01:54:45 -08:00
Chris Robinson a8249c3469 Move OpenDataFile to helpers.c so other sources can use it 2014-02-27 01:49:23 -08:00
Chris Robinson 3145bac8c6 Add an explicit cast to make Windows happy 2014-02-26 17:45:18 -08:00
Chris Robinson 99d82690ef Don't fallback to relative filename handling if opening as absolute fails 2014-02-26 17:38:19 -08:00
Chris Robinson 1b9a722601 Expand environment variables for all config option values when loading 2014-02-26 17:25:05 -08:00
Chris Robinson 89506435fa Support environment variables in the hrtf_tables config value 2014-02-25 19:18:59 -08:00
Chris Robinson 0ddb9888fe Fix a config option comment 2014-02-23 21:46:11 -08:00
Chris Robinson 9dd16f8b9c Attempt to restore the Neon-enhanced ApplyCoeffsStep method
Unable to test, but it hopefully works.
2014-02-23 21:28:34 -08:00
Chris Robinson 9a4ded2491 Revert "Apply HRTF coefficient stepping separately"
This reverts commit 25b9c3d0c1.

Conflicts:
	Alc/mixer_neon.c

Unfortunately this also undoes the Neon-enhanced ApplyCoeffsStep method.
2014-02-23 21:17:09 -08:00
Chris Robinson c68ce288d0 Move HRTF macros and function declarations to a separate header 2014-02-23 21:11:01 -08:00
Chris Robinson 5731534e8d Move the default hrtf table to an external file 2014-02-23 21:02:11 -08:00
Chris Robinson 9f43de62ab Add a return value to FindHrtfFormat 2014-02-23 20:14:07 -08:00
Chris Robinson ef8002a832 Check the hrtf config option earlier
This is to make sure it tries to find an HRTF-compatible format before reseting
the device, just like when using ALC_HRTF_SOFT.
2014-02-23 20:00:52 -08:00
Chris Robinson e6e3bdee83 Avoid checking the hrtf config option twice 2014-02-23 19:18:54 -08:00
Chris Robinson 0e51becd58 Search system-dependant data paths for relative hrtf table filenames 2014-02-23 17:58:34 -08:00
Chris Robinson 46b2115bfb Avoid using a global static buffer for config loading 2014-02-23 14:07:09 -08:00
Chris Robinson 0626a44eb1 Fix indentation 2014-02-23 09:54:26 -08:00
Chris Robinson 29e0af5ef0 Fix a race condition in the mmdevapi message queue thread 2014-02-09 18:55:51 -08:00
Chris Robinson 09d1319e82 Add some extra traces to the mmdevapi backend
Trying to track down the cause of some reported errors.
2014-02-09 10:52:12 -08:00
Chris Robinson ea0aea6508 Change the autowah to have a cutoff range of 20Hz to 20KHz
This is the same as allowed in the SF2 spec for its filter cutoff generator,
which can be used for a wah effect in MIDI. It makes sense to use a similar
range here.
2014-02-07 03:23:49 -08:00
Chris Robinson 6f711c32ba Fix some types 2014-02-07 03:20:27 -08:00
Chris Robinson 926ecc2dbe Slightly shorten a long line 2014-02-02 16:11:53 -08:00
Chris Robinson 7c4339c195 Rework MIDI clock timing
It's best to avoid using doubles in the mixer since the FPU's set to single-
precision mode. The new clock timing is similar to the device clock timing, and
should hopefully be less prone to drift caused by fp rounding errors.
2014-02-02 02:39:56 -08:00
Chris Robinson 755f161fc5 Use nanosecond resolution for MIDI
This makes the MIDI clock use the same resolution as the device clock and
source latency and the device clock.
2014-02-01 23:01:06 -08:00
Chris Robinson 7bfea9087e Remove an unnecessary special-case for the click removal 2014-02-01 19:31:05 -08:00
Chris Robinson 11a7b3e8f8 Avoid duplicating code in alcGetIntegerv 2014-02-01 18:40:36 -08:00
Chris Robinson 112b0f166f Add an alcGetInteger64vSOFT method and a way to get the device clock 2014-02-01 17:55:42 -08:00
Chris Robinson 69f3a7da6d Keep track of a device clock time, based on the number of samples processed 2014-02-01 16:37:11 -08:00
Chris Robinson 9e2a8843a8 Properly fill the SF2 sample buffer on big endian 2014-02-01 14:33:57 -08:00
Chris Robinson 68a32a3486 Fix an infinite loop when loading SF2 samples on big endian machines 2014-02-01 14:29:47 -08:00
Chris Robinson a4bc0a46e9 Implement dry and wet mixers for Neon
Code provided by Philippe Simons <simons.philippe@gmail.com>.
2014-01-26 01:34:39 -08:00
Chris Robinson 49baa9128d bufferQueue's Clear method doesn't take any parameters 2014-01-19 17:26:51 -08:00
Chris Robinson f193b539ce Clear the OpenSL buffer queue when stopping 2014-01-19 02:45:51 -08:00
Chris Robinson 35fd4dcf77 Check and trace INFO properties when reading them 2014-01-19 02:44:55 -08:00
Chris Robinson 748be9a97d Include sys/sysconf.h if available for sysconf() 2014-01-18 19:16:46 -08:00
Chris Robinson 1a90c7f498 Check for 0-sized sf2 structure arrays 2014-01-17 06:48:40 -08:00
Chris Robinson be883131a7 Remove redundancy in a couple error messages 2014-01-17 06:33:23 -08:00
Chris Robinson 658d35a1b0 Put the version and irom storage into the Soundfont struct 2014-01-17 06:20:39 -08:00
Chris Robinson d387deb3e8 Fix a couple warning messages to show the correct instrument ID 2014-01-17 06:10:29 -08:00
Chris Robinson 7206f5873c Partially handle the irom sub-chunk
Reject soundfonts that have ROM sample types without a valid irom sub-chunk.
2014-01-17 06:03:53 -08:00
Chris Robinson 05e9c824d5 Better handle original key and overriding root key 2014-01-17 05:14:58 -08:00
Chris Robinson 65a5311449 Rename aftertouch to keypressure 2014-01-17 04:18:49 -08:00
Chris Robinson d65279ac2a Update MIDI config option texts 2014-01-17 01:24:18 -08:00
Chris Robinson 50c8d9181a Add a config option to specify the extra MIDI volume scaling
The value specified is in decibels.
2014-01-17 00:58:47 -08:00
Chris Robinson 08ba5d9dba Use a proper struct for envelope properties 2014-01-17 00:36:33 -08:00
Chris Robinson e118ac93a0 Handle fontsound sample types using explicit enum values
Uses AL_MONO_SOFT, AL_RIGHT_SOFT, and AL_LEFT_SOFT. "Linked" samples types
aren't explicitly supported due to being under-defined in the SF2 spec, nor are
ROM samples currently.
2014-01-15 22:18:07 -08:00
Chris Robinson 9ebeae6228 Use ALfontsound_setPropi to set fontsound sample properties
And make sure the loop mode is properly translated for FluidSynth.
2014-01-15 21:17:46 -08:00
Chris Robinson 50b74629dd Add a flag to specify the device being paused
Used to prevent UpdateDeviceParams from restarting the device, if a new context
is created while paused.
2014-01-15 16:44:12 -08:00
Chris Robinson 8d7559d9d0 Stop the device when updating parameters, don't close it 2014-01-15 16:30:23 -08:00
Chris Robinson 56899716d6 Add an extension to pause audio processing 2014-01-15 16:27:17 -08:00
Chris Robinson 443e6b73b2 Use the 64-bit clamp method when getting the MIDI clock time 2014-01-15 15:38:54 -08:00
Chris Robinson 5fdc3c093e Don't use the same buffer segment for enqueueing in OpenSL 2014-01-15 15:27:38 -08:00
Chris Robinson 1739998045 Stop the OpenSL buffer queue when stopping the device 2014-01-15 15:14:25 -08:00
Chris Robinson bdc5850120 Don't reset presets when loading/unloading soundfonts 2014-01-13 16:57:56 -08:00
Chris Robinson 24ffb5b464 Be more thorough when converting sf2 modulator properties 2014-01-13 16:50:12 -08:00
Chris Robinson 90fd92628d Use FluidSynth enums when handling generators 2014-01-13 16:15:56 -08:00
Chris Robinson 0c80f62de7 Add a method to get soundfont samples 2014-01-13 00:20:14 -08:00
Chris Robinson 6f3dac6f58 Allow getting preset properties even when referenced 2014-01-12 23:57:44 -08:00
Chris Robinson e252a100f7 Minor non-functional cleanup 2014-01-12 16:27:21 -08:00
Chris Robinson a5bfc1e30b Add a method to get the current MIDI state 2014-01-12 12:27:46 -08:00
Chris Robinson 9a57aca8d4 Add an option to make HRTFs with a custom head radius 2014-01-11 02:24:20 -08:00
Rupert Steel 2aa95db5b9 Fixed compile error in Visual Studio 2013 by not redefining symbols that are now defined in the VS2013 math.h file. 2014-01-09 10:49:31 -08:00
Chris Robinson da2c9732da Don't break with an override root key generator value of -1
An accidental hold-over from when it was using a switch.
2014-01-06 13:21:08 -08:00
Chris Robinson cf66516a27 Load soundfont modulators into FluidSynth 2014-01-06 13:07:12 -08:00
Chris Robinson 4079ee2200 Add methods to get and set modulator properties on fontsounds 2014-01-05 17:21:34 -08:00
Chris Robinson 5f961a392e Move some soundfont methods to ALsoundfont 2014-01-05 05:11:07 -08:00
Chris Robinson 540a99e71f Allow "deleting" the default soundfont
The ID remains valid and the soundfont will be reloaded as needed, but this
provides a way for the application to clear up the soundfont memory.
2014-01-04 05:30:57 -08:00
Chris Robinson bb54743425 Allow querying default soundfont properties
Any attempts to modify it or its held objects will result in an
AL_INVALID_OPERATION error.
2014-01-04 04:49:10 -08:00
Chris Robinson 86f250a3f5 Trace preset, instrument, and sample names with invalid key and velocity ranges 2014-01-04 04:34:32 -08:00
Chris Robinson 4834f56779 Don't create presets that don't have any usable sounds 2014-01-04 03:20:17 -08:00
Chris Robinson 2dd87946fd Update some trace messages to be more useful 2014-01-03 23:19:52 -08:00
Chris Robinson 355b8db915 Don't allow min key/vel ranges to exceed max range 2014-01-03 23:19:14 -08:00
Chris Robinson 13066bbf87 Allow selecting the default soundfont using ID 0 2014-01-03 22:58:51 -08:00
Chris Robinson 42a67731c4 Add storage for a default soundfont object 2014-01-03 20:21:12 -08:00
Chris Robinson 3a31402b1e Remove an unused method 2014-01-03 19:35:55 -08:00
Chris Robinson d9846f3219 Move ALsoundfont methods to the right source file 2014-01-03 19:18:06 -08:00
Chris Robinson e6e7e461c0 Avoid using alFontsoundiSOFT when loading a soundfont
This should now make the soundfont loader thread-safe.
2014-01-03 18:52:17 -08:00
Chris Robinson 5dc4ea15bb Don't handle the list type in the RiffHdr struct 2014-01-03 18:09:51 -08:00
Chris Robinson f790d0e91f Use a uint map for modulators 2014-01-02 14:43:44 -08:00
Chris Robinson 49c17ad520 Mostly load into fontsound objects directly
This leaves only one unsafe alFontsoundiSOFT call.
2014-01-02 13:53:16 -08:00
Chris Robinson fcf9e72202 Load presets directly into objects 2014-01-02 11:40:30 -08:00
Chris Robinson 5f85a1dcf0 Move some error macros to alError.h 2014-01-02 10:42:00 -08:00
Chris Robinson 01f547b93e Move some preset alloc/dealloc code to separate functions 2014-01-02 10:39:21 -08:00
Chris Robinson 3ff33e2f79 Update the soundfont preset count after loading a soundfont 2014-01-02 09:09:57 -08:00
Chris Robinson e74e3e2b23 Move alLoadSoundfontSOFT to alSoundfont.c 2014-01-02 09:06:28 -08:00
Chris Robinson 7d94e9c52f Don't allow soundfonts with existing presets in alLoadSoundfontSOFT 2014-01-01 07:33:48 -08:00
Chris Robinson 0a5a08c0fb Pass the ALsoundfont object to loadSf2, rather than its ID 2014-01-01 07:25:23 -08:00
Chris Robinson e699ab8614 Force all FluidSynth sounds off when changing soundfonts
Selecting soundfonts should only happen when the MIDI engine is stopped or
reset. When stopped some notes may be left in a release phase, but we need to
ensure all fluid_sample_t objects are finished before unloading since the
soundfont (and associated sample memory) may be deleted at any time afterward.
2013-12-31 00:49:43 -08:00
Chris Robinson 6b2d3c3cfa Remove some unused MIDI backend methods 2013-12-29 06:02:48 -08:00
Chris Robinson 396a2f1595 Don't destroy the fluid_sfont_t if any of its samples are still playing
FluidSynth apparently doesn't check for this itself before calling the free
callback.
2013-12-29 05:58:40 -08:00
Chris Robinson 7640c658ee Remove explicit locking from alMidiSoundfontvSOFT.
The MIDI backend can lock as it needs.
2013-12-29 05:48:30 -08:00
Chris Robinson 37d183efc0 Update soundfont selection functions
A filename is no longer specified, and alMidiSoundfontsSOFT is renamed to
alMidiSoundfontvSOFT.
2013-12-29 05:44:18 -08:00
Chris Robinson 06dd9f9b8c Lock the device while selecting soundfonts 2013-12-29 05:36:05 -08:00
Chris Robinson 4e73b95fc0 Add the last remaining "real-time" properties 2013-12-29 05:09:50 -08:00
Chris Robinson 4c35f7c632 Add chorus and reverb send properties 2013-12-29 04:36:01 -08:00
Chris Robinson 15aa12847d Check for negative soundfont indices getting passed to the loader 2013-12-29 04:25:28 -08:00
Chris Robinson 699d84491f Add pan and attenuation properties 2013-12-29 04:24:32 -08:00
Chris Robinson d030e07879 Add tuning properties 2013-12-29 03:43:00 -08:00
Chris Robinson 1d41122372 Add filter properties 2013-12-29 03:18:31 -08:00
Chris Robinson deecbbc55d Add modulation envelope properties 2013-12-29 02:40:12 -08:00
Chris Robinson d9aedc1416 Add a few more pitch properties 2013-12-29 02:25:40 -08:00
Chris Robinson fc928bb75a Add volume envelope properties 2013-12-29 02:14:10 -08:00
Chris Robinson 4659669a74 Fix copy-paste errors in error messages 2013-12-29 00:56:01 -08:00
Chris Robinson 5aa274999c Accumulate with default modulator amounts 2013-12-29 00:52:03 -08:00
Chris Robinson 09665715ed Add support for the fontsound loop mode 2013-12-29 00:34:58 -08:00
Chris Robinson 0d7996409e Ensure the soundfont version is 2.x 2013-12-28 10:34:26 -08:00
Chris Robinson fe55cd6de8 Add alLoadSoundfontSOFT to load a soundfont via callback
Some hefty caveats:
This function is not thread-safe. In particular, the current context affecting
the thread must not be changed, and the provided soundfont must not be
altered while the function is executing. Ideally, this will be fixed.

Error handling is poor. Some RIFF structure errors may not be caught (e.g. sub-
chunks with sizes greater than the parent chunk allows for), and generated AL
errors are ignored, possibly leading to resource leaks in certain situations.
These should be fixed in time.

There is minimal error checking on the provided soundfont ID. It does not
ensure a valid ID has been provided, nor does it ensure the provided soundfont
can be modified. These short-comings should be fixed eventually.

Proper cleanup is the responsibility of the caller. The caller must get the
preset objects from the soundfont before deleting it, and the fontsound objects
from each preset before deleting them, to make sure all resources are properly
deleted.
2013-12-28 09:58:55 -08:00
Chris Robinson 7156f4439c Allow loading multiple soundfonts with FluidSynth 2013-12-27 06:41:57 -08:00
Chris Robinson 0678e3fc2b Create a custom fluid_sfloader_t loader
This allows us to load and use soundfont objects the app specifies (the first
one, at least), rather than having to load from a file. It doesn't sound that
good since modulators and most generators aren't yet available, but this will
steadily improve.
2013-12-27 06:23:07 -08:00
Chris Robinson f85d733f9d Add a method to set and get soundfonts
The main purpose of this is to select soundfonts for playback, eventually,
instead of the existing method that takes a filename.
2013-12-27 02:59:50 -08:00
Chris Robinson 2b772a5607 Allow specifying all 4 sample types
ROM-based types will be specified with a separate property
2013-12-27 01:08:48 -08:00
Chris Robinson 307ac564ca Allow specifying some fontsound sample properties 2013-12-26 23:37:22 -08:00
Chris Robinson cc30c5754f Remove the generator list 2013-12-26 22:10:41 -08:00
Chris Robinson d5f687b2fa Allow specifying key and velocity ranges on fontsounds 2013-12-26 21:45:16 -08:00
Chris Robinson 35f9e35aa3 Add methods to set and get sounds on a preset 2013-12-25 22:39:38 -08:00
Chris Robinson b5ae424dbd Store a list of sounds in the preset 2013-12-25 19:50:46 -08:00
Chris Robinson 677b864565 Remove the zone, sample, and instrument object types 2013-12-25 19:35:43 -08:00
Chris Robinson b650ecbc23 Add methods to add generators and modulators to fontsounds 2013-12-25 19:23:34 -08:00
Chris Robinson 94ddc5cb18 Add a new fontsound object type
This is basically a combined preset and intrument zone with sample header.
2013-12-25 19:13:59 -08:00
Chris Robinson cb3f82a4bf Do not allow deleting preset or instrument ID 0 2013-12-24 21:23:51 -08:00
Chris Robinson f7556b827a Allow querying the soundfont sample length and format type 2013-12-24 02:55:11 -08:00
Chris Robinson b0878fe609 Do not accept preset or instrument ID 0 as valid 2013-12-23 23:42:21 -08:00
Chris Robinson c911ca89a3 Unlock the soundfont earlier after switching preset lists 2013-12-23 23:31:47 -08:00
Chris Robinson a43b296c60 Add methods to get and set presets on a soundfont 2013-12-23 04:07:53 -08:00
Chris Robinson d7de86966d Add methods to get and set a preset object's preset and bank numbers 2013-12-23 03:23:42 -08:00
Chris Robinson 0df5eb90ea Make sure key and velocity ranges are the first two generators 2013-12-21 15:28:46 -08:00
Chris Robinson ede3c20880 Free thunk entry in the object destructor 2013-12-20 01:04:38 -08:00
Chris Robinson 5dc3f3626d Better protect against improper alSoundfontSamplesSOFT calls 2013-12-20 00:50:43 -08:00
Chris Robinson d596cf9cf3 Use a different autowah filter range
Still just guesses, but this seems to avoid math problems with low peak gains.
2013-12-20 00:16:23 -08:00
Chris Robinson 1bd828603e Add functions to set a soundfont's sample data 2013-12-19 04:19:03 -08:00
Chris Robinson 03a6bf22a5 Fix compiling without FluidSynth 2013-12-19 02:12:01 -08:00
Chris Robinson 99933fac49 Allow creating instrument objects 2013-12-19 00:57:07 -08:00
Chris Robinson 57491bf14c Call the right delete method when generating presets fails 2013-12-19 00:56:12 -08:00
Chris Robinson 666acb314b Rename some ALsoundfont fields 2013-12-19 00:37:56 -08:00
Chris Robinson 4288d3cd01 Move the soundfont and preset extension functions to separate sources 2013-12-19 00:27:11 -08:00
Chris Robinson 1b5c3495c9 Add methods to create and destroy presets 2013-12-19 00:09:55 -08:00
Chris Robinson 1e536cf7ca Properly initialize and cleanup the soundfont map 2013-12-18 23:21:59 -08:00
Chris Robinson 73fc4592ab Make alIsSoundfontSOFT check soundfont IDs 2013-12-18 23:12:44 -08:00
Chris Robinson 8083fb5be7 Add a new ALsoundfont object type
Includes a basic hierarchy for presets, instruments, samples, zones,
generators, and modulators.
2013-12-18 22:51:53 -08:00
Chris Robinson b9468dc917 Fix header guard and remove duplicate code 2013-12-18 19:17:03 -08:00
Chris Robinson cd90bdd9bd Add an option to use the HRTF time delay measured from the dataset 2013-12-18 12:43:59 -08:00
Chris Robinson 0653680690 Move the base MidiSynth to a separate file 2013-12-17 23:03:34 -08:00
Chris Robinson 0095a59fc9 Move the dummy MIDI handler to a separate file 2013-12-17 22:43:10 -08:00
Chris Robinson ee92b3142d Move FluidSynth to its own file 2013-12-17 22:36:25 -08:00
Chris Robinson ef0341c180 Make MidiSynth's methods non-static 2013-12-17 21:56:28 -08:00
Chris Robinson 7f51569186 Update the midi soundfont config option 2013-12-16 15:05:53 -08:00
Chris Robinson 189d73dd87 Render some MIDI samples even when stopped
Because stopping MIDI playback puts any playing notes into release phase, they
should be rendered. Ideally it could stop rendering once output goes silent.
2013-12-15 21:03:23 -08:00
Chris Robinson f244ef6f82 Use macros for MIDI controller values 2013-12-15 20:15:30 -08:00
Chris Robinson bcc2b9a490 Don't explicitly set reverb or chorus 2013-12-13 16:14:50 -08:00
Chris Robinson 1dd05bac5c Add a alMidiResetSOFT method to reset the synth
Playback is stopped, the queue is flushed, the clock is reset to 0, and the
MIDI system is reset to power-up status.
2013-12-13 14:15:01 -08:00
Chris Robinson a343d78724 Update behavior for alMidiStopSOFT
alMidiStopSOFT now has the following behavior:
* All pending MIDI events up to the current time are processed first, before
the queue is flushed.
* All channels then receive an All Notes Off event so that notes go through
their release phase. Controllers are *NOT* reset.
* Clock time is reset to 0, like before.
2013-12-13 13:40:53 -08:00
Chris Robinson 57eef756c3 Fix retrieving source properties 2013-12-09 13:04:16 -08:00
Chris Robinson 5349b90a48 Fix leak if inserting a sysex event fails 2013-12-08 06:20:44 -08:00
Chris Robinson ac525db845 Fix numeric constant type (double->float) 2013-12-08 05:01:06 -08:00
Chris Robinson a846418964 Rename pthread wrappers used for Windows to althread 2013-12-06 02:12:45 -08:00
Chris Robinson 3f5914e094 Simplify dummy synth processing loop 2013-12-01 01:03:55 -08:00
Chris Robinson 517be463ae Minor comment update 2013-12-01 00:31:39 -08:00
Chris Robinson dc13ce7a0a Use a method to get the default soundfont filename 2013-12-01 00:22:20 -08:00
Chris Robinson c834713526 Add methods to set and get the MIDI gain 2013-11-30 23:47:42 -08:00
Chris Robinson 2633fbfc4b Add a method to check if a file is a soundfont 2013-11-30 20:37:37 -08:00
Chris Robinson e0babed29b Add a workaround for allowing GM2 bank selection with a GM2 On SysEx 2013-11-29 23:57:32 -08:00
Chris Robinson be446366fb Add a method to specifying MIDI SysEx messages 2013-11-29 05:37:45 -08:00
Chris Robinson ea1d7d56d3 Add infrastructure for handling SysEx MIDI events 2013-11-29 04:56:33 -08:00
Chris Robinson 82d4e69da1 Don't force MMA bank selection mode 2013-11-29 00:42:27 -08:00
Chris Robinson f4cf84e72d Combine duplicate soundfont loading code 2013-11-28 05:30:48 -08:00
Chris Robinson 8615a109ce Remove an unneeded MidiSynth field 2013-11-28 05:06:40 -08:00
Chris Robinson f8c68291d3 Add a method to stop MIDI playback
Unlike pausing, this resets the MIDI clock time to 0, clears the existing event
queue, stops all MIDI sounds, and resets MIDI controllers.
2013-11-28 04:52:53 -08:00
Chris Robinson 29a4ac329a Move MidiSynth locking out of the setState method 2013-11-28 04:29:38 -08:00
Chris Robinson 5dad1688d4 Fix building without FluidSynth 2013-11-28 04:21:39 -08:00
Chris Robinson 98b0a3187e Add a config option for specifying the default MIDI soundfont 2013-11-28 04:16:46 -08:00
Chris Robinson 7794e53667 Be a bit safer with MIDI state changes 2013-11-28 03:57:45 -08:00
Chris Robinson bb0207d784 Add a method to load a soundfont 2013-11-28 03:38:17 -08:00
Chris Robinson a3c76c3274 Add an option for FLuidSynth to handle MIDI 2013-11-28 03:08:28 -08:00
Chris Robinson a48f362d28 Start a MIDI interface extension 2013-11-28 01:53:05 -08:00
Chris Robinson 489723723c Cleanups for MidiSynth
Remove an unneeded field, make MidiSynth_getTime public, and properly return a
64-bit time from it.
2013-11-27 19:41:52 -08:00
Chris Robinson 182b08d8bc Create and use a MidiSynth on playback devices 2013-11-27 18:52:56 -08:00
Chris Robinson 5554b8cfc9 Add a MidiSynth interface for devices 2013-11-27 18:45:02 -08:00
Chris Robinson 3a1613253b Add a base MidiSynth struct 2013-11-27 05:12:11 -08:00
Chris Robinson 6a76f50fa3 Ensure UINT64_MAX is defined 2013-11-27 05:09:33 -08:00
Chris Robinson 37fa82f138 Use a 64-bit uint for MIDI event times 2013-11-27 04:33:33 -08:00
Chris Robinson d315feae40 Add a MIDI event queue
In preparation for a MIDI extension.
2013-11-27 03:49:26 -08:00
Chris Robinson 08dfbcfd5c Add min/max/clamp methods for doubles 2013-11-27 00:30:13 -08:00
Chris Robinson d339120145 Move ALfloatBUFFERSIZE to a common location 2013-11-27 00:16:30 -08:00
Chris Robinson 80ea801204 Add a macro for GCC to ensure stack alignment 2013-11-25 17:29:39 -08:00
Chris Robinson 191e8172f0 Use a macro to help condense some repeated code 2013-11-24 18:11:45 -08:00
Chris Robinson b029b77784 Add some missing 'static's 2013-11-24 17:43:20 -08:00
Chris Robinson 4830b1afe7 Try to make sure GCC is providing C99 inline semantics 2013-11-24 17:37:48 -08:00
Chris Robinson 25b9c3d0c1 Apply HRTF coefficient stepping separately 2013-11-10 05:52:22 -08:00
Chris Robinson 4386ee32ce Use one long buffer for the chorus and flanger delay lines 2013-11-10 02:48:03 -08:00
Chris Robinson a603dd55ff Rename lfo_coeff to lfo_scale 2013-11-08 16:54:45 -08:00
Chris Robinson 9228d13333 Use integer modulo for chorus and flanger
Also simplify LFO coefficient calculations.
2013-11-08 16:33:42 -08:00
Chris Robinson bca959f372 Only use the inner loop for the chorus/flanger template method 2013-11-08 05:26:43 -08:00
Chris Robinson d6eff654be Redo the autowah effect
It's now a low-pass filter with a cutoff that varies according to the input
signal amplitude.

There remains issues with apparent feedback in the resonant frequency with high
resonance values. The actual cutoff range for the filter is also a guess.
2013-11-07 17:46:14 -08:00
Chris Robinson e0fecdcaaa Reimplement the compressor to work per-sample 2013-11-06 23:39:41 -08:00
Chris Robinson 414a9edc6e Explicitly define _POSIX_C_SOURCE and _XOPEN_SOURCE
Set them to 200809L and 700 respectively, instead of assuming _GNU_SOURCE will
do it.
2013-11-06 01:41:12 -08:00
Chris Robinson b84f6d100f Set -D_GNU_SOURCE=1 earlier 2013-11-05 22:49:16 -08:00
Chris Robinson 801b7cb059 Include strings.h when available 2013-11-05 13:07:46 -08:00
Chris Robinson 10dbb1bc9b Remove the Lock and Unlock methods from BackendFuncs
All backends that still use the old interface use the default locking methods,
which is also used by the ALCbackend base.
2013-11-04 23:34:18 -08:00
Chris Robinson ca83629e4e Forward some wrapper functions to the base method 2013-11-04 23:19:30 -08:00
Chris Robinson 5874e387a8 Move some inline methods to their appropriate headers 2013-11-04 13:51:19 -08:00
Chris Robinson d3c70e63b4 Use C99 inline in more places 2013-11-04 13:44:46 -08:00
Chris Robinson 551f893ae9 Use C99 inline semantics 2013-11-04 12:12:31 -08:00
Chris Robinson 692ab1085d Only set device functions when needed 2013-11-04 10:52:03 -08:00
Chris Robinson 94aeb5f52e Use a unique backend type for loopback 2013-11-04 10:51:22 -08:00
Chris Robinson 9760a592b2 Rename VCALL and VCALL0 to V and V0 2013-11-02 17:30:28 -07:00
Chris Robinson f868d881d9 Constify some function pointers 2013-11-02 16:58:26 -07:00
Chris Robinson 3d921e0e6f Make backend factory methods static as needed 2013-11-02 16:35:05 -07:00
Chris Robinson a1a3f51be2 Convert the OSS backend to the new interface 2013-11-02 15:42:45 -07:00
Chris Robinson c851e2c611 Set vtables in the constructor 2013-11-02 13:23:20 -07:00
Chris Robinson ddf521eddc Make LockContext and UnlockContext inline again 2013-11-02 12:05:02 -07:00
Chris Robinson 7142e3828f Rename alcGetLatency to ALCdevice_GetLatency 2013-11-02 12:01:58 -07:00
Chris Robinson 426429279b Use the existing ALint64SOFT/ALuint64SOFT types for ALint64/ALuint64 2013-11-01 16:49:05 -07:00
Chris Robinson 7460dd1b08 Prefer long as a 64-bit type instead of long long 2013-10-30 12:29:53 -07:00
Chris Robinson 90a51d7897 Move a macro definition up nearer to where it's used 2013-10-29 23:54:10 -07:00
Chris Robinson bc9c3de969 Use a helper macro for making vtable thunks 2013-10-29 20:13:42 -07:00
Chris Robinson 9f0e49917d Add default handlers for reset, captureSamples, and availableSamples 2013-10-29 15:07:13 -07:00
Chris Robinson 779eb4b82b Convert the PulseAudio backend to the new interface 2013-10-29 14:44:07 -07:00
Chris Robinson 6e43757fed Remove the cmake option to use wine headers 2013-10-29 11:38:37 -07:00
Chris Robinson a507d0aa75 Disable MSVC warning 4098
a.k.a. "'void' function returning a value", caused by returning a void in a
function that returns void. Such as:

void foo() { }
void bar()
{
    return foo();
}

Which can happen due to some generalized macros that generate wrappers.
2013-10-29 11:27:06 -07:00
Chris Robinson d4aa4e16a0 Add a macro to forward methods to a base type 2013-10-29 11:22:18 -07:00
Chris Robinson d2b3615943 Convert the loopback backend to the new interface 2013-10-29 10:38:55 -07:00
Chris Robinson 3437769ba0 Remove a couple unnecessary (and incorrect) parameter names 2013-10-28 22:29:59 -07:00
Chris Robinson 3c65c946d4 Fix capture with the new backend interface 2013-10-28 22:03:54 -07:00
Chris Robinson 16d5d5760c Convert ALSA to the new backend interface 2013-10-28 21:56:14 -07:00
Chris Robinson 7a424ab4bf Don't return a void 2013-10-28 20:23:41 -07:00
Chris Robinson 823782a33f Declare LoadLib functions in one place 2013-10-28 17:48:48 -07:00
Chris Robinson 321644de7c Add a missing compat.h include for portaudio 2013-10-28 17:48:03 -07:00
Chris Robinson a407d57639 Rename the support method to querySupport 2013-10-28 17:23:19 -07:00
Chris Robinson 0a6eff6a89 Add a GNU-specific AL_PRINT
This one makes use of the ', ## __VA_ARGS__' construct to avoid
splitting the output into two *printf calls.
2013-10-28 17:09:52 -07:00
Chris Robinson 6fd857739c Support capture backends with the new interface 2013-10-28 17:04:44 -07:00
Chris Robinson 21f1e54cb9 Create and use a backend wrapper for capture 2013-10-28 14:38:55 -07:00
Chris Robinson f8c95f1e90 Add audio capture methods to ALCbackend 2013-10-28 13:51:55 -07:00
Chris Robinson 8d9fb5109b Move some stuff out of alMain.h 2013-10-28 12:48:13 -07:00
Chris Robinson 20bcb68ad6 Move ALCbackend base stuff to a separate file 2013-10-28 12:30:57 -07:00
Chris Robinson 0617df9b5f Fix a couple casts 2013-10-28 12:12:26 -07:00
Chris Robinson f24cb44781 Move the device mutex to the backend 2013-10-28 12:05:33 -07:00
Chris Robinson 2912d130c2 Separate compatibility declarations 2013-10-28 11:26:26 -07:00
Chris Robinson 034935b2e1 Modify how VCALL is handled
Now instead of specifying the arguments as a third argument to the macro, like
VCALL(object,function,(arg1, arg2));
they are specified separately after the macro, like
VCALL(object,function)(arg1, arg2);

Also, VCALL_NOARGS has been removed in favor of VCALL0, which behaves like
above but expects an empty argument list (a separate macro is needed to work
around preprocessor limitations).
2013-10-28 11:06:04 -07:00
Chris Robinson c1cdd3095b Convert the Null backend to the ALCbackend style 2013-10-28 08:29:19 -07:00
Chris Robinson dc7ed39fa7 Add a backend factory base type 2013-10-28 07:27:35 -07:00
Chris Robinson c8603092d3 Add a default getLatency to ALCbackend
And make sure the backend is properly deleted.
2013-10-28 05:57:07 -07:00
Chris Robinson f065700ef9 Move the lock/unlock methods to the backend 2013-10-28 05:10:28 -07:00
Chris Robinson e54983694b Add missing header to git 2013-10-27 16:37:40 -07:00
Chris Robinson 3ed425d7ef Move the ALCdevice handle to the ALCbackend base 2013-10-27 16:20:47 -07:00
Chris Robinson af8be56f17 Use an ALCbackend object to access playback backends
This is the start of a backend redesign. Currently, a wrapper object is used to
avoid having to redo all the backends at once, but they should slowly be
converted to derive from ALCbackend instead. The ALCbackend interface can
change as needed.
2013-10-27 14:24:55 -07:00
Chris Robinson bf465eb2eb Move SetThreadName to threads.c 2013-10-27 08:32:58 -07:00
Chris Robinson 8ceb800def Rework threading functions 2013-10-27 08:14:13 -07:00
Chris Robinson b9e30f7604 Add a cmake option to disable building only alsoft-config 2013-10-27 07:03:58 -07:00
Chris Robinson f93bfab824 Set a name for the mixer and recording threads 2013-10-27 07:00:44 -07:00
Chris Robinson ff5277f4d7 Add a method to set the running thread's name 2013-10-26 12:39:19 -07:00
Chris Robinson 6617985a4e Link to the correct sdl library for the loopback example 2013-10-26 08:52:07 -07:00
Chris Robinson 54b8690781 Fix a comment 2013-10-26 08:49:37 -07:00
Chris Robinson 959b768911 Use SET_VTABLE1 to set the ALeffect vtables 2013-10-08 18:58:44 -07:00
Chris Robinson a421e515be Use a helper macro for pi*2 2013-10-08 16:31:23 -07:00
Chris Robinson 11365b42c7 Use helper macros to convert between degrees and radians 2013-10-08 12:39:22 -07:00
Chris Robinson b42fcce014 Use inline initialization for effect state factory vtables 2013-10-07 14:49:36 -07:00
Chris Robinson fc31a41473 Constify the effect parameter of effect getters 2013-10-07 12:56:41 -07:00
Chris Robinson b3841653c6 Remove the last of the al_try code 2013-10-07 12:05:39 -07:00
Chris Robinson 32d3bde261 Use inline functions to lookup and remove objects 2013-10-07 11:39:32 -07:00
Chris Robinson c787a9bf74 Remove al_try from alAuxEffectSlot.c 2013-10-07 11:30:11 -07:00
Chris Robinson 4f72da0005 Return only true or false from the source getters and setters 2013-10-07 11:10:49 -07:00
Chris Robinson 69d373db78 Remove al_try from alSource.c 2013-10-07 09:24:50 -07:00
Chris Robinson 0803f1b2f1 Remove al_try from alListener.c 2013-10-07 09:04:52 -07:00
Chris Robinson c080dcae8d Remove al_try from alState.c 2013-10-07 08:50:17 -07:00
Chris Robinson 997f6228de Use the UNUSED macro in the effects 2013-10-07 08:34:54 -07:00
Chris Robinson 32e85d469b Use the UNUSED macro in the backends 2013-10-07 08:00:18 -07:00
Chris Robinson 1518895e15 Use an UNUSED macro instead of void-tagging unused parameters 2013-10-07 07:44:09 -07:00
Chris Robinson 44172f701c Remove a duplicate and unused macro 2013-10-07 07:32:45 -07:00
Chris Robinson e1d5be570e Remove al_try usage from alBuffer.c 2013-10-07 07:06:01 -07:00
Chris Robinson b5fece0381 Add a CMake option to not define the IDs used on Windows
This includes the GUIDs, IIDs, CLSID, and PropertyKeys. It is up to the user
to ensure the appropriate IDs are defined when linked.
2013-10-07 06:36:58 -07:00
Chris Robinson 1461903e0d Add a workaround for KDevelop not recognizing the ALIGN macro 2013-10-07 06:05:42 -07:00
Chris Robinson 8aa237ac23 Fix effect slot struct declarations so KDevelop doesn't barf on them 2013-10-07 05:41:41 -07:00
Chris Robinson 5d014ba85f Start using a simpler method for error handling
This helps avoid the al_try/al_throwerr/al_endtry stuff.
2013-10-06 18:01:01 -07:00
Chris Robinson 79edd2cf3b Use a macro for the silence threshold 2013-10-06 17:25:47 -07:00
Chris Robinson 6425480d40 Don't store the effect slot in SendParams
This makes it much more like DirectParams.
2013-10-06 10:11:01 -07:00
Chris Robinson 8448b94c0b Rename Filter param member to LpFilter 2013-10-06 09:33:02 -07:00
Chris Robinson 9ee3d01f6e Use a simpler U64 macro to make 64-bit constants 2013-10-06 04:21:03 -07:00
Chris Robinson 69e64e1ed2 Add a workaround for KDevelop not recognizing 'restrict' 2013-10-06 03:53:20 -07:00
Chris Robinson ba52ac9bcd Finalize AL_SOFT_deferred_updates 2013-10-05 06:14:04 -07:00
Chris Robinson 43c375716a Only rest as long as needed in the Null renderer 2013-10-05 00:33:56 -07:00
Chris Robinson ead8573aaf Add some more missing enums 2013-10-03 07:57:54 -07:00
Chris Robinson 41175ec84c Implement the Compressor effect 2013-10-03 07:55:12 -07:00
Chris Robinson b9fd3f9eb4 Add missing enum to the list 2013-10-03 07:32:01 -07:00
Chris Robinson 01a5946a2e Compile using -std=c99 when available 2013-10-03 05:45:12 -07:00
Chris Robinson 2e605590a2 Convert autowah.c line-endings 2013-10-03 05:04:39 -07:00
Chris Robinson 764ea95781 Use helpers to set channel gain arrays
Also avoid unnecessary clearing.
2013-10-03 05:02:16 -07:00
Chris Robinson 99fa5911bc Implement the Autowah effect. 2013-10-03 03:37:03 -07:00
Chris Robinson 2f676832bc Avoid a divide-by-0 when distortion edge is 1 2013-09-04 15:46:05 -07:00
Chris Robinson 94884ed04b Use a separate value for the maximum buffer channels
Unlike the device, input buffers are accessed based on channel numbers
instead of enums. This means the maximum number of channels they hold
depends on the number of channels any one format can have, rather than
the total number of recognized channels. Currently, this is 8 for 7.1.
2013-07-23 00:13:15 -07:00
Chris Robinson 2219ce2475 Implement master high-pass filter for EAXREVERB 2013-07-21 22:12:50 -07:00
Chris Robinson 393d0eb138 Limit lower filter gain to -100dB 2013-07-21 18:42:14 -07:00
Chris Robinson 51791e6a54 Remove an unused function 2013-07-21 03:22:14 -07:00
Chris Robinson d85f0c0bb2 Improve the source's low-pass filter response 2013-07-20 22:18:44 -07:00
Chris Robinson 0a95c470b0 Some .ui changes that Qt Designer insists on 2013-07-10 10:53:01 -07:00
Chris Robinson 7ebc1bb69e Use OpenAL_SOURCE_DIR for the CMake module path and declare the project earlier
The minimum CMake version required is also bumped to 2.6. Patch by Andrew West,
allows OpenAL Soft to be properly built as a sub-project.
2013-07-10 10:46:07 -07:00
Chris Robinson 52096234e1 Limit the source step to 10
This means the combination of the buffer frequency, source pitch, and
doppler shift can't exceed 10x the device playback frequency.

This is needed to keep the mixer from starving with a really high
increment, causing small DstBufferSize values that require a lot of
iterations.
2013-07-02 06:57:27 -07:00
Chris Robinson 61c6a38f04 Don't have ../../ as part of the target name 2013-07-02 02:02:57 -07:00
Chris Robinson f23a28e8f3 Disable HRTF remove button when no files selected 2013-06-28 00:08:57 -07:00
Chris Robinson 314f9e2686 Update the period size/count sliders when loading 2013-06-27 23:31:51 -07:00
Chris Robinson 2c7f94e285 Remove the unused menu bar and status bar from alsoft-config 2013-06-27 21:42:38 -07:00
Chris Robinson a375104826 Make sure SDL_sound was found before setting the includes 2013-06-25 03:53:48 -07:00
Chris Robinson 62dd5f33b2 Better handle the INCLUDE_DIRECTORIES property
Older cmake verions (prior to 2.8.8) don't have a per-target
INCLUDE_DIRECTORIES property, so the directories have to be
added using the INCLUDE_DIRECTORIES command.
2013-06-25 03:14:04 -07:00
Chris Robinson 77a2c8bcf0 Check for Qt4 earlier 2013-06-24 18:38:26 -07:00
Chris Robinson d984cec3a1 Properly check that Qt4 4.8+ was found 2013-06-24 17:40:41 -07:00
Chris Robinson 5c7680ec84 Use the COMPILE_DEFINITIONS property instead of DEFINE_SYMBOL 2013-06-24 17:40:03 -07:00
Chris Robinson 18157bc331 Better specify include directories and defines needed for various targets 2013-06-24 16:30:04 -07:00
Chris Robinson 27b66b2008 Specify the required Qt4 version to find_package 2013-06-24 16:28:19 -07:00
Chris Robinson f48c0c9e0a Make sure QtCore and QtGui were found and link with the appropriate libs 2013-06-24 05:02:45 -07:00
Chris Robinson b3f54124bd Require Qt 4.8 or newer for alsoft-config 2013-06-24 04:47:36 -07:00
Chris Robinson eebc6c0419 Move alsoft-config to a separate project file
This to help avoid FindQt4.cmake from polluting the current project with
defines and include directories, applying them to targets that don't use Qt.
2013-06-24 04:27:35 -07:00
Chris Robinson 144059b062 Add a configuration UI application
Not complete, but it's a decent start. Some problems:

* Only some otions are handled (backend-specific options in particular aren't
  handled).

* Does not warn when quitting with unsaved changes.

* Some options are missing tooltips.
2013-06-23 22:47:51 -07:00
Chris Robinson 4017e4a96a Check for MinGW in CMakeLists.txt instead of config.h 2013-06-20 17:17:17 -07:00
Chris Robinson ed0568bb24 Fix a potential infinite loop.
If the first XDG_CONFIG_DIRS entry isn't a proper relative path,
it would never break the loop.
2013-06-19 23:24:06 -07:00
Chris Robinson 160e901381 Handle non-native endian formats with SDL_sound 2013-06-19 22:47:17 -07:00
Chris Robinson a9fac12969 Work around some problems with KDevelop's parser 2013-06-18 14:33:39 -07:00
Chris Robinson 3c90ed95af Look for alsoft.conf in the XDG_CONFIG_DIRS and XDG_CONFIG_HOME directories
This follows the XDG Base Directory Specification. The old files/locations are
still supported, but configs found in XDG_CONFIG_DIRS take precedence over
/etc/openal/alsoft.conf, and a config found in XDG_CONFIG_HOME takes precedence
over $HOME/.alsoftrc.
2013-06-16 22:55:11 -07:00
Chris Robinson 48d5002190 Trace config file names being loaded 2013-06-16 20:43:31 -07:00
Chris Robinson 784e396cc7 Don't split config sections into separate blocks 2013-06-16 19:52:56 -07:00
Chris Robinson 8bd77be522 Improve parsing of the config files
This will also now recognize (and strip) quotation marks from around values.
2013-06-16 18:42:27 -07:00
Chris Robinson ff44ebfd5b Add a loopback extension example 2013-06-16 16:10:21 -07:00
Chris Robinson a5471ce0a4 Better handle spaces between string list entries 2013-06-16 15:44:41 -07:00
Chris Robinson 817ca82e4b Fix input and feedback in the echo effect 2013-06-10 05:57:27 -07:00
Chris Robinson 68898dad1e Don't open the log file with both 'truncate' and 'append' flags 2013-06-07 03:38:09 -07:00
Chris Robinson 024374e0ce Use explicit bit offsets when checking for SSE and SSE2
Clang's cpuid.h doesn't contain the bit_* macros.
2013-06-06 19:30:55 -07:00
Chris Robinson 647398d7c6 Use ALfilterState for the distortion effect filters 2013-06-06 03:24:44 -07:00
Chris Robinson dcefeac6e6 Use a static lib for the common example code 2013-06-05 23:07:33 -07:00
Chris Robinson 9fd8731480 Use SDL_sound for the other examples 2013-06-05 20:12:13 -07:00
Chris Robinson 056fa2a474 Use SDL_sound for the alstream example 2013-06-05 19:50:18 -07:00
Chris Robinson fe5428dc49 Add a custom FindSDL_sound cmake module
The one that comes with CMake is broken, incorrectly setting
SDL_SOUND_LIBRARIES, causing warnings, and not even working if the
project is C only.
2013-06-05 19:21:09 -07:00
Chris Robinson a371de080b Silence some clang warnings 2013-06-05 01:52:49 -07:00
Chris Robinson 0e0f70b288 Avoid a NULL dereference 2013-06-04 17:36:54 -07:00
Chris Robinson 7257aa3ed2 Allow enabling HRTF through a context creation attribute 2013-05-31 19:29:32 -07:00
Chris Robinson 69e0c19767 Start an extension that can query the status of HRTF rendering 2013-05-31 16:03:59 -07:00
Chris Robinson 8f14902290 Add ALC_EXT_DEFAULT_FILTER_ORDER to alext.h 2013-05-31 15:40:02 -07:00
Chris Robinson 2290e08053 Make some example function pointers static 2013-05-30 13:29:50 -07:00
Chris Robinson a7a3d1e194 Avoid an unnecessary loop 2013-05-29 22:34:57 -07:00
Chris Robinson 9c3f1d11f0 Check the right flag for tracing SSE2 support 2013-05-29 21:28:27 -07:00
Chris Robinson 764e3aa496 Fix up the naming convention of effect methods 2013-05-29 11:17:45 -07:00
Chris Robinson e96cc656e9 Use C99's inline instead of __inline 2013-05-28 22:27:07 -07:00
Chris Robinson 48aa1e10d6 Use the high-shelf filter in place of low-pass
They effectively both work to lower (or raise) high frequencies. However, the
high-shelf performs better when gain=1.
2013-05-28 10:57:38 -07:00
Chris Robinson 6556626055 Simplify and fix some filter gain calculations 2013-05-27 21:57:22 -07:00
Chris Robinson 59a38442ac Use ALfilterState for the modulator high pass filter 2013-05-27 21:24:28 -07:00
Chris Robinson 0c7c5327c9 Use ALfilterState for the master echo and reverb filters 2013-05-27 19:55:00 -07:00
Chris Robinson 3e663d7307 Remove an unused method 2013-05-27 19:33:12 -07:00
Chris Robinson feeec34333 Use ALfilterState for the source direct and send filters 2013-05-27 19:14:02 -07:00
Chris Robinson bcf4842eb3 Fix filter coefficient pre-calc
a[0] was getting set to 1 before a[1] and a[2] were normalized. a[0] needs to
be done last.
2013-05-27 17:47:13 -07:00
Chris Robinson b1ae44206f Move ALEQFilter to alFilter.c/h and rename it to ALfilterState 2013-05-27 15:32:02 -07:00
Chris Robinson 0ad71a67d4 Store pointers to the getFactory funcs in the map 2013-05-27 13:41:16 -07:00
Chris Robinson 334a7c1d59 Cleanup the ALeffectStateFactory_create methods
Get rid of the ALeffectStateFactory_create macro, and use the VCALL_NOARGS
helper (requires adding the 'this' factory parameter).
2013-05-27 13:16:17 -07:00
Chris Robinson 8d874512b6 No-op if DELETE_OBJ is given a NULL pointer 2013-05-27 12:07:54 -07:00
Chris Robinson 9e84f38a46 Use generic VCALL[_NOARGS] macros instead of type-specific wrappers 2013-05-27 11:48:29 -07:00
Chris Robinson 06c8442af6 Rename a variable 2013-05-26 17:10:57 -07:00
Chris Robinson 0a4be3470e Pre-apply the a[0] EQ filter coefficient
Saves 20 fp divisions per sample
2013-05-26 17:04:45 -07:00
Chris Robinson 4bf5466f64 Use SET_ERROR_AND_RETURN in a few more places 2013-05-26 16:34:08 -07:00
Chris Robinson 82caba6bbe Use the helper macros in more places 2013-05-26 08:54:34 -07:00
Chris Robinson 8808159d55 Add helper macros to set an error and return, and use it in a few places 2013-05-26 00:01:07 -07:00
Chris Robinson 2759239754 Define the ALeffect typedef with the struct 2013-05-25 22:31:24 -07:00
Chris Robinson d1c5599c8e Use an ALeffectProps union to store the effect properties 2013-05-25 22:07:31 -07:00
Chris Robinson 6571d80540 Use a Delete method for deletable objects 2013-05-25 21:04:00 -07:00
Chris Robinson 2da6caeaa6 Update the Null effect so it can act as a guide to new effects 2013-05-25 17:42:34 -07:00
Chris Robinson e157238ce7 Use vtables for setting effect properties 2013-05-24 23:26:59 -07:00
Chris Robinson 49446c7b32 Set cmake policy CMP0005 and avoid unnecessary escape sequences 2013-05-24 23:25:35 -07:00
Chris Robinson f105a8bb4b Add quotes around an ADD_DEFINITIONS parameter 2013-05-24 10:36:55 -07:00
Chris Robinson 18807e06fe Check for __restrict in a way to make MSVC happy, hopefully 2013-05-24 00:45:25 -07:00
Chris Robinson 357cf72ab3 Move remaining effects to the effects subdir 2013-05-23 21:33:16 -07:00
Chris Robinson 2376683128 Move reverb to the effects subdir 2013-05-23 19:48:24 -07:00
Chris Robinson f667c028e6 Move the AL_EFFECT_NULL state into a separate file 2013-05-23 18:50:07 -07:00
Chris Robinson dbb6da8c47 Reword a couple cmake option names 2013-05-23 17:27:57 -07:00
Chris Robinson 4f068b51e5 Don't explicitly check for fesetround
It's C99 standard with fenv.h, which we already check for.
2013-05-22 23:23:45 -07:00
Chris Robinson be4a4d7468 Use fegetenv/fesetenv to backup and restore the FPU state 2013-05-22 23:05:26 -07:00
Chris Robinson eb15b13ca1 Merge the fesetround FPU handler with the assembly one
The assembly is only needed for SSE since we can't access the intrinsics
without the -msse switch, which itself would cause SSE code to be generated
elsewhere automtically.
2013-05-22 22:21:00 -07:00
Chris Robinson 38a9e642df Check for SSE2 and set the denormals-are-zero bit for mixing if available 2013-05-22 17:04:37 -07:00
Chris Robinson b80efeb87d Set the correct SSE bits for round-to-zero 2013-05-22 16:31:43 -07:00
Chris Robinson 52efb8d7f4 Use restrict instead of RESTRICT 2013-05-22 15:11:39 -07:00
Chris Robinson a6fa4a8bc2 Recognize DSSPEAKER_5POINT1_SURROUND (and 7POINT1) with DSound 2013-05-22 14:25:33 -07:00
Chris Robinson 1bed3cff21 Remove an unnecessary check for vsnprintf 2013-05-22 14:24:30 -07:00
Chris Robinson cdbb0722f6 Avoid using a temp buffer for al_print
It's now using two *printf calls, which unfortuantely means there could be a
race between the two and cause the message to break up if something else tries
to print to the same file. This shouldn't really be a big deal since al_print
isn't used that often, and it now allows for lines of practically unlimited
length.
2013-05-22 03:02:39 -07:00
Chris Robinson 2eb8a520d4 Add casts to silence MSVC 2013-05-22 00:07:23 -07:00
Chris Robinson 0a07ed14c2 Rename DELETE to DELETE_OBJ
Because Windows.
2013-05-21 23:42:40 -07:00
Chris Robinson 604726c639 Add a DELETE macro to help destroy objects 2013-05-21 13:27:27 -07:00
Chris Robinson eaccaa5028 Rename the effect state's Destroy method to Destruct 2013-05-21 13:02:56 -07:00
Chris Robinson a5d94f5d09 Use factories to create and destroy effect states 2013-05-21 12:47:18 -07:00
Chris Robinson af1936be5d Remove some unused code 2013-05-21 07:24:53 -07:00
Chris Robinson e4186f4903 Use a properly-defined history for the FILTER struct 2013-05-21 07:10:24 -07:00
Chris Robinson 3ee0f8feb9 Avoid storing the device frequency in the equalizer 2013-05-21 05:33:54 -07:00
Chris Robinson 5c8c40afef Auto-generate wrappers to upcast objects before calling user methods 2013-05-21 05:02:25 -07:00
Chris Robinson 5516d8df0b Use macros to help define vtables for effect states 2013-05-21 04:18:02 -07:00
Chris Robinson fba9ac6db1 Avoid storing the device frequency in the distortion state 2013-05-21 02:43:59 -07:00
Chris Robinson de387c853f The effect state being destroyed can't be NULL 2013-05-21 02:37:27 -07:00
Chris Robinson 44da54ec7f Rename some inheritance macros 2013-05-21 02:30:11 -07:00
Chris Robinson b6da74b6e2 Pre-scale the chorus and flanger delays to be in samples 2013-05-21 01:59:44 -07:00
Chris Robinson 7e9960f7f1 Avoid storing some chorus and flanger properties in the effect state 2013-05-21 01:38:43 -07:00
Chris Robinson c6872d4d86 Process 64 samples at a time for chorus and flanger effects 2013-05-20 04:41:03 -07:00
Chris Robinson 5b706f3bdc Process 64 samples at a time for some effects
This should help with the non-interleaved samples of the output, and
allow skipping channels that don't contribute to the output.
2013-05-20 04:16:48 -07:00
Chris Robinson 80459b13e4 Split flanger delay calculation into separate methods 2013-05-20 03:41:32 -07:00
Chris Robinson 012fbc36b9 Split chorus delay calculation into separate methods 2013-05-20 02:38:06 -07:00
Chris Robinson 9dae98071f Fix possible overflow when converting float to int
Same as with the mixer, we can only use 25 bits of precision from
floats.
2013-05-20 01:34:14 -07:00
Chris Robinson 4c436b106d Use some macros to help with deriving types 2013-05-20 01:32:02 -07:00
Chris Robinson 1c523df160 Remove an unused macro 2013-05-20 00:14:36 -07:00
Chris Robinson 6aa722808e Clear all the old channel gains to 0 before setting them 2013-05-20 00:02:47 -07:00
Chris Robinson c693e649e6 Fix a potential leak when mmdevapi fails to open 2013-05-19 03:19:19 -07:00
Chris Robinson 1976d64814 Avoid recalculating the same coefficient 2013-05-19 03:18:21 -07:00
Chris Robinson 78e7c1c27b Implement distortion and equalizer effects
Code provided by Mike Gorchak
2013-05-18 01:33:01 -07:00
Chris Robinson a7ad6080f0 Modulator fixes
Modulator functions return 0..1 instead of -1..+1
Apply high-pass filter before modulation
Increase fractional precision to 24 bits from 16

Thanks to Mike Gorchak
2013-05-18 00:43:09 -07:00
Chris Robinson 563f16dc2a Fix overflow issues when converting float to int
32-bit floats can only reach between -16777215...+16777215 (25 bits) before it
starts losing whole-number accuracy. Some compiler optimizations may break
trying to multiply a float by 2147483647.0 when the result is given right to a
float parameter, causing it instead to multiply by the nearest representable
float value, 2147483648.0.
2013-04-22 00:52:50 -07:00
Chris Robinson 43b406ad9b Simplify al_try code 2013-03-24 13:55:41 -07:00
Chris Robinson dd48375bd6 QSA is always build-time linked 2013-03-24 12:45:05 -07:00
Chris Robinson 678ce4109c Reorder some switch cases 2013-03-24 12:43:53 -07:00
Chris Robinson b313c881c0 Add a missing cast to silence MSVC 2013-03-22 11:21:21 -07:00
Chris Robinson 2c501f3b28 Document QSA backend options, and move PulseAudio's up 2013-03-22 11:19:50 -07:00
Chris Robinson e725db4480 Add win64build to .gitignore 2013-03-19 08:36:06 -07:00
Chris Robinson 23703ff40f Avoid an unnecessary size variable 2013-03-19 07:38:42 -07:00
Chris Robinson fb11eb1190 Avoid explicitly checking each effect to create 2013-03-19 05:39:34 -07:00
Chris Robinson 061b75b343 Fix MMDevApi backend option text 2013-03-19 05:05:00 -07:00
Chris Robinson 6bf8f01af5 Report the actual ALSA error if setting the buffer metrics fails 2013-03-18 05:21:41 -07:00
Chris Robinson bf54de4984 Use less math to clamp floats to -1...+1
The previous code could have issues as precision lowers. This should hopefully
work better while only using one if check instead of two.
2013-03-17 09:07:55 -07:00
Chris Robinson eb1080910e Use force_align_arg_pointer for QSA 2013-03-15 04:32:46 -07:00
Chris Robinson d237410d1d Add a QSA backend for QNX 2013-03-14 01:29:20 -07:00
Chris Robinson 2486f13dae Remove unnecessary NULL checks 2013-03-13 23:36:45 -07:00
Chris Robinson 3fd0f23e48 Add Chorus and Flanger effects
Code provided by Mike Gorchak
2013-03-13 23:31:12 -07:00
Chris Robinson 991aba286f Move the effect-specific get/set methods to where the effect is implemented 2013-03-13 21:53:42 -07:00
Chris Robinson a3846ba53b Don't use a custom CheckCCompilerFlag.cmake 2013-03-13 21:29:47 -07:00
Chris Robinson 0423caeb8c Use CHECK_SYMBOL_EXISTS to check for pthread_setschedparam 2013-03-13 21:20:00 -07:00
Chris Robinson f1b0f67553 Build an appropriate channel map for PulseAudio 2013-02-08 18:59:48 -08:00
Chris Robinson 89fae876a4 Only print the filename in alstream, without the path 2013-02-03 04:55:44 -08:00
Chris Robinson ac4930e233 Check the correct CMake CoreAudio option 2013-02-03 04:43:49 -08:00
Chris Robinson e358882f14 Destroy the ring buffer when closing down a portaudio device 2013-02-01 18:38:44 -08:00
Chris Robinson 564a182259 Avoid an underflow in the SSE mixers if BufferSize is less than 4 2012-12-24 05:49:44 -08:00
Chris Robinson 9cd086a707 Filter unneeded thread messages with MMDevApi 2012-12-24 05:37:53 -08:00
Chris Robinson c2adea1999 Make the context's LastError volatile 2012-12-24 05:32:15 -08:00
Chris Robinson 7d81d6b24e Look for /arch:SSE with MSVC for SSE 2012-12-24 05:26:33 -08:00
Chris Robinson 84944af142 Rename cmake options so GUIs order them better 2012-12-24 05:20:04 -08:00
Chris Robinson be25e6802d Add support for OpenSL ES 1.0.1 on Android 2012-12-12 14:36:58 -08:00
Chris Robinson 9b6a226da5 Release 1.15.1 2012-12-11 12:35:55 -08:00
Chris Robinson eb5073a8a7 Properly convert uint values when getting them as int64 2012-12-07 18:43:13 -08:00
Chris Robinson beb84bdc21 SetSourceiv should never get AL_SAMPLE_OFFSET_LATENCY_SOFT 2012-12-06 09:03:48 -08:00
Chris Robinson a20f1fa779 Use a helper to count the number of float/double values for a property 2012-12-05 20:51:25 -08:00
Chris Robinson 7445ffe1a6 Use a helper to count the number of int(64) values for a property 2012-12-05 19:58:01 -08:00
Chris Robinson c225c59b0a Remove 'v' from the source enum names 2012-12-05 18:27:05 -08:00
Chris Robinson 37a6705556 Add a comment to note the extension for sfvDistanceModel 2012-12-05 13:48:33 -08:00
Chris Robinson ed5738bfa4 Use an enum to handle int source properties 2012-12-05 09:55:05 -08:00
Chris Robinson 5e14a83030 Use an enum to handle float source properties 2012-12-05 09:22:38 -08:00
Chris Robinson 3b9b176f14 Print an error if trying to handle a missed source property 2012-12-05 08:29:38 -08:00
Chris Robinson 838f0fd71b Fix retrieving source gain 2012-12-05 08:27:02 -08:00
Chris Robinson fd83e006ec Release 1.15 2012-12-04 13:49:16 -08:00
Chris Robinson e51574c764 Trace the list of supported backends 2012-12-04 13:46:51 -08:00
Chris Robinson 893a1bedbc Use +/-90 degrees for stereo sources with non-HRTF stereo output. 2012-12-04 05:30:06 -08:00
Chris Robinson 4621a7b426 Add support for float32 output to CoreAudio 2012-12-03 11:50:02 -08:00
Chris Robinson 4808313e70 Use the correct multiplier for int32 output with CoreAudio 2012-12-03 10:44:02 -08:00
Chris Robinson 1fb9311d82 Lock the device before calling aluHandleDisconnect
PulseAudio causes an assert if being relocked inside a callback on the worker
thread, where aluHandleDisconnect is called. We can assume it's already locked
there, so just make sure the device is locked before being calling it.
2012-12-02 11:30:23 -08:00
Chris Robinson dd34daed42 Trace the opened device name in alcCaptureOpenDevice 2012-12-02 06:37:34 -08:00
Chris Robinson 830428fb06 Use WARN when implicitly deleting resources with the context 2012-12-02 01:17:26 -08:00
Chris Robinson 0ca7ab2cf8 Explicitly check for malloc.h 2012-11-25 19:20:35 -08:00
Chris Robinson a0c39989f5 malloc.h isn't standard, and is only needed for _aligned_malloc 2012-11-25 02:33:20 -08:00
Chris Robinson 209d09ae13 Avoid locking PulseAudio's mainloop when starting and stopping capture
The mainloop is already locked by the caller, and double-locking interferes
with the subsequent wait.
2012-11-19 16:50:03 -08:00
Chris Robinson 0c030f7d9c Fix a comment describing the sinc function 2012-11-18 19:02:50 -08:00
Chris Robinson 2d800e99e2 Fix off-by-one error in the makehrtf dither and add a resample option
Also fixes some size_t type warnings
2012-11-18 09:29:58 -08:00
Chris Robinson 0518ecca14 Avoid storing the SSE state if it's not supported 2012-11-13 15:20:36 -08:00
Chris Robinson 43b9027611 Return the integer from fastf2i with MSVC 2012-11-13 15:12:46 -08:00
Chris Robinson 40ea4f4b46 Use int instead of long for the IMA4 tables 2012-11-10 03:40:36 -08:00
Chris Robinson 4f624cb734 Load Int and UInt samples as float with alBufferData
This shouldn't really ever happen, as there's no 32-bit (u)int formats
alBufferData can take.
2012-11-10 03:36:37 -08:00
Chris Robinson b03013d6a7 Specify that the config file being installed is a sample 2012-11-06 03:52:41 -08:00
Chris Robinson 9e79d144de Install the config sample by default 2012-11-05 04:20:15 -08:00
Chris Robinson 40b3f60cde Install the config example into share/openal instead of /etc/openal
/etc/openal/alsoft.conf is still used for configuration, only the sample's
install placement is changed.
2012-11-05 04:14:05 -08:00
Chris Robinson 2b27d243ca Use stdout to print alstream information 2012-11-04 22:37:10 -08:00
Chris Robinson 2f07c46e20 Include standard headers before conditional ones 2012-11-04 22:36:34 -08:00
Chris Robinson 3da1b540fd Use the stored buffer metrics for the mmdevapi buffer size 2012-11-04 22:35:35 -08:00
Chris Robinson 92dde81296 Add rudimentary latency tracking for mmdevapi
This won't be as granular as it could be, since it only updates when the wakeup
event trips (which may or may not happen more often than OpenAL's mix updates).
A more correct method would be to query GetCurrentPadding directly, but that
would require sending a message to the processing thread and waiting for a
reply, since we can't guarantee COM on the calling thread.
2012-11-04 08:35:39 -08:00
Chris Robinson e2368eb960 Use an array to specify the offset for each channel of the device buffer
This effectively inverts the DevChannels array
2012-11-04 04:41:11 -08:00
Chris Robinson 3e71a7cb94 Rename some struct fields for DirectSound 2012-11-04 01:18:39 -08:00
Chris Robinson b92ad830ef Avoid recreating the DSound primary buffer 2012-11-04 00:37:31 -07:00
Chris Robinson fc7adccd6f Fix retrieving the source's write offset 2012-11-01 23:41:18 -07:00
Chris Robinson 97bf718d0e Pass the offset latency properties to the set handler 2012-11-01 18:35:20 -07:00
Chris Robinson eb6360e1f8 Set the proper error when trying to set AL_SAMPLE_OFFSET_LATENCY_SOFT or AL_SEC_OFFSET_LATENCY_SOFT 2012-11-01 00:16:44 -07:00
Chris Robinson 41b1ba146a Add an example to get and show the source latency 2012-10-31 23:53:10 -07:00
Chris Robinson 6450d0cbca Avoid prefixing function pointers in the streaming example 2012-10-31 22:38:27 -07:00
Chris Robinson 4e281350a0 Print the name of the device when opening it in the example apps 2012-10-31 17:16:56 -07:00
Chris Robinson e868dad61c Make a decimal value a float type 2012-10-31 06:58:16 -07:00
Chris Robinson 4a3d36a176 Finalize AL_SOFT_source_latency 2012-10-31 05:09:42 -07:00
Chris Robinson f3dddb6e63 Add an example program showing how to apply reverb to a source 2012-10-31 04:04:49 -07:00
Chris Robinson 3c3e3b123b Add a couple comments to the code 2012-10-26 20:11:59 -07:00
Chris Robinson f02a9935c6 Avoid branching when clamping and converting float samples to integer 2012-10-26 15:31:14 -07:00
Chris Robinson 3711735ed9 Add the AL subdir to the list of include flags in the pkg-config file
This allows apps to include "al.h", "alc.h", etc, like with Creative's SDK and
CMake's FindOpenAL module, or as "AL/al.h" and "AL/alc.h" like before.
2012-10-26 00:02:39 -07:00
Chris Robinson 2c802cc10a Reorder and comment some function in alhelpers.c 2012-10-25 23:53:46 -07:00
Chris Robinson e2e502970c Move some math functions to where they're used 2012-10-25 17:24:22 -07:00
Chris Robinson 404d601666 Add a couple missing RESTRICTs 2012-10-25 17:02:21 -07:00
Chris Robinson 252fc40f78 Fix some types 2012-10-25 15:37:52 -07:00
Chris Robinson fb21a73b4e Add the RESTRICT keyword to a function pointer definition's parameters 2012-10-25 14:52:09 -07:00
Chris Robinson 138a4c6d92 Add explicit casts for MSVC 2012-10-25 14:46:27 -07:00
Chris Robinson c802bfccef Remove the float math wrapper functions 2012-10-25 14:16:35 -07:00
Chris Robinson fae4959db3 Add retrieval of missing source properties 2012-10-21 11:36:27 -07:00
Chris Robinson 8a67e5cfa2 Use an asterisk to denote a requested format option in the trace 2012-10-16 07:46:04 -07:00
Chris Robinson 617def3ef0 Require an alignment declaration for SSE support 2012-10-15 02:23:08 -07:00
Chris Robinson 4f5aafe59f Put the HRTF DirectParams into an anonymous struct 2012-10-15 01:37:37 -07:00
Chris Robinson a14383d6cc Constify the direct and send parameters given to the mixer 2012-10-15 01:31:58 -07:00
Chris Robinson fe1903fb56 Use a separate step to deinterlace reverb samples 2012-10-14 11:55:03 -07:00
Chris Robinson f0d27e5f91 Remove the unused Device parameter 2012-10-14 11:29:28 -07:00
Chris Robinson a852669dce Store the output buffers in the DirectParams struct 2012-10-14 11:21:52 -07:00
Chris Robinson 3240894cd5 Remove the now-unused Source parameter from the DryMix methods 2012-10-14 11:04:36 -07:00
Chris Robinson 0b679167c9 Store some more HRTF info in the DirectParams struct 2012-10-14 10:57:11 -07:00
Chris Robinson 5ac0a93fba Mark some parameters as const and/or RESTRICT 2012-10-14 10:27:15 -07:00
Chris Robinson f7655d44a2 Move alhelpers and alffmpeg code to a common sub-directory 2012-10-14 01:55:39 -07:00
Chris Robinson 38e6bfb702 Use a helper method to convert i64 values to the proper types 2012-10-14 01:41:49 -07:00
Chris Robinson 20ede6de59 Check i64 property ranges before passing them to the int handlers
Note that some properties (e.g. AL_BUFFER, AL_DIRECT_FILTER) actually take
unsigned int values, and so are checked against the unsigned range even though
they eventually get casted to an int. The int handler casts them back as
needed.
2012-10-14 00:55:46 -07:00
Chris Robinson 5c70e6b9ab Don't add the dry gain to the mixing matrix 2012-10-13 11:10:58 -07:00
Chris Robinson 9bd3b88a58 Silence an implicit int-to-float conversion warning on MSVC 2012-10-13 11:00:43 -07:00
Chris Robinson e60281c927 Don't use a loop to determine the next power of 2 2012-10-13 08:58:48 -07:00
Chris Robinson 01136ecd51 List the in-progress AL_SOFT_source_latency extension 2012-10-13 00:59:46 -07:00
Chris Robinson 1212523470 Implement the double and int64 source setters
Note that currently the int64 setters do not range check before being passed to
the int setters, erroneously chopping off the upper bits.
2012-10-13 00:56:39 -07:00
Chris Robinson 73105b2d4b Fix copying the listener Up vector 2012-10-12 23:35:00 -07:00
Chris Robinson 06ab9cf70e Use the enum to set the default distance model 2012-10-12 23:28:51 -07:00
Chris Robinson a3d4868ef4 Initialize the listener velocity parameter 2012-10-12 08:01:02 -07:00
Chris Robinson 815b03855b Apply the listener matrix to the listener velocity once 2012-10-12 07:56:37 -07:00
Chris Robinson 6750bbb1d7 Avoid needlessly copying the listener matrix 2012-10-12 07:45:20 -07:00
Chris Robinson 43c8c4fa29 Apply the listener position to the transformation matrix 2012-10-12 07:38:29 -07:00
Chris Robinson 25b8a95987 Check the distance against epsilon to determine if it matches 2012-10-12 07:10:51 -07:00
Chris Robinson 99fcd6f816 Properly scale wide-stereo sources 2012-10-10 04:08:35 -07:00
Chris Robinson 513c6db0d5 Avoid mixing to a send slot if the gain is too low 2012-10-10 03:52:45 -07:00
Chris Robinson 1b7f01b7cc The max step is no longer dependent on the channel count 2012-10-10 03:40:01 -07:00
Chris Robinson a4f21aaa64 Set Hrtf.Moving after setting the source params instead of after mixing 2012-10-09 06:54:21 -07:00
Chris Robinson 39bc2ba65c Build the listener matrix separately 2012-10-09 06:19:36 -07:00
Chris Robinson d598f82722 Don't include alListener.h from alMain.h 2012-10-09 04:48:12 -07:00
Chris Robinson c22408cb2a Store a pointer to the listener in the context 2012-10-09 04:44:27 -07:00
Chris Robinson 8c3419ed48 Define RESTRICT in config.h instead of alMain.h 2012-10-09 01:46:02 -07:00
Chris Robinson a98361ebbe Don't assume x86 or x64 to use IsProcessorFeaturePresent 2012-10-08 00:43:51 -07:00
Chris Robinson 999f0783c5 Fix a type conversion warning 2012-10-08 00:32:35 -07:00
Chris Robinson 2a6dd2f243 Fix a type mismatch 2012-10-07 08:19:09 -07:00
Chris Robinson f3349221c6 Add missing include 2012-10-07 08:18:53 -07:00
Chris Robinson 65c7c468cc Use IsProcessorFeaturePresent to detect SSE on Windows 2012-10-07 08:18:22 -07:00
Chris Robinson ebdf636759 Revert "Use __cpuid from intrin.h in Windows when available"
This reverts commit 6b870714a9.
2012-10-07 08:08:11 -07:00
Chris Robinson 6b870714a9 Use __cpuid from intrin.h in Windows when available 2012-10-07 05:36:25 -07:00
Chris Robinson 708f3ccc3d Add trace, warn, and error markers to logged output 2012-10-07 04:31:31 -07:00
Chris Robinson 84f4afa5f2 Lock the device using the pulseaudio main loop 2012-10-07 02:25:59 -07:00
Chris Robinson 991e21a712 Avoid unnecessarily locking the list lock 2012-10-07 00:13:23 -07:00
Chris Robinson 1a9cf7ab80 Shorten format trace messages 2012-10-06 23:32:36 -07:00
Chris Robinson 9501f1cb9e Define WIN32_LEAN_AND_MEAN when including windows.h in alMain.h 2012-10-06 21:36:15 -07:00
Chris Robinson d52cb3708b Include windows.h before cpuid.h to avoid __cpuid clashes 2012-10-06 21:30:03 -07:00
Chris Robinson fa8d374613 Rename a couple functions
Since they don't use the stack anymore.
2012-10-05 06:44:55 -07:00
Chris Robinson 0d26b0dcc2 Put temporary storage in the device instead of on the stack 2012-10-05 06:42:26 -07:00
Chris Robinson a6287fd407 Add a special resampler for matching sample rates 2012-10-05 06:03:19 -07:00
Chris Robinson 16bdf79d4c Round up the device's default slot alignment 2012-10-05 00:32:41 -07:00
Chris Robinson 0b47844606 Fix potential buffer overflow with temp sample space 2012-10-05 00:27:29 -07:00
Chris Robinson 468759e6ab Scale and round NumUpdates when PulseAudio changes the sample rate 2012-10-04 23:36:59 -07:00
Chris Robinson f138774d78 Avoid void* arithmetic 2012-10-04 06:16:16 -07:00
Chris Robinson 068da3fe83 Make sure the output buffer pointer is updated in case multiple iterations are needed 2012-10-03 02:35:43 -07:00
Chris Robinson 29b11db13f Update the example to use avcodec_decode_audio4 2012-10-03 02:01:52 -07:00
Chris Robinson dca6254b35 Update some ffmpeg functions 2012-10-03 00:29:08 -07:00
Chris Robinson cccec40e87 Fix some signed/unsigned comparisons 2012-10-02 18:55:39 -07:00
Chris Robinson b5d9d8e005 Reset maxlength if PulseAudio updates the playback rate 2012-10-02 17:01:35 -07:00
Chris Robinson 825c5b5282 Use lrintf to fast convert floats to ints when possible 2012-09-28 04:20:55 -07:00
Chris Robinson 25ca179389 Decrease the default BUFFERSIZE to 2048 2012-09-28 03:30:57 -07:00
Chris Robinson 8bd0592e3f Combine early reflections and late reverb in VerbPass 2012-09-27 17:30:35 -07:00
Chris Robinson 06a0d1b86e Use an unsigned type for the sample position fraction 2012-09-27 13:52:45 -07:00
Chris Robinson 41b2c2f3bb Remove an unneeded parameter from the resampler 2012-09-27 02:46:15 -07:00
Chris Robinson 68faef5b84 Ensure the device's default effect slot is properly aligned 2012-09-26 18:21:03 -07:00
Chris Robinson abf1309fb6 Update a comment 2012-09-26 18:04:22 -07:00
Chris Robinson d3891af36e Ensure effect slots are aligned 2012-09-26 18:00:38 -07:00
Chris Robinson ed82194260 Deinterlace audio when loading it into the stack.
It may be better to deinterlace it on load into the buffer, though I don't
imagine this will affect much. It could be nice for consistency's sake, though
it would also complicate things if we ever want to support direct access to
buffer data.
2012-09-26 17:16:25 -07:00
Chris Robinson 012e61b105 Round when calculating NumUpdates from PulseAudio's minreq 2012-09-25 11:32:49 -07:00
Chris Robinson d19d7321d9 Remove an unnecessary include 2012-09-24 20:39:12 -07:00
Chris Robinson d4e82d441e Combine multiple loops into one 2012-09-24 19:07:07 -07:00
Chris Robinson 2ceea21f76 Avoid mixing silence to output
"Silence" being less than -100dB.
2012-09-24 15:33:44 -07:00
Chris Robinson 12327da4fe Remove SSE resamplers. They aren't gaining us much this way. 2012-09-24 14:47:06 -07:00
Chris Robinson 52d403b0d1 Make sure PulseAudio sets an update size that's a multiple of 4 samples with SSE 2012-09-20 15:01:49 -07:00
Chris Robinson ccd35bffe7 Help ensure the update size is a multiple of 4 with SSE 2012-09-20 14:54:28 -07:00
Chris Robinson d1c93525ed Fix matrix multiply used by the SSE cubic resampler
Also remove the 4-sample loop. It's not terribly effective.
2012-09-18 18:09:43 -07:00
Chris Robinson f0732426f2 Precision control bits don't exist with SSE 2012-09-18 15:08:36 -07:00
Chris Robinson eedbd1e81c Improve a couple error traces with the HRTF loader 2012-09-18 12:15:43 -07:00
Chris Robinson 6005e7f27f fpu_control.h is no longer used 2012-09-18 11:28:47 -07:00
Chris Robinson e779e64c95 Win64 doesn't allow _controlfp or __control87_2 to set the precision control bits 2012-09-18 11:20:48 -07:00
Chris Robinson 3baaf99a6e Fix a typo in a comment 2012-09-17 07:48:57 -07:00
Chris Robinson 4a86983a6f Avoid correcting for more non-existing clicks 2012-09-16 09:04:37 -07:00
Chris Robinson c19d9d3452 Don't try to correct for non-existing clicks. 2012-09-16 08:42:36 -07:00
Chris Robinson 79b6d89b3f Explicitly give the wet buffer 1 channel 2012-09-16 08:27:50 -07:00
Chris Robinson 535e8fd0f7 Check the proper value for setting WetPendingClicks 2012-09-16 08:15:03 -07:00
Chris Robinson a240abb6c8 Avoid building redundant mixers 2012-09-16 08:14:26 -07:00
Chris Robinson dd9d30e248 Implement an SSE MixSend method 2012-09-16 06:19:39 -07:00
Chris Robinson 95535ce99e Properly restore the SSE control word with __control87_2 2012-09-16 06:01:48 -07:00
Chris Robinson df2e82da78 Properly handle the SSE control word 2012-09-16 05:35:38 -07:00
Chris Robinson a1daec66e2 Use __control87_2 when available 2012-09-16 01:44:45 -07:00
Chris Robinson 657ee85136 Use a struct to store the FPU mode 2012-09-16 01:35:16 -07:00
Chris Robinson 965306b296 Add missing source property retrievals 2012-09-14 09:02:36 -07:00
Chris Robinson 28086f6cb7 Implement an SSE cubic resampler 2012-09-14 07:01:58 -07:00
Chris Robinson 45bb010b28 Shorten a warning 2012-09-14 05:05:20 -07:00
Chris Robinson 90e0c74721 Implement an SSE linear resampler 2012-09-14 04:48:53 -07:00
Chris Robinson a20341ac2a Ensure the ResampledBuffer is aligned 2012-09-14 04:16:49 -07:00
Chris Robinson 7635afcb52 Use a source param for the resampler and move them to the mixer source 2012-09-14 04:13:18 -07:00
Chris Robinson 74aee374a6 Remove an unused header 2012-09-14 03:10:12 -07:00
Chris Robinson 4f70766b95 Move a couple macros to more appropriate headers 2012-09-14 02:52:37 -07:00
Chris Robinson 9f58edd7a8 Fix up some more header includes 2012-09-14 02:42:36 -07:00
Chris Robinson 0f3a575a09 Don't include alu.h in alMain.h 2012-09-14 02:14:29 -07:00
Chris Robinson ec74fb9ba2 Enable flush-to-zero mode when possible 2012-09-14 01:10:19 -07:00
Chris Robinson ff809f2271 Move a couple methods to where they're used 2012-09-12 07:57:50 -07:00
Chris Robinson 41f52fdd6a Restore support for the previous mhr format 2012-09-12 07:25:05 -07:00
Chris Robinson aa827790bd Add a note about the hrtf_tables filename markers 2012-09-12 05:34:42 -07:00
Chris Robinson 38e7bd5359 Properly scale the HRTF delays when writing them 2012-09-12 05:11:53 -07:00
Chris Robinson 613ce3f976 Update fnamelist after handling % markers 2012-09-12 04:55:03 -07:00
Chris Robinson ce8a0a9fd5 Use PATH_MAX for the fname length 2012-09-12 03:45:26 -07:00
Chris Robinson f59e4ba7c2 Improve parsing hrtf_tables filenames 2012-09-12 03:41:41 -07:00
Chris Robinson df469bfdea Fix HRTF data format description 2012-09-11 19:55:45 -07:00
Chris Robinson f4642953d4 Warn about SSE performance with non-multiple-of-4 update sizes 2012-09-11 11:17:41 -07:00
Chris Robinson 88b0c30152 Use the correct size for defaultCoeffs 2012-09-11 10:43:53 -07:00
Chris Robinson 01183ad93b Minor cleanups for variable declarations 2012-09-11 09:40:08 -07:00
Chris Robinson 3f97af4c8c Don't use count-specific functions to write to the output buffer 2012-09-11 06:41:24 -07:00
Chris Robinson 4c5cb2189b Use a non-interleaved DryBuffer 2012-09-11 06:32:42 -07:00
Chris Robinson 98ff6f990a Do the filtering separately from the mixing 2012-09-11 05:56:19 -07:00
Chris Robinson aaa81e00b7 Use HUGE_VAL instead of dividing by 0 for infinity, where possible 2012-09-11 04:45:43 -07:00
Chris Robinson 1c44a96768 Silence a couple MSVC warnings 2012-09-11 03:29:00 -07:00
Chris Robinson 34fef456c8 Add example HRTF definition files for makehrtf 2012-09-11 03:19:03 -07:00
Chris Robinson 7e81918f7b Update HRTF code
This update allows for much more flexibility in the HRTF data. It also allows
for HRTF table file names to include "%r" to represent the device's playback
rate (e.g. if you set hrtf-%r.mhr, then it will try to use hrtf-44100.mhr or
hrtf-48000.mhr depending if the device's output rate is 44100 or 48000,
respectively).

The makehrtf utility has also been updated to support more options and input
file formats, as well as the new mhr format.
2012-09-11 02:11:51 -07:00
Chris Robinson 1b840a3db8 Again try to get _WIN32_IE to behave right with MinGW 2012-09-11 01:39:05 -07:00
Chris Robinson 4e440fff62 Remove a hack for MinGW 2012-09-10 00:19:13 -07:00
Chris Robinson 81208acd6e Add missing returns 2012-09-09 21:27:54 -07:00
Chris Robinson af3b5c23c8 Try to ensure mixing samples in multiples of 4 2012-09-09 04:27:47 -07:00
Chris Robinson 9f208417df Use the right method to clear a __m128 to 0 2012-09-09 04:09:31 -07:00
Chris Robinson f1ce139997 Use SSE to do 4 samples at once (non-HRTF direct mix), instead of to apply a matrix row
MaxCHannels no longer needs to be a multiple of 4 now, either.
2012-09-09 04:02:54 -07:00
Chris Robinson f56dddfa73 Implement MixDirect_SSE separately from the C and Neon versions 2012-09-09 00:53:54 -07:00
Chris Robinson 2bf1979d4a Move the target effect slot to the SendParams struct 2012-09-08 22:32:30 -07:00
Chris Robinson 89cab3cd39 Remove STACK_DATA_SIZE in favor of BUFFERSIZE 2012-09-08 22:09:34 -07:00
Chris Robinson 6723d27e06 Remove some unused code 2012-09-08 21:53:04 -07:00
Chris Robinson 1c03025090 Separate the resampling and mixing steps 2012-09-08 21:34:36 -07:00
Chris Robinson 7a37a63b8e Workaround MinGW __declspec(align(x)) bug 2012-09-08 19:24:07 -07:00
Chris Robinson 2b5fdad2c8 Free the global ALSA config after opening capture devices too 2012-09-02 14:26:46 -07:00
Lauri Kasanen a6f9ede849 alsa: Free the global config cache to remove a lot of reported leaks
Signed-off-by: Lauri Kasanen <curaga@operamail.com>
2012-09-02 14:16:23 -07:00
Chris Robinson 78dac1bf4a Set the error if setting a new integer source offset fails 2012-09-01 19:31:43 -07:00
Chris Robinson 795909c39e Minor cleanups for mixer_inc 2012-08-29 02:43:19 -07:00
Chris Robinson 3c042a054d Add an SSE-enhanced path for applying the mixer matrix 2012-08-29 01:56:04 -07:00
Chris Robinson 332d66c183 Make sure device buffers are 16-byte aligned 2012-08-29 01:40:42 -07:00
Chris Robinson de006947ac Increase MaxChannels to be a multiple of 4 2012-08-29 01:11:55 -07:00
Chris Robinson 8a128e5d25 Set an error closer to where it occurs for setting source properties 2012-08-29 00:25:01 -07:00
Chris Robinson 34cbacf424 Add helpers to set source properties 2012-08-28 22:16:55 -07:00
Chris Robinson f2fa5e7c88 Print the error when reporting snd_pcm_delay failure 2012-08-27 22:52:37 -07:00
Chris Robinson 3b735cc34a Use _aligned_free for pointers returned by _aligned_malloc 2012-08-24 17:45:16 -07:00
Chris Robinson 238caa1492 Constify some parameters 2012-08-21 16:01:11 -07:00
Chris Robinson e5ebe345ad Add the option to retrieve the source offset and latency in seconds 2012-08-20 15:57:27 -07:00
Chris Robinson 965608356f Fix getting the RW offsets with alGetSourcei64vSOFT 2012-08-20 15:33:28 -07:00
Chris Robinson bc1ce7b3ac Add methods to get source properties as doubles 2012-08-20 15:26:35 -07:00
Chris Robinson 6a3619c40f Fix a parameter name 2012-08-20 14:50:43 -07:00
Chris Robinson 50fa9a44be Properly export alGetSource3i64SOFT 2012-08-20 14:48:08 -07:00
Chris Robinson 4937a48bd9 Use helper functions to read source properties
And make sure the 64-bit int getters to get 32-bit int values
2012-08-20 14:16:58 -07:00
Chris Robinson 987a81c4de Use the correct 64-bit int type for the extension functions 2012-08-20 12:22:00 -07:00
Chris Robinson f5e0500df4 Add a macro to help make a 64-bit value 2012-08-19 22:31:55 -07:00
Chris Robinson 37924887e8 Use a default method to provide a dummy latency 2012-08-19 12:31:59 -07:00
Chris Robinson 0db7c4131b Hold the device lock with PulseAudio between mixing and writing 2012-08-18 17:20:20 -07:00
Chris Robinson a56a974149 Hold the device lock with ALSA between mixing and writing 2012-08-18 17:18:53 -07:00
Chris Robinson af7f3768fa Use non-blocking access for ALSA playback 2012-08-18 17:01:56 -07:00
Chris Robinson 0865db564f Move the device lock into the backend function table
For backend-specific implementations: this should hold the audio mixer loop for
playback devices, and provide recursive mutex behavior.
2012-08-18 15:58:04 -07:00
Chris Robinson 2b020040b4 Keep the backend device open until the last reference is released
This should generally happen with the next ALCdevice_DecRef call unless the
device is in the middle of being used in another thread, in which case it's
probably best to not remove the resources.
2012-08-18 14:49:42 -07:00
Chris Robinson 3ae5fcbd7e Add the start of AL_SOFT_source_latency
This extension will provide a way for apps to get accurate latency and playback
position information
2012-08-18 11:06:39 -07:00
Chris Robinson ac4fc4026c Add a device method to retrieve the active latency
This is effectively the time until the next update will be heard by the user,
or the closest approximation thereof, in nanoseconds.
2012-08-17 13:38:52 -07:00
Chris Robinson 9737f68315 Place the alignment attribute before the variable declaration 2012-08-16 10:42:48 -07:00
Chris Robinson 2a8c26e390 Remove extra parenthesis used in the aligned attribute declaration 2012-08-16 10:33:37 -07:00
Chris Robinson 3c395a82db Fix aligned allocator fallback 2012-08-16 10:30:13 -07:00
Chris Robinson 15c6b9b165 MSVC wants __declspec(align), not __declspec(aligned) 2012-08-16 10:23:25 -07:00
Chris Robinson 147fd9de4e Avoid dynamic handling of libsndio
This makes it safer and easier to handle as OpenBSD updates it
2012-08-16 10:12:50 -07:00
Chris Robinson 0bca771a88 Apply HRTF mixer coefficients with stepping using SSE 2012-08-15 08:19:04 -07:00
Chris Robinson f4ff63e271 Check the max CPUID functions before calling them 2012-08-15 06:28:45 -07:00
Chris Robinson bbcf4e8c1d Make sure sources are 16-byte aligned
They contain fields that require 16-byte alignment for SSE (and Neon?)
acceleration.
2012-08-15 05:54:13 -07:00
Chris Robinson 2859357939 Add wrapper methods to ensure aligned allocations 2012-08-15 05:50:40 -07:00
Chris Robinson 2cbb565d09 Ensure some HRTF-related parameters are aligned 2012-08-15 04:10:41 -07:00
Chris Robinson 4d157d2849 Make sure the working coefficients are aligned for the SSE mixer 2012-08-15 03:43:36 -07:00
Chris Robinson 64cd763e19 Remove unused filter functions 2012-08-15 01:48:47 -07:00
Chris Robinson c3f387da70 Check for and use the -msse switch when dealing with SSE intrinsics 2012-08-15 01:46:42 -07:00
Chris Robinson 6ebeb3b4f7 Check the correct macro for ARM Neon support 2012-08-15 01:32:17 -07:00
Chris Robinson 2e18baa3c8 Don't bother handling MMX since we don't use it 2012-08-15 01:29:19 -07:00
Chris Robinson c9e87a19a9 Add cmake options to disable or require support for CPU extensions 2012-08-15 01:24:50 -07:00
Chris Robinson 3b87e29e63 Move mixers into separate source files 2012-08-15 01:01:55 -07:00
Chris Robinson e9a20cb985 Add an SSE-enhanced HRTF mixer using intrinsics
Not as good as it could be, but it's something
2012-08-14 03:53:07 -07:00
Chris Robinson cfc5205569 Fix a typo 2012-08-13 22:31:58 -07:00
Chris Robinson 17dfaa3aae Add a config option to disable use of CPU extensions 2012-08-13 10:37:49 -07:00
Chris Robinson 0a3eba08d8 Allow building multiple types of mixers, selecting based on CPU caps 2012-08-13 09:18:46 -07:00
Chris Robinson add53e28c2 Check for some CPU extensions 2012-08-13 09:07:01 -07:00
Chris Robinson f42f655ea9 Don't unlock the pulseaudio mainloop when mixing
Lock contention could cause a lengthy delay between mixing and writing the
audio. There shouldn't be any risk of dead-locking as the device lock should
never be held when the mainloop gets locked.
2012-08-12 03:52:38 -07:00
Chris Robinson 8304fc1bef Handle the scalar values a bit better 2012-08-11 06:20:24 -07:00
Chris Robinson ea84fee952 Use a more efficient method to blend HRTF delays and coefficients 2012-08-11 02:16:34 -07:00
Chris Robinson d6fd52a0e5 Add an option for wide stereo sources
Replaces the old stereodup option
2012-08-09 05:38:07 -07:00
Chris Robinson 0dde7dd88a Use a more efficient method to offset the speaker angles when computing gains 2012-08-09 02:31:19 -07:00
Chris Robinson 1323e11701 Interpolate between HRTF delay samples using the delay fraction 2012-07-20 15:08:11 -07:00
Chris Robinson 7ac9944bb3 Increase the directional change sensitivity for the HRTF delta 2012-07-20 14:38:49 -07:00
Chris Robinson e32e2fd6b8 Fix an inadvertent change in the HRTF coefficient stepping value array size 2012-07-19 11:49:43 -07:00
Chris Robinson 6bd535bed0 Use wrappers for float-typed math functions 2012-06-29 02:12:36 -07:00
Chris Robinson 524c88c402 Test the squared length of a vector before normalizing 2012-06-28 20:54:10 -07:00
Chris Robinson 1c096b101d Minor typo fixes 2012-06-28 19:57:23 -07:00
Chris Robinson 583dc8dbca Don't use all caps for enum value names 2012-06-28 18:49:49 -07:00
Chris Robinson befa4e7528 Don't try to set FE_TOWARDZERO when it's not available 2012-06-28 18:27:45 -07:00
Chris Robinson bc6751237c Clamp the Y position before trying to get the elevation 2012-06-28 17:26:00 -07:00
Chris Robinson 6f3be31b60 Add some includes 2012-06-20 03:01:19 -07:00
Chris Robinson 8001d86751 Add missing include 2012-06-19 22:15:19 -07:00
Chris Robinson acca3e5a21 Use a proper method to wait for a PulseAudio operation 2012-06-19 10:37:09 -07:00
Chris Robinson d17f835e74 Remove an unused include 2012-06-19 04:02:55 -07:00
Chris Robinson ef1f8aa9a0 Don't needlessly verify a device 2012-06-19 00:25:45 -07:00
Chris Robinson a5ac6eb524 Remove an outdated comment about _REENTRANT 2012-06-18 02:04:10 -07:00
Chris Robinson ac84d95c6e Silence some GCC warnings 2012-06-17 22:17:03 -07:00
Chris Robinson 785f52aa29 Only try float32 with DSound when specifically requested
Some hardware drivers seem to have a bug where a buffer descriptor with a
32-bit float sample type ends up creating a non-float32 buffer.
2012-06-17 04:13:01 -07:00
Chris Robinson 051195a991 Avoid failing if ALSA can't set a buffer or period time near to our request
These functions should not fail, but there seem to be bugs that cause them to
sometimes. We're flexible enough to handle almost anything it throws back at us
though, so anything is better than nothing here.
2012-06-16 23:02:59 -07:00
Chris Robinson 90ca6675c2 Allow PulseAudio to spawn a server by default
Since only one backend is used at a time now, the issues with device ownership
aren't really prevelant anymore. An application that tries to open all
enumerated devices won't run into the problem of spawning a server and end up
preventing other backend devices from working.
2012-06-16 22:37:45 -07:00
Chris Robinson 0db9730a50 Add an option to allow PulseAudio to move streams
This could be made the default, if there's a safe way to update the AL device
specifier.
2012-06-16 22:32:19 -07:00
Chris Robinson ad2643d8af Clarify some comments, fix some definition ordering, and add some includes 2012-06-15 23:46:09 -07:00
Chris Robinson 46840ee787 Add DEVPKEY_Device_FriendlyName declaration for mingw-w64 compatibility 2012-06-14 18:10:10 -07:00
Chris Robinson 1e0282a238 Add some more entries to .gitignore 2012-05-09 16:30:22 -07:00
Chris Robinson 259e4f5b38 Rename AllDevice -> AllDevices 2012-05-09 16:28:16 -07:00
Chris Robinson e44229c74e Clamp the ALSA period time between min and max if setting near fails 2012-05-08 01:23:53 -07:00
Chris Robinson 92827cc467 Clamp the ALSA buffer time between min and max if setting near fails
It seems there's some bugs with snd_pcm_hw_params_set_buffer_time_near that
cause it to return "invalid argument" despite a valid nearby buffer time being
available. Clamping the buffer time seems to mitigate the issue a bit.
2012-05-07 11:42:59 -07:00
Chris Robinson dd3d5bc2d4 Look for libsndio.so.2 instead of libsndio.so 2012-05-05 16:19:51 -07:00
Chris Robinson b4c7b3309a Only use _FPU_GETCW and _FPU_SETCW for x86 targets 2012-05-03 12:15:51 -07:00
Chris Robinson 010a66487d Use 2 chained one-pole filters for the wet path 2012-04-30 09:33:00 -07:00
Chris Robinson aed35fd811 Use ComputeAngleGains for multi-channel sources and remove the unused lookup table stuff 2012-04-29 05:04:46 -07:00
Chris Robinson 8d9838012d Remove the stereodup option
There's better ways to do it now with "wide" sound sources.
2012-04-29 04:44:53 -07:00
Chris Robinson 57d36ec582 Fix a couple comment typos, and a minor cleanup 2012-04-29 03:39:08 -07:00
Chris Robinson febc2a50cb Use ComputeAngleGains for 3D sounds 2012-04-28 13:06:16 -07:00
Chris Robinson 74e128bd0f Fix ComputeAngleGains
The old code would improperly handle speakers just inside or outside the
coverage area if it had to be inverted. It also didn't properly handle when
only one speaker was outside of the covered area.
2012-04-28 12:12:02 -07:00
Chris Robinson c34d78f417 Use ComputeAngleGains for the echo and dedicated effects 2012-04-28 08:24:09 -07:00
Chris Robinson 611bd0b2d3 Add a method to calculate gains given a sound point and its half-width, and use it for reverb
The half-width ranges from 0 to pi, and essentially specifies the coverage area
around the listener. At 0, it's an infinitely small point sound and behaves
like a usual panning sound. At pi/2 it covers half the area, and at pi it
covers the whole area.
2012-04-28 08:21:53 -07:00
Chris Robinson 267d38cf20 Flip the parameters to aluCart2LUTpos, so it behaves a bit more like atan2 2012-04-28 03:58:58 -07:00
Chris Robinson 26456f13e1 Pass the direct/send params to the mixer instead of accessing through the source 2012-04-28 03:31:13 -07:00
Chris Robinson 2484c44db0 Apply the listener gain with the filter 2012-04-28 03:13:37 -07:00
Chris Robinson a8a13fcbd8 Move the HrtfParams into the DirectParams 2012-04-28 02:23:53 -07:00
Chris Robinson 0a193c5645 Move the direct and send params into separate different types 2012-04-28 01:32:52 -07:00
Chris Robinson 2450ee1723 Use separate methods for the dry and wet mixing loops 2012-04-27 23:46:51 -07:00
Chris Robinson 67f1cdfcbe Store the speaker angles in the device 2012-04-27 07:38:17 -07:00
Chris Robinson 549d542a31 Reorganize some ALsource fields 2012-04-27 01:36:13 -07:00
Chris Robinson 2cdb58fb85 The mixer always uses floats 2012-04-26 07:50:17 -07:00
Chris Robinson 68ab232364 Use a separate loop to actually update DataPosFrac and DataPosInt 2012-04-26 06:44:37 -07:00
Chris Robinson 871d61f3f9 Add and fixup some ALsource comments 2012-04-26 03:26:22 -07:00
Chris Robinson b08bd3746e Add AL_EXT_SOURCE_RADIUS to alext.h 2012-04-26 02:05:49 -07:00
Chris Robinson 39ab0b0aa3 Add AL_EXT_STEREO_ANGLES to alext.h 2012-04-26 01:19:25 -07:00
Chris Robinson 5c70cacc13 Fixup some comments in ALu.c 2012-04-26 00:59:17 -07:00
Chris Robinson 8c2cd057b2 Fix a minor typo 2012-04-25 19:37:12 -07:00
Chris Robinson fdc4133ac9 Partially convert alEffect.c and alFilter.c to the new error handling 2012-04-25 00:24:02 -07:00
Chris Robinson 3833c021c0 Move a comment 2012-04-25 00:03:01 -07:00
Chris Robinson cafbd9461e Fix up alEffect.c and alFilter.c a bit 2012-04-24 23:06:10 -07:00
Chris Robinson 6995ac1ed2 Change alAuxEffectSlot.c to the new error handling 2012-04-24 22:57:23 -07:00
Chris Robinson 58e3dc3d52 Add a SIGTRAP for when alGetError is called without a context 2012-04-24 21:41:01 -07:00
Chris Robinson 3e49e79140 Couple small error handling fixups 2012-04-24 03:52:21 -07:00
Chris Robinson dec8e81211 Change alState.c to the new error handling 2012-04-24 03:09:28 -07:00
Chris Robinson 4188a5f3f3 Fix compiling al.h with C++ 2012-04-24 02:51:23 -07:00
Chris Robinson 31d124582b Switch alBuffer.c to the new error handling scheme, and get rid of more hungarian notation 2012-04-24 00:17:05 -07:00
Chris Robinson 248e079301 Switch alListener.c to the new error handling scheme, and get rid of more hungarian notation 2012-04-23 21:35:05 -07:00
Chris Robinson 5ce850570f Add try/catch-like macros to handle errors, and convert alSource.c to use them 2012-04-23 19:46:05 -07:00
Chris Robinson 648464a2da Check the right playback device file with OSS 2012-04-22 05:06:44 -07:00
Chris Robinson 29f77003c0 Some alSource.c cleanups 2012-04-21 05:56:03 -07:00
Chris Robinson dfa38ed7a5 Reformat alc/h and al.h, and redo the comments 2012-04-21 04:34:08 -07:00
Chris Robinson 66c918c84b Remove export pragmas from headers
Available information suggests it's only useful for Mac OS9 and earlier (not
OSX).
2012-04-20 22:53:47 -07:00
Chris Robinson 77f55da6c7 Use a macro to declare the function and enum list members 2012-04-20 22:38:03 -07:00
Chris Robinson d6af7cdcb2 Rename another variable to avoid hungarian notation 2012-04-20 22:37:36 -07:00
Chris Robinson 0ad8047820 Combine ALCenum and ALenum lists 2012-04-20 01:24:58 -07:00
Chris Robinson 2115f578e9 Reorganize ALC functions and fixup some comments 2012-04-20 01:18:38 -07:00
Chris Robinson 511b4790d0 Remove hungarian notation from dsound and mmdevapi 2012-04-19 23:42:02 -07:00
Chris Robinson d6491e21f9 Remove some more hungarian notation 2012-04-19 23:28:34 -07:00
Chris Robinson cff0f938e5 Remove hungarian notation from the winmm backend 2012-04-19 23:22:11 -07:00
Chris Robinson 5418519637 Remove some more hungarian notation 2012-04-19 23:00:58 -07:00
Chris Robinson b023dbe7b8 Remove hungarian notation from the device and context structs 2012-04-19 22:50:11 -07:00
Chris Robinson 8092cd9205 Remove hungarian notation from some function parameters 2012-04-19 22:40:40 -07:00
Chris Robinson 60da5bd338 Remove an unused field 2012-04-19 22:32:04 -07:00
Chris Robinson c9e64596a4 Use a consistent name for the self-id field 2012-04-19 22:28:01 -07:00
Chris Robinson 08bd5e8b37 Fix comment and rename the buffer's self-id 2012-04-19 22:14:02 -07:00
Chris Robinson 125b743e5c Remove hungarian notation from sources 2012-04-19 21:46:29 -07:00
Chris Robinson 9a3eb397f4 Print the device name in printALCInfo 2012-04-16 23:27:13 -07:00
Chris Robinson 84c1f2bfd0 Avoid shadowing a couple variables 2012-04-16 23:09:36 -07:00
Chris Robinson d7ba49ea62 Clear the position fraction when setting a new source offset 2012-04-16 22:13:39 -07:00
Chris Robinson e47557630e Use a double to store the source offset and don't use milliseconds for seconds 2012-04-16 22:11:03 -07:00
Chris Robinson f6a1e53f19 Read ALSOFT_TRAP_*ERROR when reading the config 2012-04-16 20:13:50 -07:00
Chris Robinson a0a015f19f Set a 1MB stack size for created threads 2012-04-16 19:15:48 -07:00
Chris Robinson e3bea63f94 Check for a recent libavutil for the example, too 2012-04-07 12:35:59 -07:00
Chris Robinson 6b154025ab Release 1.14 2012-03-28 08:55:17 -07:00
Chris Robinson b9b2bdb37a Make sure we have a recent-enough ffmpeg 2012-03-27 08:52:21 -07:00
Chris Robinson 7b35c77062 Add a missing round() for MSVC 2012-03-27 08:44:08 -07:00
Chris Robinson c23cfdcfa1 Make sure M_PI is defined for makehrtf 2012-03-27 08:20:04 -07:00
Chris Robinson 929e9bfac1 Avoid DOS-style line-endings and stdint types for the makehrtf utility 2012-03-25 19:57:40 -07:00
Chris Robinson b51afb74da Add a utility to generate OpenAL Soft's HRTF data from the MIT KEMAR data 2012-03-22 22:45:03 -07:00
Chris Robinson 5a43cd3b64 Use avformat_free_context to free a format context that failed to open 2012-03-21 13:54:13 -07:00
Chris Robinson 5d08563975 Handle very small speed of sound values 2012-03-18 08:20:08 -07:00
Chris Robinson adb83ad3c0 Avoid converting positions to meters 2012-03-18 08:09:59 -07:00
Chris Robinson 2d0e028993 Base the initial reverb decay on the source's dry path attenuation 2012-03-18 08:06:21 -07:00
Chris Robinson 703d72f20e Use the correct loop count for reading the elevation offsets from external HRTF files 2012-03-17 03:42:07 -07:00
Chris Robinson b7d67d8bcc Don't error when querying unknown enums for the channel config and sample format 2012-03-15 15:22:58 -07:00
Chris Robinson 7e5b13aabd Finalize ALC_SOFT_loopback 2012-03-15 15:21:15 -07:00
Chris Robinson 7fbcb4b9e6 Use the actual distance from the ref distance for air absorption and initial reverb decay 2012-03-14 22:45:52 -07:00
Chris Robinson 767a5b3c73 Mark the GlobalContext pointer as volatile 2012-03-14 00:13:16 -07:00
Chris Robinson a08080bd82 Avoid modifying the ALCdevice in portaudio's open method 2012-03-13 22:18:51 -07:00
Chris Robinson 415bf58a06 Store the portaudio stream parameters with the device 2012-03-13 21:52:47 -07:00
Chris Robinson 3b517bfa59 Initialize the default effect after opening the device 2012-03-13 19:02:26 -07:00
Chris Robinson e356527a88 Rename GetReverbEffect to LoadReverbPreset 2012-03-13 15:48:51 -07:00
Chris Robinson 07e34cc3cf Use enums to specify the default channel configuration and sample type 2012-03-13 15:32:44 -07:00
Chris Robinson f7f1dd1346 Initialize the default effect when opening the device and update it with the other effect slots 2012-03-13 15:07:38 -07:00
Chris Robinson 92ccacdc77 Pass the device to InitializeEffect and return the error enum from it 2012-03-13 14:58:34 -07:00
Chris Robinson b5ed2a5351 Pass a device to the effect update functions 2012-03-13 14:49:58 -07:00
Chris Robinson 55c5e4fe92 Rename ForcedEffect to DefaultEffect 2012-03-13 14:38:09 -07:00
Chris Robinson 3b165809cc Minor coreaudio fix, and make sure to set the device name 2012-03-13 13:48:42 -07:00
Chris Robinson 5c59e6e704 Try to ensure the full mmdevapi buffer is used 2012-03-13 01:39:57 -07:00
Chris Robinson ef2f237276 Try to ensure at least 2 mmdevapi updates 2012-03-13 01:35:31 -07:00
Chris Robinson 0af14c7a11 Try to find a multiple of mmdevapi's period size nearest to the wanted update size 2012-03-13 01:32:15 -07:00
Chris Robinson 8ef777ebbe Restore the FPU mode in an error path 2012-03-12 23:58:12 -07:00
Chris Robinson 305d790a19 Revert the default stereo layout back to -90 and +90 degrees
For the time being, until better stereo spatialization can be implemented and
headphones can be better detected.
2012-03-12 22:49:10 -07:00
Chris Robinson 1f3c1091d3 Finalize AL_SOFT_direct_channels 2012-03-12 22:46:13 -07:00
Chris Robinson 663b43f4f1 Attempted fix for the coreaudio backend 2012-03-12 21:58:40 -07:00
Chris Robinson 3e0cf0f239 Simply (hopefuly) the explanation about the HRIR set ordering 2012-03-12 18:42:21 -07:00
Chris Robinson 560f34e3df Channel angle is already in radians, not degrees 2012-03-12 18:29:46 -07:00
Chris Robinson 335232207d With DirectChannels enabled, don't try to mix channels that have no matching output 2012-03-12 18:27:25 -07:00
Chris Robinson 93e71f0554 Some HRTF clarifications 2012-03-12 17:54:16 -07:00
Chris Robinson 0efbf7bc9e Fix typo 2012-03-12 17:15:44 -07:00
Chris Robinson 3daaa078fb Add a note about the HRTF data being minimum-phase reconstructed 2012-03-12 17:14:42 -07:00
Chris Robinson 647448a32b Reset HAVE_MMDEVAPI before checking the backends 2012-03-12 15:33:56 -07:00
Chris Robinson f7a8e3db35 Don't assume the calculated HRTF delta for the gain difference is less than 1 2012-03-12 13:31:19 -07:00
Chris Robinson 7747c320a8 Remove an unused property key definition 2012-03-12 12:56:52 -07:00
Chris Robinson ebc69bc870 Minor corrections to the hrtf.txt 2012-03-12 00:58:14 -07:00
Chris Robinson 10e2b7ecbf Document the hrtf_tables config option 2012-03-11 19:29:19 -07:00
Chris Robinson 842a698f2b Add a text file explaining OpenAL Soft's HRTF support and data file format 2012-03-11 19:27:40 -07:00
Chris Robinson f5f519aa80 Properly make the source velocity relative to the listener velocity 2012-03-09 23:53:45 -08:00
Chris Robinson 7db29a2332 Clamp the upper and lower bound of the doppler velocity scale more like the original 2012-03-09 22:38:26 -08:00
Chris Robinson 6e70ae9958 The listener velocity is specified in world coordinates, too 2012-03-08 18:27:05 -08:00
Chris Robinson e190fc9416 Refactor the doppler shift calculations 2012-03-08 18:19:15 -08:00
Chris Robinson 1a4a0abd1a Pin the DLL for Windows
The mmdevapi backend does not react well to being unloaded dynamically. It has
a message-handling thread running in the background which can't quit before
DllMain is called with DLL_PROCESS_DETACH, at which point it's too late to
safely message and wait for it shutddown, thus it can continue running after
the DLL is unloaded from memory.
2012-03-08 17:42:16 -08:00
Chris Robinson dddacc25a5 Store a duplicate of the mmdevapi device ID 2012-03-06 13:15:55 -08:00
Chris Robinson 2ba2da467c Add a comment explaining the pulseaudio prop_filter 2012-03-06 12:01:15 -08:00
Chris Robinson f2b64fd111 Update the ALCdevice in winmm's reset method instead of open 2012-03-06 10:14:05 -08:00
Chris Robinson 6623f69476 Mark the device as running for capture, too 2012-03-05 15:10:02 -08:00
Chris Robinson db36574667 Hold the list lock while calling the backend
Pretty ugly, but we mustn't hold the device lock
2012-03-05 14:53:25 -08:00
Chris Robinson 3131104595 Filter out a couple pulseaudio stream properties by setting them to 0-length data
It seems Qt/Phonon will globally set media.role and phonon.streamid properties,
causing them to apply even to our streams if they happen to be loaded in the
same process. This is particularly a problem because media.role gets set to
"event", preventing the streams from showing in pavucontrol.
2012-03-05 13:00:51 -08:00
Chris Robinson f64bdc4bd4 Returned pa_operations can be NULL 2012-03-05 08:37:50 -08:00
Chris Robinson fe6e532c87 Use a separate backend callback to start playback of the device
This allows us to properly update the ALCdevice and its resources with the new
parameters before starting playback, instead of expecting the mixer to block
and wait after it has begun.

This also lets us avoid holding the device lock while resetting and starting
the device, which helps prevent lock inversion on some backends (ie, one thread
locking A then B, and another thread locking B then A), ultimately allowing
certain backends to asynchronously update the ALCdevice without risk of lockup.
Capture still has issues here, however.
2012-03-05 07:11:09 -08:00
Chris Robinson 5cdeeb47f3 Avoid some uninitialized warnings 2012-03-05 00:28:57 -08:00
Chris Robinson bc1bb7f90f Print and handle errors from pa_stream_readable_size 2012-03-05 00:11:28 -08:00
Chris Robinson 0f0141b9dd Set prebuf to 0 since we're handling the stream start manually 2012-03-04 09:24:07 -08:00
Chris Robinson 302c08a14b Enforce range limits on periods and period_size. 2012-03-04 05:44:37 -08:00
Chris Robinson b643f13a6e Make sure the pulse stream is properly started and better handle buffer size changes 2012-03-04 05:30:42 -08:00
Chris Robinson 487d05a73e Invert a nested loop 2012-03-03 10:31:27 -08:00
Chris Robinson 91ed077eba Handle BS2B cross-feed right after click removal 2012-03-03 09:41:16 -08:00
Chris Robinson 49433c2074 Slight correction for handling the wave writer backend's timer wrapping 2012-03-03 08:50:15 -08:00
Chris Robinson a93ff6ef3c Slight correction for handling the null backend's timer wrapping 2012-03-03 08:47:23 -08:00
Chris Robinson 44e10c4f69 Remove some unneeded pulseaudio calls 2012-03-02 09:46:36 -08:00
Chris Robinson 7a832c4522 Don't unload libpulse
This sometimes spawns esoteric errors in dlclose
2012-03-02 09:27:30 -08:00
Chris Robinson 7d0faba424 Remove some unneeded markers 2012-03-02 09:08:17 -08:00
Chris Robinson 01d942628b Set prebuf to the expected buffer size
This is to make sure the stream starts even if PulseAudio increases tlength.
2012-03-02 05:38:11 -08:00
Chris Robinson 2a9012c94d Don't rely on PulseAudio to wake us up using early requests 2012-03-02 04:51:29 -08:00
Chris Robinson b571926a6d Avoid a leak if PortAudio fails to give a usable channel count 2012-03-02 02:14:04 -08:00
Chris Robinson f784aa704d Make sure the device gets stopped when closing even if there's no contexts 2012-03-02 00:10:24 -08:00
Chris Robinson 153affac12 Recalculate the new update size using the device's UpdateSize 2012-03-02 00:08:56 -08:00
Chris Robinson e5d365a3d9 Avoid an unnecessary doCapture check 2012-03-01 20:47:49 -08:00
Chris Robinson 6ffaabeb75 Update the layout config option names 2012-03-01 10:06:55 -08:00
Chris Robinson 06debb153d Fix a memory leak 2012-03-01 09:56:25 -08:00
Chris Robinson 51e6786f31 Set the func list after successfully connecting to PulseAudio 2012-03-01 09:22:37 -08:00
Chris Robinson 50ef19720d Make alcLoopbackOpenDeviceSOFT take a standard "device" name for a parameter 2012-03-01 08:30:21 -08:00
Chris Robinson db4d62d17c Print out a more descriptive name for the opened device in openal-info 2012-03-01 06:59:51 -08:00
Chris Robinson 8d24bd720f Don't use GUIDs to ID mmdevapi devices, and don't enumerate if not needed 2012-03-01 06:07:00 -08:00
Chris Robinson 55556f0e6c Avoid enumerating when opening the default ALSA device 2012-03-01 04:58:28 -08:00
Chris Robinson 7d9cabebff Use NULL to open the default PulseAudio device, and get the actual device name from the stream 2012-03-01 04:37:40 -08:00
Chris Robinson 9602c46809 Move some function definitions down 2012-03-01 04:12:47 -08:00
Chris Robinson 85daab0d5e Don't define lib handles when dynamic loading is disabled 2012-03-01 03:46:45 -08:00
Chris Robinson 105faa1935 Only support PA_API_VERSION 12 2012-03-01 03:29:28 -08:00
Chris Robinson b299b90964 Update the stored buffer attributes if PulseAudio changes it 2012-03-01 03:09:15 -08:00
Chris Robinson 346f1ac293 Don't allow the PulseAudio stream to move
Since the device specifier isn't updated to reflect the change, it's best to
not allow it to move. This should be fixed eventually.
2012-03-01 02:52:32 -08:00
Chris Robinson 6e113eea89 Don't force a sample spec when looking for the default device 2012-03-01 01:34:49 -08:00
Chris Robinson 54f906b347 Don't check if the pulseaudio sink is suspended when opening 2012-03-01 01:19:07 -08:00
Chris Robinson 4b252bc494 Constify a couple string pointers 2012-03-01 01:03:37 -08:00
Chris Robinson b192165fb4 Look for and enumerate the default PulseAudio source first 2012-02-29 23:52:10 -08:00
Chris Robinson f887f4efdd Use a separate function to create and connect a PulseAudio record stream 2012-02-29 23:46:30 -08:00
Chris Robinson b37e54eead Look for and enumerate the default PulseAudio sink first 2012-02-29 23:35:01 -08:00
Chris Robinson ea4f458c1d Avoid enumerating multiple devices with the same name 2012-02-29 23:21:38 -08:00
Chris Robinson 394c76d64f Avoid passing the ALCdevice to connect_playback_stream 2012-02-29 23:13:09 -08:00
Chris Robinson d34fc21a50 Print the enumerated devices from PulseAudio 2012-02-29 22:31:51 -08:00
Chris Robinson f5435aa2de Minor cleanups for OSS and Solaris 2012-02-29 21:45:44 -08:00
Chris Robinson e88f954cbf Allow reading audio from ALSA directly if the requested length is supported 2012-02-29 10:01:45 -08:00
Chris Robinson fe897f291b Don't store the frame size with pulse_data 2012-02-29 06:51:21 -08:00
Chris Robinson fbb6239299 Read capture data from pulseaudio as needed, avoiding a ring buffer 2012-02-29 05:57:54 -08:00
Chris Robinson ae1633497c Get and release the mmdevapi render client iface on the message thread 2012-02-29 05:54:58 -08:00
Chris Robinson 00b0e64bd2 Use local variables to determine how much to write to mmdevapi 2012-02-29 00:32:20 -08:00
Chris Robinson 2747c87893 Print out unsupported audio format info from ffmpeg 2012-02-28 04:04:52 -08:00
Chris Robinson 2285c79ded Give "OpenAL Soft" for the standard device name 2012-02-28 03:36:16 -08:00
Chris Robinson 230e784b9e Trace the name of the created device 2012-02-27 21:57:33 -08:00
Chris Robinson 4b88168edb Use a switch to handle the ALSA PCM state 2012-02-27 04:31:03 -08:00
Chris Robinson f29fbdf088 Print enumerated ALSA devices 2012-02-26 23:02:29 -08:00
Chris Robinson 233acff3c4 Print enumerated dsound devices 2012-02-26 22:55:50 -08:00
Chris Robinson 5e625c9a68 Watch for unsupported sample types for dsound capture 2012-02-26 22:03:27 -08:00
Chris Robinson da4408835d Print the mmdevapi device and GUID found 2012-02-26 21:59:27 -08:00
Chris Robinson b78b8f0c82 Use separate loops for getting and checking device attributes 2012-02-25 18:03:33 -08:00
Chris Robinson 484e8f07a0 Watch for CLSIDFromString errors 2012-02-23 21:41:24 -08:00
Chris Robinson 504cdadd60 Use an enum for the device type 2012-02-23 15:25:30 -08:00
Chris Robinson 45c10229d9 Print a message when loading the "none" reverb preset 2012-02-23 13:49:46 -08:00
Chris Robinson 973b92b8fd Minor rearrangements for the pulseaudio mixing loop 2012-02-22 18:33:28 -08:00
Chris Robinson 6ba0fd25a2 Remove an unneeded wrapper 2012-02-22 18:08:15 -08:00
Chris Robinson 1dde8ad60b Use the stored minreq size from PulseAudio for rounding off the length to write 2012-02-21 18:07:20 -08:00
Chris Robinson 45b9cbe2b3 Only probe for devices when a backend is loaded 2012-02-20 22:12:48 -08:00
Chris Robinson 14912229b1 Avoid default names for winmm and dsound 2012-02-20 22:03:08 -08:00
Chris Robinson 2b64df05e6 Avoid using a default name with the MMDevApi backend 2012-02-20 21:46:01 -08:00
Chris Robinson 01d8d956da Avoid using a default name when opening a PulseAudio device 2012-02-20 21:03:54 -08:00
Chris Robinson 4f0d862cbe Set the default ALSA device name when probing 2012-02-20 20:59:55 -08:00
Chris Robinson e2ccc6f98e Always use "OpenAL Soft" for the short device enumeration list 2012-02-20 20:45:22 -08:00
Chris Robinson d24ada7ab7 Don't accept the pulseaudio default name for capture 2012-02-20 20:29:20 -08:00
Chris Robinson 54e2316b65 Use a string to report unsupported capture sample types 2012-02-20 20:25:58 -08:00
Chris Robinson 58316a97d5 Override the resampler as needed right after calculating the step value 2012-02-20 20:08:45 -08:00
Chris Robinson 771ef3e8d5 Rename p -> hp for ALSA's hw params 2012-02-20 12:46:09 -08:00
Chris Robinson 65ed18960e Add a text file describing the available environment variables 2012-02-19 23:03:04 -08:00
Chris Robinson 799f8da333 Recognize "none" to disable the default reverb 2012-02-19 17:41:42 -08:00
Chris Robinson 40bb8bd9dc Remove the trailing double-underscore from a couple env vars 2012-02-19 17:24:01 -08:00
Chris Robinson 426f0273cb Remove mention of the environment variables from alsoftrc.sample
There will be a separate file to document all of them.
2012-02-19 17:20:02 -08:00
Chris Robinson 03ec2d6fc7 Check for the ALSOFT_DRIVERS env var for a list of backend drivers
This overrides the 'drivers' config option
2012-02-19 14:27:00 -08:00
Chris Robinson e4f14fdc61 Check the pulse spawn-server config option before testing a connection to pulseaudio 2012-02-19 14:07:44 -08:00
Chris Robinson 418c766493 Use memset to clear the bs2b filter's last sample and remove the unused is_clear function 2012-02-19 13:09:29 -08:00
Chris Robinson 4a65747a4b Add a COUNTOF macro to get the number of entries in a static array 2012-02-19 12:07:40 -08:00
Chris Robinson 6dc194ed1d Fix a misnamed variable 2012-02-19 11:56:01 -08:00
Chris Robinson 1aa69e77e9 Print a warning if using the 'format' config option 2012-02-19 11:54:24 -08:00
Chris Robinson 6ac16867b4 Only set a default slot for non-NULL effect types 2012-02-18 17:09:08 -08:00
Chris Robinson bbc679a5db Minor MMDevApi cleanups 2012-02-18 13:08:54 -08:00
Chris Robinson 592fb2b913 Make sure an ALSA capture device was found before accessing the map 2012-02-18 12:39:17 -08:00
Chris Robinson f68f3f5f86 Simplify ALSA parameter error checking 2012-02-18 12:34:58 -08:00
Chris Robinson 5f23860880 Rename a couple variables and fix their types 2012-02-18 11:07:54 -08:00
Chris Robinson faf59a7cba Fix some comments 2012-02-18 11:03:42 -08:00
Chris Robinson cc2cb1142b Remove unused variables 2012-02-17 12:32:43 -08:00
Chris Robinson eb8be88866 Use the correct type to store the count from IMMDeviceCollection_GetCount 2012-02-17 12:31:53 -08:00
Chris Robinson e43e1bd7f5 Silence another MSVC warning 2012-02-17 01:32:35 -08:00
Chris Robinson eb586b0ed7 Initialize a variable before use 2012-02-17 01:30:41 -08:00
Chris Robinson 75aa96a4a6 Silence an MSVC warning 2012-02-17 01:29:23 -08:00
Chris Robinson 10257f485a Support device enumeration with mmdevapi 2012-02-16 17:04:08 -08:00
Chris Robinson deee6a73f0 Fix a device count leak and make sure COM is cleaned up if an mmdevice open fails 2012-02-16 10:08:50 -08:00
Chris Robinson 84ba0a94c0 Make sure the source write position is at least 15ms ahead of the read 2012-02-16 00:11:50 -08:00
Chris Robinson 5d17cef55f Calculate the buffer time before modifying the frequency 2012-02-15 22:54:17 -08:00
Chris Robinson 70f1a7e3ca Get the mmdevapi device period after initialization 2012-02-15 22:40:01 -08:00
Chris Robinson 60785eab8e Move the endian test macro to alMain.h 2012-02-15 21:47:35 -08:00
Chris Robinson 7e9af6f406 Use a switch to check the device format with winmm 2012-02-15 21:13:13 -08:00
Chris Robinson 6b272ff585 Add a device parameter to alcLoopbackOpenDeviceSOFT 2012-02-15 20:52:49 -08:00
Chris Robinson fecd679f33 Add a device request flag for the sample type 2012-02-15 16:26:32 -08:00
Chris Robinson 2ab0e3521c Remove parameter names from typedefs 2012-02-15 15:23:23 -08:00
Chris Robinson 9afd6b2858 Separate the format config option into channels and sample-type 2012-02-15 15:22:44 -08:00
Chris Robinson 670c88a64c Define a minimum output sample rate and warn if the config option goes below it 2012-02-15 12:26:19 -08:00
Chris Robinson 425ff7cb00 Combine a couple if checks 2012-02-15 11:28:19 -08:00
Chris Robinson 1140b3ae83 Support signed and unsigned 32-bit int output 2012-02-14 11:44:57 -08:00
Chris Robinson 5e1d1a52ba Check for each ALSA prefix config option only as needed 2012-02-13 10:33:53 -08:00
Chris Robinson e394d14cda Use more proper enum names for the resampler 2012-02-12 08:45:19 -08:00
Chris Robinson 9f073b6f1b Use a proper enum value for setting the default distance model 2012-02-12 08:18:20 -08:00
Chris Robinson b375b130ba Add config options for card- and device-specific prefixes in ALSA device names 2012-02-11 09:36:40 -08:00
Chris Robinson 3fb6351212 Format the ALSA device name when enumerating them 2012-02-11 06:55:24 -08:00
Chris Robinson 61be01efc1 Rename SpeakerGain to ChannelGain 2012-02-10 00:29:47 -08:00
Chris Robinson 23bda38be0 Match the 7.1 channel angles with the speakers 2012-02-09 23:59:24 -08:00
Chris Robinson e6a0dd10a9 Pre-scale the channel angles 2012-02-09 23:52:20 -08:00
Chris Robinson ef68aa39f8 Rename non_virtual_channels to direct_channels 2012-02-09 23:35:17 -08:00
Chris Robinson b4b492c417 Improve checks for supported output formats from ALSA 2012-02-09 22:26:16 -08:00
Chris Robinson a98e381c8e Ensure ALbyte3 and ALubyte3 are the proper size 2012-02-07 08:05:01 -08:00
Chris Robinson 18f87b97d5 Always use the default speaker layout for loopback devices 2012-02-07 07:29:34 -08:00
Chris Robinson c211f1d3fe Don't enable BS2B for loopback devices 2012-02-07 07:06:38 -08:00
Chris Robinson deb84cfdac Improve a couple error messages with the full ALSA device control name 2012-02-07 07:04:33 -08:00
Chris Robinson 7ad1b45d11 Change the default stereo speaker positions to match the stereo channel positions
A separate "headphones" configuration may be nice to add for -90,+90, however
not many audio APIs can detect this. Ideally HRTF would be used with
headphones too, which largely ignores the speaker positions, however there
could be situations where this is unfeasible or unwanted.
2012-02-04 10:29:19 -08:00
Chris Robinson 47161150d7 Check for a 32-bit x86 processor before using MSVC's asm intrinsics 2012-02-03 19:34:06 -08:00
Chris Robinson 8e6832407a Only use fast float-to-int GCC assembly for x86 and x86_64 targets 2012-02-03 17:40:21 -08:00
Chris Robinson 9166432ccf Fix typo in the "CarpetedHallway" reverb preset 2012-02-01 16:31:37 -08:00
Chris Robinson 0107e74986 Use an enum for static const values 2012-01-31 03:43:52 -08:00
Chris Robinson c605e06e07 Make the EFX preset values explicit floats 2012-01-31 02:57:22 -08:00
Chris Robinson 15a8e58225 Implement capture for the DirectSound backend
Based on a patch by Jeffrey Knockel <jeff250.at.gmail.com>
2012-01-31 01:47:59 -08:00
Chris Robinson 4393b6b05f Only initialize the default reverb effect when a slot is available 2012-01-29 22:49:17 -08:00
Chris Robinson ed037698fd Reorder a couple effect presets 2012-01-29 22:16:39 -08:00
Chris Robinson e870c22f87 av_find_stream_info is also deprecated, in favor of avformat_find_stream_info 2012-01-29 20:06:30 -08:00
Chris Robinson fd6f03cf74 Use AVMEDIA_TYPE_AUDIO instead of the deprecated CODEC_TYPE_AUDIO 2012-01-29 16:21:57 -08:00
Chris Robinson 706f407ed5 Add some more reverb presets 2012-01-27 23:15:16 -08:00
Chris Robinson dd3abd0f16 Use sizeof(ALfloat) when finding the max stepping value
The mixer always reads from floats, so the size of the storage type is
unimportant.
2012-01-26 22:33:36 -08:00
Chris Robinson 3fda804217 Pass the uncompressed sample count to LoadData and ConvertData for IMA4 2012-01-26 16:53:21 -08:00
Chris Robinson 1d1440ac64 Make sure context attributes are specified for loopback devices 2012-01-26 16:31:46 -08:00
Chris Robinson 119768001c Make sure InitializeEffect on the default slot doesn't set a context error 2012-01-26 14:46:52 -08:00
Chris Robinson 3ee92a2b33 Return the error code from UpdateDeviceParams 2012-01-25 20:00:34 -08:00
Chris Robinson f87b607a53 Make sure the attributes list specifies a format for loopback devices 2012-01-25 19:54:55 -08:00
Chris Robinson b4ba83ec34 Avoid a bit of code duplication 2012-01-25 19:32:10 -08:00
Chris Robinson d807c4bf87 Don't process a default device slot if it doesn't exist 2012-01-25 18:48:26 -08:00
Chris Robinson 423f89c908 Fix a typo 2012-01-24 15:11:23 -08:00
Chris Robinson 8d07bc41c9 Some cleanups for the reverb list 2012-01-24 13:29:33 -08:00
Chris Robinson 86ef115e21 Set the source's slot send parameter to NULL if it's the AL_EFFECT_NULL type 2012-01-23 06:29:03 -08:00
Chris Robinson 1dd8aa9b6f Link with AudioToolbox.framework for the CoreAudio backend if it's found 2012-01-23 03:45:04 -08:00
Chris Robinson f520257463 Avoid duplicating some initialization code 2012-01-20 16:23:15 -08:00
Chris Robinson 00dc3088c8 Honor the disabled effects for the default reverb 2012-01-20 15:56:32 -08:00
Chris Robinson e52e4b1a8c Not all devices will have a default effect slot 2012-01-20 12:53:41 -08:00
Chris Robinson 0ef3a30b84 Fix WaveIn shutdown for real
The processing thread must be messaged when shutting down, and depending on the
state of the device, waveInReset may not do it. Additionally, all buffers must
be removed from the device before they can be unprepared and the device closed.
2012-01-20 12:36:23 -08:00
Chris Robinson b375897374 Remove an unnecessary else statement 2012-01-20 12:20:24 -08:00
Chris Robinson 2b2ce8bf3b Always message the WinMM processing thread when a buffer is done
So the processing thread can keep track of completed buffers when shutting
down, too.
2012-01-20 12:06:31 -08:00
Chris Robinson 47c8c15878 Fix a possible race condition when shutting down WinMM devices
If the processing threads are interrupted between the shutdown check and re-
adding the buffer back to the device, the device can be left with a buffer that
will get unprepared and freed while in use.
2012-01-20 03:02:21 -08:00
Chris Robinson fa36435290 Add a default-reverb config option, and rename __ALSOFT_FORCE_REVERB to match 2012-01-20 01:51:27 -08:00
Chris Robinson 4b83123cb8 Install efx-presets.h 2012-01-19 22:30:12 -08:00
Chris Robinson 37b17c8c78 Add the ability to specify a preset for forced reverb 2012-01-19 20:02:57 -08:00
Chris Robinson 51e81f4867 Add a global option to apply a reverb effect on source send 0
A special slot on the device is created and processed, so it can be shared
across all contexts on the device. Sources that don't have a slot set on send 0
will use this special slot instead.
2012-01-19 19:30:03 -08:00
Chris Robinson ae7b61b040 Avoid printing messages about device parameter changes in the backends 2012-01-17 16:38:58 -08:00
Chris Robinson 5a2a534240 Report parameter request changes in UpdateDeviceParams 2012-01-17 15:55:37 -08:00
Chris Robinson 09258ca5c1 Change the default output format to 32-bit float 2012-01-17 15:38:36 -08:00
Chris Robinson e0966d99a1 Retry opening the PortAudio device with 16-bit short if 32-bit float output fails 2012-01-17 15:26:22 -08:00
Chris Robinson 66a700729b Retry opening the DSound device with 16-bit short if 32-bit float output fails 2012-01-17 12:38:47 -08:00
Chris Robinson a7266442c2 Retry opening the WinMM device with 16-bit short if 32-bit float output fails 2012-01-17 12:21:52 -08:00
Chris Robinson cf34b9c04e Support 32-bit float in the WinMM backend 2012-01-17 12:17:41 -08:00
Chris Robinson 530597347c Safely insert the new context into the list 2012-01-12 06:13:24 -08:00
Chris Robinson c543611897 Add some casts 2012-01-11 00:55:02 -08:00
Chris Robinson d88596e13c There's no need to pass a buffer to avio_alloc_context 2012-01-11 00:43:24 -08:00
Chris Robinson c59bbe7c22 Handle a couple of special AVSEEK_ seek modes 2012-01-11 00:32:16 -08:00
Chris Robinson d4670faafd Fix compiling the example program, and support AL_SOFT_buffer_samples with it 2012-01-10 02:12:54 -08:00
Chris Robinson e772fb7d0e Finalize AL_SOFT_buffer_samples 2012-01-10 01:14:02 -08:00
Chris Robinson 29ba8f9f1a Remove the unnecessary AL_MULAW_SOFT, AL_ALAW_SOFT, and AL_IMA4_SOFT defines 2012-01-10 00:59:10 -08:00
Chris Robinson 61af4d715c Append _SOFT to the AL_SOFT_buffer_samples enums 2012-01-10 00:41:05 -08:00
Chris Robinson 535091e36c Make LockDevice, LockContext, etc, inline 2012-01-09 23:37:03 -08:00
Chris Robinson 5b7f8c1816 Check earlier for ffmpeg 2012-01-02 11:52:20 -08:00
Chris Robinson 18216ab1a9 Remove the warning when a context being released is globally current
Just about every implementation allows this, and there's no direct harm caused
by it. However, leave the warnings for when it's still current on specific
threads, as this can cause issues.
2012-01-01 15:36:31 -08:00
Chris Robinson 5500ed9209 Use the device lock when removing the context from the device's list 2012-01-01 15:22:58 -08:00
Chris Robinson 886f874ff3 Use a proper typedef for handling atomic pointer swaps 2012-01-01 15:00:03 -08:00
Chris Robinson 466cac328f Add an example program that streams audio using ffmpeg 2012-01-01 12:36:41 -08:00
Chris Robinson 06caac2619 Update some config option descriptions 2011-12-20 01:45:29 -08:00
Chris Robinson 2691c33b55 Use a string to specify the resampler config option 2011-12-20 01:36:01 -08:00
Chris Robinson 7f6844e48d Use a struct to associate channel enums and their angles 2011-12-20 01:17:11 -08:00
Chris Robinson a5a929ff72 Restore the old FPU mode if the effects fail to update with new device parameters 2011-12-20 00:35:32 -08:00
Chris Robinson 84b5311257 Use an explicit 0 for the "no error" enums 2011-12-03 08:27:51 -08:00
Chris Robinson 6a97f6a327 Reset the ALSA device if the number of available samples grows too large 2011-11-28 10:24:24 -08:00
Chris Robinson dfd02817fa Make ALSA XRUN when the number of available samples exceeds the buffer size 2011-11-28 09:12:30 -08:00
Chris Robinson 222d2363cd Don't fail to insert a map entry when the key exists and the limit is reached 2011-11-26 03:52:07 -08:00
Chris Robinson 14c117ffd3 Initialize listener matrix to identity 2011-11-26 03:37:37 -08:00
Chris Robinson d5df2ad3ce Define TlsDestructor for Windows static builds, too 2011-11-12 16:00:37 -08:00
Chris Robinson c56b2734fd Use loops to write reverb output 2011-11-02 08:15:10 -07:00
Chris Robinson 5eebe73f79 Use a loop to write the modulator effect output 2011-11-01 17:12:56 -07:00
Chris Robinson 21d400e3c6 Use panning for echo instead of specifying discrete channels 2011-11-01 17:03:11 -07:00
Chris Robinson d270188578 Append _SOFT to the ALC loopback format enums 2011-11-01 16:00:47 -07:00
Chris Robinson fcf9034c2b Calculate the listener matrix when a new orientation is specified
This is so the matrix isn't derived each time a source is updated, and it will
make supporting user-defined matrices easier.
2011-10-30 08:27:24 -07:00
Chris Robinson 956e6f95ec Don't translate the listener velocity 2011-10-30 05:49:17 -07:00
Chris Robinson 50913948a0 Don't transform/translate vectors with an invalid listener orientation 2011-10-30 05:47:14 -07:00
Chris Robinson a5c02556e6 Add a work-around for cross-compiling with MinGW 2011-10-27 21:22:00 -07:00
Chris Robinson 319f40462f Initialize/Uninitialize COM as needed 2011-10-24 22:11:16 -07:00
Chris Robinson e5374f60c9 Don't close devices that are left open on lib shutdown 2011-10-24 08:23:41 -07:00
Chris Robinson 75679c959a Don't update the device update parameters when PulseAudio does an async buffer update
It risks a deadlock, and though the UpdateSize won't change, we'll still fill
as much of the buffer as we can up to a multiple of it
2011-10-19 16:07:56 -07:00
Chris Robinson 628356a294 Set AL_STOPPED after setting AL_PLAYING when a source has nothing to play 2011-10-15 10:33:30 -07:00
Chris Robinson 0fba3b0e1e Use the local Device variable 2011-10-11 22:30:58 -07:00
Chris Robinson 92a650c827 Use the local variable instead of re-reading the source property 2011-10-11 22:29:06 -07:00
Chris Robinson 3bbde05e0e Use the square root to build the panning table instead of sin/cos 2011-10-10 09:39:02 -07:00
Chris Robinson 3324bab81e Mark a couple more arrays with restrict 2011-10-08 03:08:13 -07:00
Chris Robinson 81133769de Return the key's value from the map when it's removed 2011-10-06 06:39:13 -07:00
Chris Robinson a7d3779dfa Check for the HRTF object instead of a flag 2011-10-06 01:16:07 -07:00
Chris Robinson 75fee8c11f Add min/max/clamp methods for 64-bit int 2011-10-04 22:39:35 -07:00
Chris Robinson 9e11b0a8c6 Fix maximum BufferSize calculation 2011-10-04 22:35:01 -07:00
Chris Robinson 5861920453 Rename a variable to avoid confusion 2011-10-04 22:16:54 -07:00
Chris Robinson eb277cc578 Remove some unneeded templates and parameters 2011-10-04 09:55:36 -07:00
Chris Robinson 20fd648729 Convert samples to float when copying to the stack 2011-10-04 09:47:08 -07:00
Chris Robinson 11caba9807 Use sample frames when handling the buffer length 2011-10-04 02:08:45 -07:00
Chris Robinson 35b4b31d57 Track the temp stack buffer using sample frames instead of bytes 2011-10-03 10:06:09 -07:00
Chris Robinson a5c74e324c Make sure GCC properly errors when checking visiblity attributes 2011-10-02 22:12:31 -07:00
Chris Robinson e1cbf06653 Don't use a double when calculating the HRTF coefficients 2011-10-02 22:05:03 -07:00
Chris Robinson 5a1959f0cf Make a floating-point constant a float type 2011-10-02 22:01:51 -07:00
Chris Robinson 7f5074949e Don't use inline asm with MSVC on 64-bit 2011-10-02 21:44:08 -07:00
Chris Robinson cf10a8d321 Get rid of some unnecessary casting 2011-10-02 16:54:45 -07:00
Chris Robinson a05006b06a Rename some parameters 2011-10-02 07:31:26 -07:00
Chris Robinson eb0b96752a Remove the OriginalAlign buffer field, and calculate it as needed 2011-10-01 19:52:07 -07:00
Chris Robinson 2f453eba35 Fix potential divide-by-0 2011-10-01 08:37:42 -07:00
Chris Robinson 8b2e1fdd9a Add buffer properties to get the internal format, and the length in bytes, samples, and seconds
The provided buffer lengths correspond to the source offsets, in that the byte
length specifies the end of the byte offset (ie, when the buffer is used for a
static source, the offset will range between 0 (inclusive) and the byte length
(exclusive)). Although an application could use the AL_SIZE, AL_CHANNELS,
AL_BITS, and AL_FREQUENCY properties to find the length in samples and seconds,
the byte length cannot be reliably calculated this way.
2011-10-01 06:19:55 -07:00
Chris Robinson c99b32a8ec Use tokens matching the string 2011-10-01 04:56:45 -07:00
Chris Robinson 92f95e4d94 Add support for AL_EXT_ALAW 2011-10-01 04:55:03 -07:00
Chris Robinson d1011f8d37 Add AL_EXT_MULAW and AL_EXT_ALAW tokens to alext.h 2011-10-01 04:05:39 -07:00
Chris Robinson a486045f7c Improve index calculation for reverb modulation updates 2011-10-01 02:38:28 -07:00
Chris Robinson 0e7dc93681 Add some formatting spaces to the reverb state struct 2011-10-01 02:09:17 -07:00
Chris Robinson a7bbf7a741 Rename LOWPASSFREQCUTOFF to LOWPASSFREQREF 2011-09-30 23:07:15 -07:00
Chris Robinson bfa782f329 Define the values for F_PI and F_PI_2 directly instead of using M_PI 2011-09-30 22:53:47 -07:00
Chris Robinson bed37a148c Use raise(SIGTRAP) instead of kill(getpid(),SIGTRAP) 2011-09-30 20:46:18 -07:00
Chris Robinson bf19186223 Apply the initial wet send reverb decay before clamping the gains 2011-09-30 17:51:21 -07:00
Chris Robinson 087e75d47f Multiply by the reciprocal value instead of dividing 2011-09-29 22:29:10 -07:00
Chris Robinson 9e2600cac4 Use fast float-to-int conversions for some mixer-related code 2011-09-29 06:55:00 -07:00
Chris Robinson 3c27c24dec Set the mixer FPU mode when setting a new effect on an effect slot 2011-09-29 05:25:01 -07:00
Chris Robinson 1b81642db5 Set the mixer FPU mode for device parameter updates 2011-09-29 05:15:20 -07:00
Chris Robinson d3a6de3f86 Set the FPU into single-precision mode for mixer updates 2011-09-29 05:10:15 -07:00
Chris Robinson f4925a0e6a Use inline functions to set/restore the FPU mode for mixer updates 2011-09-29 04:03:18 -07:00
Chris Robinson b6b3ca6e6f Use inline assembly for fast float-to-int conversions 2011-09-29 03:51:46 -07:00
Chris Robinson 53572da7de Set toward-zero rounding when updating in alDeferUpdatesSOFT 2011-09-28 22:02:36 -07:00
Chris Robinson 8a0e0265bc Clamp the low-pass reference gain to -60 dB instead of -40 dB 2011-09-25 11:32:51 -07:00
Chris Robinson 0ada7b2da5 Copy only the gains needed for the channel being mixed 2011-09-24 22:24:26 -07:00
Chris Robinson 1dee1245ff A static source only has one buffer in the queue 2011-09-24 21:21:59 -07:00
Chris Robinson e9da6950ee Centralize the Lookup and Remove macros 2011-09-24 18:34:45 -07:00
Chris Robinson 12abd75aed Only warn when a symbol fails to load 2011-09-24 14:52:54 -07:00
Chris Robinson 044774ed72 Rename the MMDevAPI backend's MessageProc function 2011-09-24 13:55:44 -07:00
Chris Robinson cf56b0733b Look for and use atan2f, log10f, and floorf 2011-09-24 12:17:39 -07:00
Chris Robinson 2b0a63003f Cast a 64-bit variable to a 32-bit int 2011-09-24 00:52:58 -07:00
Chris Robinson deef98658b Don't require libm for the float-type math functions 2011-09-24 00:42:14 -07:00
Chris Robinson d9544d006b Use aluAtan instead of atan 2011-09-24 00:04:34 -07:00
Chris Robinson 6a2b2a0aed Silence MSVC warnings in the HRTF code 2011-09-23 23:16:42 -07:00
Chris Robinson 9060d0cc36 Add a WAVEFORM_FRACONE macro and minor cleanups for the ring modulator 2011-09-23 23:11:35 -07:00
Chris Robinson b4f9f89480 Use float types for the resamplers instead of double 2011-09-23 23:03:59 -07:00
Chris Robinson a9b6b284c0 Change a double type to a float 2011-09-23 22:50:33 -07:00
Chris Robinson da62f50528 Check for asinf and use it 2011-09-23 22:44:34 -07:00
Chris Robinson e84e38cc19 Make AIRABSORBGAINHF a float type 2011-09-23 22:37:20 -07:00
Chris Robinson 9266a37276 Move ConeScale and ZScale to ALu.c and alu.h, and make them floats 2011-09-23 22:33:37 -07:00
Chris Robinson ff8ee68852 Add atomic functions for GCC inline asm 2011-09-23 12:42:13 -07:00
Chris Robinson 470b506952 Use macros for float-typed PI values, to avoid manual casts everywhere 2011-09-22 11:17:01 -07:00
Chris Robinson de8a447ea5 More MSVC precision casts 2011-09-22 01:20:49 -07:00
Chris Robinson b2f4520ba1 Add a few more precision casts 2011-09-22 01:09:02 -07:00
Chris Robinson a4b1239f45 Use cosf and sinf when available
Also clear away a few more MSVC precision warnings
2011-09-22 01:00:44 -07:00
Chris Robinson 43350f9066 Silence some MSVC precision warnings 2011-09-22 00:35:08 -07:00
Chris Robinson 0988023d30 Constify some parameters 2011-09-22 00:31:42 -07:00
Chris Robinson e81e20446f Don't use the -fvisibility switch if the visibility attributes didn't work 2011-09-21 23:41:52 -07:00
Chris Robinson 64080250ff Actually check for hidden visibility 2011-09-20 18:35:27 -07:00
Chris Robinson 4e19f82a2b Try to use "default" and "hidden" visibility when "protected" and "internal" aren't available 2011-09-20 16:53:05 -07:00
Chris Robinson 97743e32c2 Remove a couple type size checks 2011-09-20 15:47:45 -07:00
Chris Robinson b8d53fb7e9 Check for SLES/OpenSLES.h along with SLES/OpenSLES_Android.h 2011-09-20 15:43:00 -07:00
Chris Robinson 390d94ec97 Define _WIN32_WINNT on the command line with _WIN32 2011-09-20 14:59:01 -07:00
Chris Robinson e01092a0da Move Sleep implementation into helper.c and emulate sched_yield for Windows 2011-09-20 14:43:53 -07:00
Chris Robinson 312e9a436d Fix indentation 2011-09-20 13:58:45 -07:00
Chris Robinson e43470da7a Make a separate log level to trace reference counts, to avoid log spam for traces 2011-09-20 12:24:23 -07:00
Chris Robinson d3e7a26db6 Fix a warning message 2011-09-20 11:58:26 -07:00
Chris Robinson 8c2a2a5c9d Trace created device and context pointers 2011-09-20 11:55:40 -07:00
Chris Robinson bf2ac6625c Avoid size_t and ssize_t in some places 2011-09-20 11:40:56 -07:00
Chris Robinson edb386eb1e Add a ConfigValueStr function, to return the string through a parameter 2011-09-19 11:29:18 -07:00
Chris Robinson cc3dd648ac Check the number of available samples before calling the capture method 2011-09-18 20:27:34 -07:00
Chris Robinson 98b2fa6cb5 Use the ALC types for the device format enum values 2011-09-18 19:43:59 -07:00
Chris Robinson b615c7d0a2 Use arrays instead of large switches to decompose formats 2011-09-18 19:06:19 -07:00
Chris Robinson 7e06a10f73 Return int and float config values through a parameter
This allows the getter functions to return whether or not the option exists
without a separate call and check.
2011-09-18 16:16:55 -07:00
Chris Robinson d46db2f648 Avoid a buffer underrun when getting the end of a config option value 2011-09-18 11:46:39 -07:00
Chris Robinson 6d77aabb23 Update a config file comment 2011-09-18 11:25:44 -07:00
Chris Robinson a7b9ee6bcb Allow loading multiple HRTF tables through a comma-separated list 2011-09-18 11:10:32 -07:00
Chris Robinson 0e5c221a3c Dynamically allocate space for HRTF tables 2011-09-18 10:09:32 -07:00
Chris Robinson 25f7c27043 Store the HRTF tables used in the device 2011-09-18 09:52:40 -07:00
Chris Robinson ec3b78e52d Fix a mutex leak in a capture open error path 2011-09-16 02:17:51 -07:00
Chris Robinson 4e903c84cf Fix comment on external HRTF file format 2011-09-15 02:27:02 -07:00
Chris Robinson ae3ec4e767 Trace compatible HRTF formats when loading instead of on error 2011-09-15 02:05:48 -07:00
Chris Robinson fd22226242 Make alcSetError static 2011-09-14 11:58:51 -07:00
Chris Robinson fe1e046ea6 Remove a couple ineffective alcSetError calls 2011-09-14 11:55:57 -07:00
Chris Robinson 2171aaa35c Use IsDebuggerPresent instead of the VC-only __try/__except 2011-09-14 02:10:42 -07:00
Chris Robinson 97024151e4 Return an error from the CaptureSamples method instead of setting it 2011-09-14 02:01:35 -07:00
Chris Robinson 62dfea43c6 Limit the minimum ALSA capture size to 100ms, and the maximum period size to 50ms 2011-09-13 23:51:22 -07:00
Chris Robinson a4f8408150 Use maxu to clamp PulseAudio's capture buffer size 2011-09-13 21:53:34 -07:00
Chris Robinson 80f2b787ef Pre-apply the deferred source offset only when it's playing or paused 2011-09-12 06:04:40 -07:00
Chris Robinson 404cfde33e Rename the ALEffect_ macros to ALeffectState_ to reflect what they work on 2011-09-12 05:59:23 -07:00
Chris Robinson 145bc6da6b Be a bit more robust while handling the global device list 2011-09-12 03:57:53 -07:00
Chris Robinson 69b10e2d7c Remove the context from being current before removing it from the device 2011-09-12 03:35:21 -07:00
Chris Robinson 25e86e8e91 Get rid of IsContext 2011-09-12 03:18:27 -07:00
Chris Robinson 780417ba39 Use VerifyDevice to validate a device handle instead of IsDevice 2011-09-12 02:17:31 -07:00
Chris Robinson 9f159bfb33 Look for __ALSOFT_TRAP_ERROR to trap both AL and ALC errors 2011-09-12 01:50:57 -07:00
Chris Robinson c2e9cda2d1 Shorten some warning messages 2011-09-12 01:21:54 -07:00
Chris Robinson 1d6ecee64f Use the active source list when stopping sources on disconnect 2011-09-12 01:11:46 -07:00
Chris Robinson 3ee2826565 Minor cleanup for applying click-removal to wet buffers 2011-09-12 01:09:01 -07:00
Chris Robinson 0ceea27b44 Remove the effect slot parameter from the effect process method 2011-09-12 00:44:52 -07:00
Chris Robinson 0dc5837b82 Fix a possible race condition when setting an effect on a slot 2011-09-11 20:51:30 -07:00
Chris Robinson b4cbb3380a Fix non-GCC CompExchangePtr implementations 2011-09-11 09:39:29 -07:00
Chris Robinson d9cb2baf4a Don't increment the context reference count when locked 2011-09-11 09:29:24 -07:00
Chris Robinson 8f456f68ff Remove GetLockedContext 2011-09-11 09:28:30 -07:00
Chris Robinson 54a421ec58 Get rid of some more GetLockedContext calls 2011-09-11 09:13:01 -07:00
Chris Robinson 6ce420afe3 Use function pointers to set type-specific effect properties 2011-09-11 08:59:47 -07:00
Chris Robinson 8028770901 Avoid accessing EAX-only reverb properties for non-EAX updates 2011-09-11 08:50:48 -07:00
Chris Robinson d51b93f869 Remove the union from the effect parameter types 2011-09-11 07:42:23 -07:00
Chris Robinson ab2b62c980 Use function pointers to set type-specific filter properties 2011-09-11 07:34:03 -07:00
Chris Robinson 97fa7cc9ac More lock avoidance 2011-09-11 06:58:49 -07:00
Chris Robinson 2648a9b011 Just reference the context when checking for extensions 2011-09-11 04:32:34 -07:00
Chris Robinson 1fdc25df75 Use a RWLock to protect access to a buffer instead of the device lock 2011-09-11 03:57:40 -07:00
Chris Robinson 4db24af59a Avoid holding the context lock for the buffer functions 2011-09-11 02:37:52 -07:00
Chris Robinson d85c12c385 Avoid holding the context lock when setting auxiliary slot properties 2011-09-11 02:01:31 -07:00
Chris Robinson ccd8cbc2a9 Make some listener properties volatile 2011-09-11 01:26:09 -07:00
Chris Robinson 1075cce7b3 Mark some source properties volatile 2011-09-11 01:18:57 -07:00
Chris Robinson 89f438df71 Small fixes to setting the source's direct filter and auxiliary send 2011-09-11 00:58:33 -07:00
Chris Robinson 97027a7ad9 Avoid holding the context lock while setting source parameters
Note that some parameters still need the lock when getting or setting them.
2011-09-11 00:47:31 -07:00
Chris Robinson 3a8ef2f0d6 Initialize source parameters before adding the source to the map 2011-09-10 21:21:07 -07:00
Chris Robinson 4c8e6dead1 Don't defer stopped and initial source state changes
Typically when these are set, an app will be releasing the buffer from the
source, possibly to immediately reuse it. There would be an extra burden placed
on the app if it was required to process updates before it could fully release
the buffer from the source.
2011-09-10 21:02:50 -07:00
Chris Robinson c19a7408d9 Add a comment about when FreeContext and FreeDevice are called 2011-09-10 20:52:19 -07:00
Chris Robinson c7b9d27b85 Trace the device pointer when warning about leftover objects 2011-09-10 20:49:16 -07:00
Chris Robinson 30510dc478 Set the device-connected flag to false first when handling a disconnect 2011-09-10 19:22:46 -07:00
Chris Robinson b788ae52b0 Condense some code 2011-09-10 19:19:32 -07:00
Chris Robinson ccbd67eab6 Clean up some code comments 2011-09-10 19:14:14 -07:00
Chris Robinson f21b1eefda Lock the lists while probing devices 2011-09-10 09:12:02 -07:00
Chris Robinson 6cd55a0a10 Clean up some comments 2011-09-10 09:00:01 -07:00
Chris Robinson 0c5c63f4ba Slightly modify a warning 2011-09-10 07:49:35 -07:00
Chris Robinson 3dcd1e793e Properly destroy contexts that are left on the device when it's closed 2011-09-10 07:35:48 -07:00
Chris Robinson 419294cddd Avoid locking the device when adding and removing contexts
Also, don't keep track of the number of contexts per device
2011-09-10 07:18:29 -07:00
Chris Robinson b9448a476e Avoid holding the list lock when not needed 2011-09-10 05:19:08 -07:00
Chris Robinson 238a747414 Warn about contexts that are errantly left current
These won't show by default in release mode, unless the log level is changed to
show warnings.
2011-09-10 03:18:31 -07:00
Chris Robinson c35433ec7a Hold an extra reference on the device for each of its contexts 2011-09-10 03:01:24 -07:00
Chris Robinson e8c9da0124 Add some reference counting to the device 2011-09-10 02:43:07 -07:00
Chris Robinson 18cf6e3210 Fix a parameter type 2011-09-10 02:13:52 -07:00
Chris Robinson c9205a58ae Use alGetEnumValue to check for supported filters and effects 2011-09-10 02:09:18 -07:00
Chris Robinson 23d693cd06 Add an option to trap device errors in a debugger 2011-09-10 01:23:59 -07:00
Chris Robinson 724ad0d893 Add an option to trap context errors in a debugger 2011-09-10 01:12:34 -07:00
Chris Robinson 7a0df8553f Allow PulseAudio's tlength to grow to make sure there's at least two updates 2011-09-09 22:30:24 -07:00
Chris Robinson 477215883f Use an array to get the device format from the user config string 2011-09-09 21:33:18 -07:00
Chris Robinson 05114815ba Avoid an unnecessary lock 2011-09-02 15:57:50 -07:00
Chris Robinson 9080d5fda0 Do an atomic compare-exchange on the global context when destroying a context 2011-09-02 02:57:21 -07:00
Chris Robinson 108b43458f Fix multichannel wet gainhf calculation 2011-09-02 02:54:00 -07:00
Chris Robinson 3dfd67680c Use an atomic exchange to get the old global context when setting a new one 2011-09-02 02:32:35 -07:00
Chris Robinson 3a2fa18744 Log the function name instead of the source file and line number 2011-09-02 02:00:37 -07:00
Chris Robinson 370c9f553d Trace the reverb buffer size 2011-09-02 01:13:22 -07:00
Chris Robinson e80aa008b2 Combine the reverb effects
Updating and processing still differs depending on whether standard or EAX
reverb is used or not. The only functional difference should be that the
allocated buffer (and subsequent offsets) take into account the modulation
and echo times.
2011-09-01 20:06:51 -07:00
Chris Robinson 812d91cbf8 Don't recreate the effect when switching between the dedicated effect types 2011-09-01 18:51:24 -07:00
Chris Robinson 942c8bf8a7 Combine the dedicated (dialog/LFE) effects 2011-09-01 18:01:14 -07:00
Chris Robinson b6b3e2f1cc Fix a comment 2011-08-31 14:01:15 -07:00
Chris Robinson ee5ff943f3 Fix a potential infinite loop when removing an effect slot 2011-08-31 13:36:47 -07:00
Chris Robinson b28f48c1bd Store the filter gains directly in the source instead of duplicate filter objects 2011-08-31 02:18:16 -07:00
Chris Robinson 49e2fa428f Avoid the context lock when generating and deleting effects and filters 2011-08-31 02:15:03 -07:00
Chris Robinson 2fcf97e207 Avoid the context lock when generating and deleting buffers 2011-08-31 01:13:02 -07:00
Chris Robinson f3ac3cd1e2 Don't set a thread priority if real-time isn't requested 2011-08-31 01:05:01 -07:00
Chris Robinson dd74757319 Add a function to initialize a RWLock 2011-08-31 01:00:19 -07:00
Chris Robinson ab0b5d9890 Hold the context lock only as needed when handling the listener 2011-08-31 00:33:33 -07:00
Chris Robinson 23f576360c Avoid locking the context while getting and setting global state 2011-08-31 00:19:27 -07:00
Chris Robinson d7bd9c7559 Use the active effect slot list for pending updates in alDeferUpdatesSOFT 2011-08-30 23:33:01 -07:00
Chris Robinson 9fb91f70aa Rename GetReffedContext to GetContextRef 2011-08-30 23:28:38 -07:00
Chris Robinson a32a3bcda8 Use a more appropriate error for trying to delete an effect slot that's in use 2011-08-30 20:57:14 -07:00
Chris Robinson 16dc73ff48 Hold the lock only as needed when generating and deleting effect slots 2011-08-30 20:49:49 -07:00
Chris Robinson 0a1321aaae Add a limit to the UIntMap size and use it for sources and effect slots 2011-08-30 20:33:47 -07:00
Chris Robinson 189add1d5a Use a separate array for the auxiliary slots in the mixer 2011-08-30 20:13:42 -07:00
Chris Robinson 755062fb76 Fix compilation with MSVC 2011-08-30 17:55:52 -07:00
Chris Robinson 7d577832cd Add functions to retrieve the source from the source map while removing it 2011-08-30 17:32:49 -07:00
Chris Robinson c4866afbe0 Use a RWLock and atomic exchanges for thunk handling 2011-08-30 17:24:58 -07:00
Chris Robinson ee60248d19 Lock the context while deleting sources only as needed 2011-08-30 00:33:01 -07:00
Chris Robinson d546813c05 Don't lock the context while checking the validity of objects 2011-08-30 00:04:02 -07:00
Chris Robinson f02d9e22d7 Do a pointer exchange when replacing the source buffer queue 2011-08-29 23:55:24 -07:00
Chris Robinson cc67f45892 Use the reference counting functions for the buffer and auxiliary slot refs 2011-08-29 23:10:02 -07:00
Chris Robinson c6a110ec59 Hold the context lock as needed while generating and deleting sources 2011-08-29 22:51:18 -07:00
Chris Robinson 7408396fd4 Use a generic int type to handle enum swaps 2011-08-29 21:30:12 -07:00
Chris Robinson da081b81c4 Do a compare-exchange to set the context error
This allows for unlocked context access when getting the error
2011-08-29 20:52:13 -07:00
Chris Robinson 500ad776ea Exchange the AL error when retrieving it 2011-08-29 20:34:47 -07:00
Chris Robinson e4a2b69b37 Make specific functions to lock/unlock UIntMap access 2011-08-29 20:21:28 -07:00
Chris Robinson 8eaa9bb469 Manually lock the map while iterating through its contents 2011-08-29 20:05:50 -07:00
Chris Robinson b283dd3682 Use a read-write lock to protect access to the UInt maps 2011-08-29 19:15:22 -07:00
Chris Robinson 4a5dd73a3c Make the source's NeedsUpdate volatile 2011-08-29 13:43:59 -07:00
Chris Robinson 950570b8c3 Do a proper exchange when setting a new state 2011-08-29 13:29:37 -07:00
Chris Robinson 72beb577b6 Lock the context as needed for the defer and process calls 2011-08-29 13:22:07 -07:00
Chris Robinson de65ee08c9 Make ALCcontext's DeferUpdates field volatile 2011-08-29 11:55:01 -07:00
Chris Robinson c06f4eb7f7 Hold a reference on the global context 2011-08-29 11:10:17 -07:00
Chris Robinson 0abe13e3ad GCC's atomic built-ins were introduced in 4.1.0, so check for that version 2011-08-29 10:51:58 -07:00
Chris Robinson 2ac808ffa2 Fix typo 2011-08-29 01:01:20 -07:00
Chris Robinson 58078e2c1e Use atomic exchanges when checking for updates to objects' internal parameters 2011-08-29 00:50:55 -07:00
Chris Robinson 01503f8a7b Use an enum instead of an boolean for some flags
They still only hold AL_TRUE or AL_FALSE, but some systems can't properly
handle atomic swaps of 1-byte types.
2011-08-29 00:03:09 -07:00
Chris Robinson aa99e1220b Increment the context reference count when locking 2011-08-28 23:49:50 -07:00
Chris Robinson 783375af56 Use a list of contexts in the device instead of an array 2011-08-28 19:28:41 -07:00
Chris Robinson 1d9f21f6dc Avoid a global list of contexts 2011-08-28 18:53:23 -07:00
Chris Robinson 0900c281a3 Check if the context is valid only if it's replacing the current thread context 2011-08-28 18:17:06 -07:00
Chris Robinson f8c4b16c30 Hold a reference on the context when being set as current on the thread
This should vastly improve efficiency for retrieving the current thread
context, as we can be assured the stored context pointer will always be valid.
2011-08-28 18:08:48 -07:00
Chris Robinson c567339718 Rename ALCcontext_Deref to ALCcontext_DecRef 2011-08-28 17:38:55 -07:00
Chris Robinson 5eceb593e9 Emulate pthread TLS functions in Windows 2011-08-28 17:21:01 -07:00
Chris Robinson 82244f298c Add reference counting to the ALC contexts 2011-08-28 15:44:03 -07:00
Chris Robinson 3ab20d4ccb Print an error for invalid speaker layout keys 2011-08-28 10:25:40 -07:00
Chris Robinson ccc2644e6b Free the WaveIn buffers' data after unpreparing them all 2011-08-25 11:33:13 -07:00
Chris Robinson 7e2155d937 Return an ALC error enum from the OpenCapture backend method 2011-08-24 14:44:15 -07:00
Chris Robinson c696d4dbb2 Return an ALC error enum from the OpenPlayback backend method 2011-08-24 14:24:48 -07:00
Chris Robinson f5195ee4f2 Remove the Buffer field from the source 2011-08-23 09:55:22 -07:00
Chris Robinson 9f25ad42c1 Explicitly set BufferListStart's prev field 2011-08-23 09:27:35 -07:00
Chris Robinson 1d4dc7d7a9 Add AL_SOFTX_deferred_updates to the context's extension list 2011-08-22 18:57:28 -07:00
Chris Robinson 65d42083e1 Prevent source and effect slot updates from occuring while updates are deferred 2011-08-22 17:13:03 -07:00
Chris Robinson 6992a67dd9 Remove some g_ prefixes 2011-08-22 07:50:08 -07:00
Chris Robinson 922f5fa081 Rename a couple variables 2011-08-22 07:44:22 -07:00
Chris Robinson 199df253ca Always use the thunk functions to get object IDs 2011-08-22 07:40:14 -07:00
Chris Robinson c1f435cc18 Don't use the thunk array for object lookups 2011-08-22 07:22:02 -07:00
Chris Robinson 858592832f Defer source offset changes requested by the app 2011-08-21 00:49:04 -07:00
Chris Robinson a97211b572 Use -1 to mark an invalid source offset instead of 0 2011-08-21 00:35:37 -07:00
Chris Robinson f196a9fc67 Defer source state changes from alSourcePlay/Pause/Stop/Rewind calls 2011-08-20 23:59:24 -07:00
Chris Robinson 7ea71d8bfa Use a separate function to set the source state 2011-08-20 06:31:10 -07:00
Chris Robinson 9b615a0add Remove some spaces 2011-08-20 04:58:50 -07:00
Chris Robinson 4540034039 Move the GUID initialization to the helper 2011-08-20 04:55:11 -07:00
Chris Robinson 29d58f3d84 Set the loopback backend functions first 2011-08-20 04:43:47 -07:00
Chris Robinson 5290c820b4 Print the ALC_DEFAULT_ALL_DEVICES_SPECIFIER when available 2011-08-20 04:16:22 -07:00
Chris Robinson 22ad15e1f1 Move some functions into a separate helper source file 2011-08-20 04:01:07 -07:00
Chris Robinson 80b8abb0ec Don't enumerate "PulseAudio Default" in the ALL_DEVICES list 2011-08-20 03:00:38 -07:00
Chris Robinson 529ff3197a Don't expose "DirectSound Default" in the ALL_DEVICES list
This is under the assumption that one of the devices in ALL_DEVICES corresponds
to the default. This is not true for all backends (eg, ALSA).
2011-08-20 02:49:15 -07:00
Chris Robinson b036bc9e04 More closely emulate pthread_once in Windows 2011-08-20 01:07:27 -07:00
Chris Robinson 9989f33fc2 Move backend sources to a separate sub-directory 2011-08-20 00:41:47 -07:00
Chris Robinson ba4456d24a Use the proper variable type 2011-08-19 23:05:54 -07:00
Chris Robinson 00718ca03f Use the string ID instead of numeral index to reference the ALSA card
The index has a habit of changing, where the ID should be more consistent.
2011-08-19 20:47:08 -07:00
Chris Robinson 6b8475b435 Add options to specify alternate ALSA device prefixes 2011-08-19 04:02:36 -07:00
Chris Robinson e51295c79e Don't scale the wet send by the number of channels
Each channel should be treated as a distinct audio source, so multiple channels
should act as multiple sources.
2011-08-19 03:59:57 -07:00
Chris Robinson b40aa8636b Remove a couple unneeded variables 2011-08-19 03:01:57 -07:00
Chris Robinson b3645df56f Delete the mutex if the device fails to open 2011-08-19 02:58:21 -07:00
Chris Robinson 711f0a29ec Make sure there's a valid backend before trying to open a device 2011-08-19 02:55:36 -07:00
Chris Robinson e8e1286572 Remove the "via *" portion of the enumerated devices 2011-08-19 01:59:03 -07:00
Chris Robinson 3d722b9ba0 Don't remove backends that fail to initialize 2011-08-19 01:54:55 -07:00
Chris Robinson 4ac0a507e0 Use only the first available backend for playback or capture devices
This should help cut down on the number of duplicate devices when enumerating
on a system that supports multiple backends. It will also avoid the problem of
backends that don't play well with others (eg, PulseAudio).
2011-08-19 01:41:16 -07:00
Chris Robinson d54908d946 Attempt to connect a context when pulseaudio initializes 2011-08-19 01:40:16 -07:00
Chris Robinson 6ba5128f72 Use separate lists for playback and capture backends 2011-08-19 00:30:05 -07:00
Chris Robinson c96a6c2b1b Remove a typedef 2011-08-18 23:58:00 -07:00
Chris Robinson 0b0588cc37 Load backend libs on init 2011-08-18 23:56:11 -07:00
Chris Robinson 742bb12498 Make DSoundLoad return a boolean 2011-08-18 01:17:42 -07:00
Chris Robinson f00017c8c1 Avoid some LP types 2011-08-18 00:31:44 -07:00
Chris Robinson d976d02936 Only attempt to load MMDevApi and DSound once, on init 2011-08-18 00:13:43 -07:00
Chris Robinson 45f0886eba Set the proper union member to 0 2011-08-17 20:44:05 -07:00
Chris Robinson 057fef88ae Remove backends that fail to initialize 2011-08-17 06:15:43 -07:00
Chris Robinson 8148626ba3 Allow the backend init functions to return failure 2011-08-17 06:05:26 -07:00
Chris Robinson 14ffb8bd9b Coalesce some mixer sample converters 2011-08-17 02:33:25 -07:00
Chris Robinson 2f8510db31 Avoid reading some more source parameters multiple times when updating 2011-08-17 01:54:09 -07:00
Chris Robinson 38a4d9a6c5 Get rid of the remaining min/max usage 2011-08-16 18:53:04 -07:00
Chris Robinson 9f5bf5f930 Rename minF/maxF/clampF to minf/maxf/clampf for consistency 2011-08-16 18:40:21 -07:00
Chris Robinson 04dad28228 Use mini/maxi/clampi and minu/maxu/clampu to replace min/max calls 2011-08-16 18:33:10 -07:00
Chris Robinson a96f817738 Avoid a float->int->float conversion 2011-08-16 06:13:55 -07:00
Chris Robinson 8a51a7ea2d Use inline minF/maxF/clampF functions instead of the __min/__max macros 2011-08-16 04:21:58 -07:00
Chris Robinson b3cb511c06 Fix wet path conehf calculation 2011-08-13 06:58:05 -07:00
Chris Robinson 7e3d02d991 Make sure there's a buffer to render loopback samples to 2011-08-12 15:44:21 -07:00
Chris Robinson 412cec73de Avoid a temporary throw-away buffer for mixing null output 2011-08-12 15:42:36 -07:00
Chris Robinson a72c0f7470 Use an existing function to clear and free the context 2011-08-12 05:40:09 -07:00
Chris Robinson 45a245d7de Round the HRTF sample step count 2011-08-11 21:43:21 -07:00
Chris Robinson 1e60481040 Use ARM Neon vector ops to apply HRTFs when available 2011-08-11 19:02:45 -07:00
Chris Robinson cda4c738e8 Don't declare i in a for() statement 2011-08-08 13:34:42 -07:00
Chris Robinson f7c176e8ca Use a message thread to handle MMDevAPI backend methods
Because MMDevAPI uses COM, CoInitialize must be called on the thread(s) where
the device calls are made. OpenAL has no such requirement, thus no guarantee
that COM will be (or remain) initialized on the calling thread(s).
2011-08-03 10:42:29 -07:00
Chris Robinson 3e2bc847d8 Avoid closing devices and deinitializing backends on process exit
They are still closed and deinitialized when the DLL is simply being detached
(via FreeLibrary), however.
2011-08-03 09:46:18 -07:00
Chris Robinson b5092a6212 Delay reading the config file on Windows, too 2011-08-03 09:37:58 -07:00
Chris Robinson 81d401eab5 Use specialized click removal loops for mono and stereo output 2011-07-30 05:43:47 -07:00
Chris Robinson f986f7dfad Wait until all headers are unprepared before freeing the buffer 2011-07-28 08:27:52 -07:00
Chris Robinson bcbc1dea25 Make the thread event auto-resetting 2011-07-28 08:23:19 -07:00
Chris Robinson c8326ab328 Remove the unnecessary wave header event
It's only ever set before sending the quit message, which will set the thread
event. It's also only waited on just before the thread event is used to signal
the end of the thread.
2011-07-28 08:18:43 -07:00
Chris Robinson 91c6bc58dc Use unnamed events for winmm 2011-07-28 08:16:09 -07:00
Chris Robinson c722579c18 Make the DSound and MMDevAPI events initially non-signaled 2011-07-24 05:29:43 -07:00
Chris Robinson 2f3bd5b5d9 Move an include to where it makes more sense to be 2011-07-23 05:53:53 -07:00
Chris Robinson fad80a4f92 Don't cast for the float versions of math functions 2011-07-23 05:52:10 -07:00
Chris Robinson 1b0a55f44c Use real-time priority by default on Windows
It's actually "time-critical", but it seems normal priority doesn't cope as
well when the system is under moderate load.
2011-07-23 05:44:55 -07:00
Chris Robinson 14b359beb0 Fix cmake check 2011-07-23 04:36:03 -07:00
Chris Robinson f32876d4bd Check for the finite() function along with _finite() 2011-07-23 04:23:54 -07:00
Chris Robinson 13f9dbe582 Look for ieeefp.h and include it when appropriate 2011-07-23 04:06:29 -07:00
Chris Robinson 8dfa2560b1 Use a better method for determining if shared functions exist 2011-07-23 00:48:19 -07:00
Chris Robinson 0c8c3b16d2 Round UpdateSize to the nearest multiple of MMDevAPI's period size 2011-07-21 18:53:15 -07:00
Chris Robinson 3d2812bd35 Reset the MMDevAPI and DSound events before starting playback 2011-07-21 17:04:33 -07:00
Chris Robinson a0e5cbc47f Fix DSound UpdateSize calculation when NumUpdates is clamped 2011-07-21 16:53:25 -07:00
Chris Robinson d5ddc6acee Make sure some state values are finite 2011-07-21 16:18:21 -07:00
Chris Robinson 30750d8763 Create and close the DSound event handle with the device 2011-07-21 03:38:43 -07:00
Chris Robinson 3e4659076a Use an event to signal the mmdevapi backend to mix
Also, use a multiple of the default period size for the update size, since the
event apparently triggers on period boundaries.
2011-07-21 03:32:20 -07:00
Chris Robinson 7cd6327969 Use DSound notifications to wake up the mixer thread 2011-07-21 02:48:09 -07:00
Chris Robinson 77128d298e Add an option to emulate EAX reverb using standard reverb 2011-07-20 22:53:46 -07:00
Chris Robinson 1e8a379a03 Add a config option to boost reverb output 2011-07-20 22:26:16 -07:00
Chris Robinson b889823b21 Set real-time priority for the WaveOut backend 2011-07-20 19:59:47 -07:00
Chris Robinson 9723c553b6 Scale reverb output based on the number of channels
A scale of sqrt(2/numchannels) is used so the perceived volume matches as if it
was stereo output, which seems to match other implementations.
2011-07-20 19:57:24 -07:00
Chris Robinson 6bfb0371eb Make sure some effect property values are finite 2011-07-20 03:33:06 -07:00
Chris Robinson 1348bf2476 Make sure some listener property values are finite 2011-07-20 03:27:05 -07:00
Chris Robinson 36446f3a58 Make sure source vectors are finite values 2011-07-20 03:18:46 -07:00
Chris Robinson 5a484f7493 Look for an isfinite function 2011-07-20 03:18:11 -07:00
Chris Robinson 244d60fb96 Slightly improve the gain change delta calculation 2011-07-19 00:43:31 -07:00
Chris Robinson 0ae9638aa5 Add a lost comment 2011-07-18 17:44:37 -07:00
Chris Robinson 55b893c784 Improve buffer metrics when using the MMDevAPI backend 2011-07-17 19:42:03 -07:00
Chris Robinson c9b6bc848f Simplify some reverb update calculations 2011-07-17 17:51:13 -07:00
Chris Robinson f97310c757 Apply the click removal offset first, before decreasing it 2011-07-17 07:23:19 -07:00
Chris Robinson 87b3520d02 Fix GetMovingHrtfCoeffs return type 2011-07-17 07:16:10 -07:00
Chris Robinson 887de0523f Scale the delay down once for the post-stepping mix loop 2011-07-17 02:34:35 -07:00
Chris Robinson a7a1f802a8 Invert the device channel map 2011-07-17 01:55:25 -07:00
Chris Robinson 163cc62a00 Add a couple new functions to handle deferred updates
Currently no-ops, they will be used in place of alcSuspendContext and
alcProcessContext for batching updates since the mentioned functions have no
coherent functionality between system implementations.
2011-07-16 16:59:20 -07:00
Chris Robinson 5f566ebf05 Fade between HRTF coefficients, to reduce noise from sudden changes 2011-07-16 16:24:01 -07:00
Chris Robinson 1622986467 Fix some format types 2011-07-16 04:21:40 -07:00
Chris Robinson ce6db53e5f Apply the slot gain during the effect update method 2011-07-16 03:35:51 -07:00
Chris Robinson 292ea0607d Request a slot update when the gain changes 2011-07-16 03:15:28 -07:00
Chris Robinson 3bd7256dc8 Pass the effect slot to the effect update method 2011-07-16 03:02:16 -07:00
Chris Robinson da2429a1d0 Allow effect slots to be updated asynchronously
Updates when the slot changes effect type is still sychronous, however, to
ensure a proper state for the Process method call. Fixing this would
essentially require all effects to work from the same state.
2011-07-16 02:41:02 -07:00
Chris Robinson 54b5f35125 Warn about HRTF being disabled in the compatibility check function 2011-07-16 00:39:13 -07:00
Chris Robinson 23bfb03d2a Improve the external HRTF data file format
Although more parameters are read from the file, some must currently conform to
specific values. In the future, these restrictions may be lifted.
2011-07-16 00:22:01 -07:00
Chris Robinson 7381b1a696 Make the sample rate part of the HRTF struct 2011-07-15 23:45:03 -07:00
Chris Robinson 99bcb7a0f6 Use both root paths when looking for Wine headers, and disable them by default 2011-07-13 01:49:43 -07:00
Chris Robinson 17773c5644 Rename the ERROR macro to ERR 2011-07-13 01:43:00 -07:00
Chris Robinson e1e47c6e43 Fix CMAKE_FIND_ROOT_PATH in XCompile.txt 2011-07-13 01:31:17 -07:00
Chris Robinson 2bb3d31d5c Avoid holding the global list lock while calling some capture methods 2011-07-13 01:21:30 -07:00
Chris Robinson 2a7bf1234b Use the correct send slot when mixing 2011-07-11 22:32:27 -07:00
Chris Robinson e56e462317 Fix default room air absorption 2011-07-11 22:07:37 -07:00
Chris Robinson 3245e992c3 Always pass the slot's effect to the update method 2011-07-11 01:51:24 -07:00
Chris Robinson 1299fa2801 Avoid some alListenerfv calls while under the context lock 2011-07-11 01:21:29 -07:00
Chris Robinson 52c78194b3 Update the context when an auxiliary slot is modified 2011-07-11 01:13:58 -07:00
Chris Robinson 58466a304b Use a flag to signifiy that all sources need updating 2011-07-11 01:05:42 -07:00
Chris Robinson 51c09e94b8 Avoid some alGet* duplication 2011-07-11 00:51:18 -07:00
Chris Robinson 22ccaf7f4a Trace device update size and count 2011-07-11 00:05:53 -07:00
Chris Robinson b237ebba44 Trace when the format is being requested 2011-07-10 23:57:45 -07:00
Chris Robinson 6038bbd6ec Avoid redundantly tracing HRTF status 2011-07-10 23:50:55 -07:00
Chris Robinson 84a67ef766 Trace device parameters when updating 2011-07-10 22:54:39 -07:00
Chris Robinson e03df97919 Accept "openal soft" and "openal-soft" to open a device 2011-07-10 22:36:10 -07:00
Chris Robinson 7bedd9ef7a Replace some AL_PRINT calls with proper logging 2011-07-10 22:32:44 -07:00
Chris Robinson 72cd670636 Use logging macros in the opensl backend 2011-07-10 22:31:24 -07:00
Chris Robinson 9b028e76d2 Use logging macros in the wave backend 2011-07-10 22:29:25 -07:00
Chris Robinson 479b4c9531 Use logging macros in the solaris backend 2011-07-10 22:28:34 -07:00
Chris Robinson 28f7d2151e Use logging macros in the winmm backend 2011-07-10 22:27:46 -07:00
Chris Robinson cd1560e2b0 Use logging macros in the coreaudio backend 2011-07-10 22:26:02 -07:00
Chris Robinson 87b4c93af9 Use logging macros for the portaudio backend 2011-07-10 22:22:38 -07:00
Chris Robinson 0393d4ba71 Use logging macros in the sndio backend 2011-07-10 22:20:34 -07:00
Chris Robinson 717a0592ba Use logging macros in the oss backend 2011-07-10 22:17:12 -07:00
Chris Robinson a67aff220f Use logging macros in the null backend 2011-07-10 22:15:16 -07:00
Chris Robinson e47e9c5ac3 Use logging macros in the mmdevapi backend 2011-07-10 22:13:57 -07:00
Chris Robinson 4cd9367520 Use logging macros in the dsound backend 2011-07-10 21:57:32 -07:00
Chris Robinson c4ac014a0c Use the logging macros for the alsa backend 2011-07-10 21:53:32 -07:00
Chris Robinson 21c99ca1e8 Use the logging macros in the pulseaudio backend 2011-07-10 21:51:41 -07:00
Chris Robinson 2cd3e9bcdb Use the logging macro for thunk errors 2011-07-10 21:49:01 -07:00
Chris Robinson f56fbbb777 Use logging macros for config handling 2011-07-10 21:44:42 -07:00
Chris Robinson b64156aa2b Add some macros for logging at various levels, and use them 2011-07-10 21:30:25 -07:00
Chris Robinson 5cb4129bc7 Use a per-device lock instead of a global one 2011-07-06 05:07:36 -07:00
Chris Robinson bed264ad77 Use a Lock/UnlockDevice function instead of locking a NULL context 2011-07-06 02:58:43 -07:00
Chris Robinson f8be0448b0 Don't modify the source's WetGain[HF]Auto because of a slot's state 2011-07-06 00:07:37 -07:00
Chris Robinson 75fa891e0a Store and use the send slot in the source params 2011-07-05 14:14:20 -07:00
Chris Robinson 21574f7e16 Scale the WetGain parameter directly instead of on each application 2011-07-05 14:00:11 -07:00
Chris Robinson 0903a7f1e0 Apply wet path calculations with their dry path equivalents
The auxiliary slot (and related effect) properties are retrieved at the same
time as the source properties. This will help ensure each property is accessed
from the source and slot only once.
2011-07-05 11:00:52 -07:00
Chris Robinson c8d385c998 Use a helper to tell if an effect type is reverb 2011-07-05 04:36:41 -07:00
Chris Robinson 4c35bfbc10 Read the source's RoomRolloffFactor once 2011-07-05 03:55:16 -07:00
Chris Robinson 83e322c5f3 Force front-centered for mono sounds that comes from the listener under HRTF 2011-07-05 03:36:14 -07:00
Chris Robinson 6020b964c7 Fix some formatting 2011-07-04 22:36:51 -07:00
Chris Robinson b2d5367830 Fix rolloff calculations for auxiliary sends 2011-07-04 07:32:23 -07:00
Chris Robinson d307ee8c2f Implement a lerped lookup of the HRTF coefficients/delays
Code supplied by Christopher Fitzgerald
2011-07-04 07:20:38 -07:00
Chris Robinson 202f57552a Use a proper enum for the distance model 2011-07-03 19:39:19 -07:00
Chris Robinson 5ba864b6da Calculate the wet gain decay time attenuation with linear values 2011-07-03 19:22:14 -07:00
Chris Robinson 30e97d5223 Read the RoomRolloffFactor property with the other properties 2011-07-03 03:34:40 -07:00
Chris Robinson 69be02c0fd Set the default auxiliary sends to MAX_SENDS
Since the property is ALC_MAX_AUXILIARY_SENDS, it makes more sense to default
it to the maximum and let the app lower it on request.
2011-07-03 03:18:18 -07:00
Chris Robinson eefda551de Properly handle input samples for changes with the FIR coefficients
This makes sure previously processed samples are not changed due to changes in
the FIR filter. It also applies the coefficients in ascending order with the
values, to improve caching.
2011-07-03 02:14:33 -07:00
Chris Robinson ac7ca07221 Get rid of a couple typedefs 2011-07-02 21:33:53 -07:00
Chris Robinson b043390ec2 Update a trace 2011-07-02 19:02:45 -07:00
Chris Robinson 4c94ba6d8a Properly save the original FPU state when using _controlfp 2011-07-02 02:56:06 -07:00
Chris Robinson 7b87519c15 Move functions to where they're used 2011-07-02 02:54:05 -07:00
Chris Robinson 14bfe0293a Use separate functions to find valid ALC loopback device formats 2011-07-02 02:51:33 -07:00
Chris Robinson 931d422443 Use separate ALC enums for loopback device formats 2011-07-02 02:25:34 -07:00
Chris Robinson 895150eebe Update HRTF coefficients 2011-07-01 05:08:57 -07:00
Chris Robinson a5ccae5779 Do air absorption with linear gain values
This copies the way the calculation is done with the wet path
2011-07-01 02:30:15 -07:00
Chris Robinson 1635132fd5 Use a template function to handle processing ring modulator types 2011-07-01 01:48:04 -07:00
Chris Robinson 048aee66cf Avoid accessing source properties more than once when updating parameters 2011-07-01 01:46:56 -07:00
Chris Robinson aea2868ce1 Avoid calling some al[Get]Effect functions while the context is locked 2011-06-30 20:59:35 -07:00
Chris Robinson 723755788d Rename Suspend/ProcessContext since they are locking a mutex 2011-06-30 18:10:04 -07:00
Chris Robinson 032d0836a7 Declare some variables closer to where they're used 2011-06-29 23:32:48 -07:00
Chris Robinson 5736bbc3e4 Add a source property to skip channel virtualization for multi-channel buffers
With virtualization disabled, channels are mapped directly from input to output
and any input channel that is missing an output is dropped. Any virtualization
effects (such as HRTF filters) for positional cues are also skipped.

The idea is to allow applications a way to play pre-filtered audio that channel
vitualization effects could interfere with.
2011-06-29 23:18:49 -07:00
Chris Robinson 1b773a8585 Select the correct resampler 2011-06-28 00:30:00 -07:00
Chris Robinson 3f0214ed6b Implement capture support for the CoreAudio backend 2011-06-27 23:49:17 -07:00
Chris Robinson ea83608ee4 Avoid a junk sample at the start of the ring buffer, and allocate the buffer
memory at the end of the ring buffer itself.
2011-06-27 14:30:45 -07:00
Chris Robinson 8cae1aee80 Clamp PortAudio output to stereo 2011-06-27 01:00:34 -07:00
Chris Robinson 3db061e670 Combine the two pa_load definitions into one 2011-06-26 15:40:15 -07:00
Chris Robinson efaaf9b5f9 Don't bother with a per-context lock for disconnecting devices 2011-06-26 01:26:50 -07:00
Chris Robinson c451bf370d Remove an incorrect ProcessContext call 2011-06-26 01:22:29 -07:00
Chris Robinson 212b187447 Add a function to check if a device is compatible with the Hrtf filter 2011-06-25 21:39:36 -07:00
Chris Robinson ac9a6e891f Fix the cross-compile toolchain script
CMAKE_INSTALL_PREFIX is now properly set, and the pkgconfig env vars are set so
it won't find .pc files for the default compiler.
2011-06-25 13:49:40 -07:00
Chris Robinson 1fc44d5788 Select the mixer during a source update 2011-06-25 00:13:56 -07:00
Chris Robinson 913c70557d Fix formatter for size_t 2011-06-24 15:03:29 -07:00
Chris Robinson e8b785677d Make sure to clear HAVE_SNDIO before checking backends 2011-06-23 18:39:16 -07:00
Chris Robinson 5c17eb69cb Fix comment typo 2011-06-23 17:02:57 -07:00
Chris Robinson f6d1e0f299 Avoid setting a thread priority for OpenBSD
It apparently causes a negative performance impact regardless of which values
are set.
2011-06-23 15:42:35 -07:00
Chris Robinson e42bdc2022 Fix device Update parameters for sndio 2011-06-23 15:42:16 -07:00
Chris Robinson cf44f499b8 Be silent about failing to load shared libs 2011-06-22 20:48:45 -07:00
Chris Robinson 39c984fda8 Actually load libsndio.so 2011-06-22 20:45:45 -07:00
Chris Robinson 9c23bc53b7 Update the default drivers config option comment 2011-06-22 19:31:27 -07:00
Chris Robinson 96b6cd46e6 Add a FreeBSD sndio backend 2011-06-22 19:29:13 -07:00
Chris Robinson bc6e3879d4 Properly handle the PortAudio lib in OpenBSD 2011-06-22 16:43:37 -07:00
Chris Robinson 3dcfd0dc98 Use 'Distance' for the unclamped distance value, since the clamped value is
only used for distance attenuation
2011-06-21 12:55:21 -07:00
Chris Robinson 40b8934487 Calculate wet gain when needed 2011-06-18 16:45:26 -07:00
Chris Robinson 0d39ac6528 Allow alThunkAddEntry to return a proper error 2011-06-17 23:59:25 -07:00
Chris Robinson 157e1394d6 Open the log file with config initialization 2011-06-17 18:54:05 -07:00
Chris Robinson edf78b077e Use separate lines for HRTF history and values 2011-06-17 16:22:39 -07:00
Chris Robinson 753e64c76c Do stereo duplication in two passes
First using the rear channels, then using the normal stereo channels
2011-06-17 16:20:18 -07:00
Chris Robinson 3a072a294d Avoid resetting a flag that isn't on 2011-06-17 14:59:10 -07:00
Chris Robinson 23ae3c69b5 Handle the device's disconnected status earlier when playing a source 2011-06-17 13:21:02 -07:00
Chris Robinson bef9c5e2d5 Avoid some al[Get]Effect* calls from under the context lock 2011-06-17 13:07:47 -07:00
Chris Robinson 58add24830 Call al[Get]Filterf directly from the *v equivalents
There are no filter parameters which take multiple values, and besides some
duplicated checks, they call straight through anyway.
2011-06-16 14:40:37 -07:00
Chris Robinson fdffbfd2c4 Use the predefined filter macros instead of hardcoded values 2011-06-16 10:53:01 -07:00
Chris Robinson 357f841e88 Fix namespacing of EFX filter property values 2011-06-16 10:52:17 -07:00
Chris Robinson 7d73ac4405 Avoid calling some AL functions from under the context lock
There are still some more occurances to clear out (deletion in gen error,
effects, some filters), which shall be coming up. There is a possibility for a
deadlock between the listlock and the global/context lock, if another attempt
to get the listlock is made while under the context lock.
2011-06-16 09:14:41 -07:00
Chris Robinson c429a5be25 The DirectSound DLL is always loaded by the Win32 API 2011-06-16 06:17:57 -07:00
Chris Robinson fba58fb1f0 Make sure user configuration is always initialized for Windows 2011-06-16 05:36:16 -07:00
Chris Robinson dad7f1cf0f Prefer DllMain when in Windows 2011-06-16 05:34:13 -07:00
Chris Robinson 8bee7a2b7f Don't suspend individual contexts when updating 2011-06-16 05:29:38 -07:00
Chris Robinson 632b4e6332 Avoid some channel order duplication 2011-06-16 00:00:40 -07:00
Chris Robinson f26798bd19 Remove some unnecessary casts 2011-06-15 23:52:21 -07:00
Chris Robinson 7f4dcefc80 Remove the databuffer functions and structs
It's been disabled for a while now, and not likely to be re-enabled.
2011-06-15 23:46:35 -07:00
Chris Robinson d702cb9b49 Avoid an unnecessary lock when retrieving the current context 2011-06-15 23:22:34 -07:00
Chris Robinson 2081228055 Declare some variables where they're used 2011-06-15 06:45:51 -07:00
Chris Robinson f85283c036 Don't prematurely set the HRTF flag 2011-06-15 02:00:41 -07:00
Chris Robinson 14d6ab384d Use a flag to specify when a device is running 2011-06-15 01:59:07 -07:00
Chris Robinson 62036745f3 Separate config init from library init
OSX has a bug where the file descriptor resource limit can't be increased once
certain functions (such as fopen) are used. Since lib initialization happens in
a global constructor and includes opening and parsing config files, it prevents
apps from being able to increase the rlimit once the lib is loaded, which is
usually before main().

By delaying config init, it allows apps the opportunity to change the rlimit
before calling certain OpenAL functions.
2011-06-14 09:43:33 -07:00
Chris Robinson 264274685a Throw an error when no global initialization is available 2011-06-14 08:47:53 -07:00
Chris Robinson 1babf28491 Unset the local context if it's being destroyed 2011-06-14 08:27:09 -07:00
Chris Robinson 5a15dc4ddf Avoid multiple list searches when destroying devices and contexts 2011-06-14 07:04:08 -07:00
Chris Robinson 0f782b385b Create TLS for contexts earlier 2011-06-14 06:57:51 -07:00
Chris Robinson 1534fcfafd Remove some unneeded includes 2011-06-14 05:06:39 -07:00
Chris Robinson 1c7ed6e693 Avoid using function pointers directly in the ALSA backend
Also make it use the lib wrappers
2011-06-14 05:00:59 -07:00
Chris Robinson 0c5c56cf88 Avoid using function pointers directly in the PulseAudio backend
Also, switch to the lib wrapper functions
2011-06-14 04:45:52 -07:00
Chris Robinson 53c5275aa4 Use a proper enum for the probe type 2011-06-14 04:02:58 -07:00
Chris Robinson c93d7a1721 Open the logfile in append/text mode 2011-06-14 03:04:00 -07:00
Chris Robinson 9c7dad1bdf Combine multiple probe functions into one 2011-06-14 02:28:15 -07:00
Chris Robinson bfe09b933e Trace errors in the opensl callback 2011-06-14 01:51:44 -07:00
Chris Robinson 823f5779e1 Set format_pcm according to the device format 2011-06-14 01:48:13 -07:00
Chris Robinson 96a18ba1d5 Make fewer assumptions on the OpenSL buffer count 2011-06-12 19:36:28 -07:00
Chris Robinson b8bb41107c Don't be silent when an OpenSL error occurs 2011-06-12 18:10:01 -07:00
Chris Robinson ccffb7e380 Add missing semicolon 2011-06-12 16:30:51 -07:00
Chris Robinson 39088b2035 Uninline the timeGetTime wrapper 2011-06-12 05:04:36 -07:00
Chris Robinson 8db05b8c17 Avoid defining a macro twice 2011-06-12 04:45:03 -07:00
Chris Robinson ba069ded40 Add an OpenSL backend
Currently for Android's OpenSL ES implementation
2011-06-12 04:41:42 -07:00
Chris Robinson 6b8209fdc9 Cleanup dynamic loading in the PortAudio backend 2011-06-12 04:37:32 -07:00
Chris Robinson ccf62fc525 Add wrappers to handle dyanmically loading libs 2011-06-12 04:36:24 -07:00
Chris Robinson 6bfab6427a Avoid calling some function pointers directly 2011-06-12 03:55:02 -07:00
Chris Robinson aab4f85383 Change HRTF table user input format 2011-06-03 03:15:13 -07:00
Chris Robinson 99b9208896 Improve default HRTF filter delays
They're now rounded from the source's float delays
2011-06-03 03:09:13 -07:00
Chris Robinson 7402e3743d Readd the default HRTF filter (based on the diffuse set) 2011-06-03 01:47:33 -07:00
Chris Robinson 7ddfacb58f Use a minimum phase HRTF data set
This reduces the coefficient size from 128 down to 32, with a set of delays
2011-06-03 01:06:00 -07:00
Chris Robinson c7a80418d9 Separate device-type parameter checks for alcGetIntegerv 2011-05-29 23:20:33 -07:00
Chris Robinson e98104dac8 Avoid holding locks once the context is removed from the lists 2011-05-29 23:03:59 -07:00
Chris Robinson 884f668855 Uninline a couple more functions 2011-05-29 03:28:34 -07:00
Chris Robinson 2bbe9d1b1f Uninline a couple mutex wrapper functions 2011-05-29 03:08:26 -07:00
Chris Robinson 91c3cffe41 Protect the mixer while the device's context array is being updated 2011-05-29 03:04:32 -07:00
Chris Robinson 60a55aa897 Hold the global lock while the device is being updated 2011-05-29 02:56:00 -07:00
Chris Robinson 9325a78043 Use a separate lock for device and context list accesses 2011-05-29 02:26:46 -07:00
Chris Robinson cd0e80126c Require a lock for IsDevice calls 2011-05-28 22:21:11 -07:00
Chris Robinson 1778c0d7d8 Get the device format directly from the string 2011-05-28 21:37:33 -07:00
Chris Robinson b07901f1a0 Set the subformat type when an unknown one is encountered 2011-05-28 21:21:37 -07:00
Chris Robinson a15a1abb21 Trace the format that is set when reporting HRTF disabled 2011-05-28 20:40:46 -07:00
Chris Robinson 7da2eea8f0 Add a 5.1 device format that uses side channels instead of back 2011-05-28 19:35:32 -07:00
Chris Robinson 596a1af7d9 Switch to the diffuse HRTF data set 2011-05-26 15:43:26 -07:00
Chris Robinson 6b6a7757ce Clear HRTF coefficients for the LFE channel 2011-05-25 19:04:19 -07:00
Chris Robinson 8a6266239c Rename Quad to Quadraphonic 2011-05-24 14:34:53 -07:00
Chris Robinson d83af679bd Fix handling of GUID definitions 2011-05-22 13:29:32 -07:00
Chris Robinson af005668da Avoid using an unnecessary temp variable 2011-05-21 23:14:55 -07:00
Chris Robinson d07e94056d Use libuuid when it has MMDevApi's UIDs 2011-05-21 21:57:44 -07:00
Chris Robinson 4d7aab1df4 Make some declarations static or static const 2011-05-21 21:28:27 -07:00
Chris Robinson c3cbfecbae Remove duplicate macros 2011-05-21 21:16:30 -07:00
Chris Robinson a9d9553fff Use a multi-dimensional array for the panning LUT 2011-05-21 17:45:54 -07:00
Chris Robinson 5cdf774ea7 Use the stored sample and channel count 2011-05-21 17:36:54 -07:00
Chris Robinson b75d4beb1a Dedicated dialog should go in front 2011-05-20 09:50:31 -07:00
Chris Robinson 35a0430c81 Store the separate effect parameters in a union 2011-05-20 09:36:36 -07:00
Chris Robinson 1b5caa4112 Check against the correct limits 2011-05-20 09:19:53 -07:00
Chris Robinson cbbb04eb40 Always fully normalize the position when using HRTF 2011-05-20 07:58:05 -07:00
Chris Robinson 7396aaf2c2 Fix HRTF elevation calculation 2011-05-19 17:57:14 -07:00
Chris Robinson 555ccce4ee Update the device update fields after initializing the MMDevApi device 2011-05-19 15:55:27 -07:00
Chris Robinson b838d123f8 Multiply the history offset in the filtering funcitons 2011-05-18 18:52:47 -07:00
Chris Robinson 1d3926e187 Remove some more duplication 2011-05-18 18:49:41 -07:00
Chris Robinson 194f8fc791 Uninline lpCoeffCalc 2011-05-18 18:42:25 -07:00
Chris Robinson c316766144 Use lerp() in a couple more places 2011-05-18 18:09:04 -07:00
Chris Robinson c00b2f9254 Avoid looking in the buffer queue to find the frame size 2011-05-18 17:02:18 -07:00
Chris Robinson 35c38e0b5e Update alsoftrc.sample 2011-05-18 15:13:11 -07:00
Chris Robinson 87ef4eeb36 Add cmake options to require specific backends
Note that this alone doesn't gurantee run-time availability for backends libs
that are loaded at run-time.
2011-05-18 05:34:21 -07:00
Chris Robinson 06f5f0b7c8 Make mixers channel-count agnostic
As a result, this combines/removes a bunch of mixers
2011-05-17 13:25:15 -07:00
Chris Robinson 9f91ff4734 Move the channel loop to the outside of the sample mixing loop 2011-05-17 12:49:26 -07:00
Chris Robinson f7936b4709 Avoid multiple getenv calls to the same var 2011-05-17 10:15:53 -07:00
Chris Robinson 60965e3a6c Determine the function export attribute using CMake 2011-05-17 09:13:55 -07:00
Chris Robinson b5cb2d633c Avoid including config.h in the utilities 2011-05-17 09:03:54 -07:00
Chris Robinson 810734718b Always look for libwinmm 2011-05-17 08:36:26 -07:00
Chris Robinson ca48c2e51b Modify the cross-compile toolchain file to only need a HOST option 2011-05-17 06:53:36 -07:00
Chris Robinson 86ed2ee72b Add mmdevapi to alsoftrc.sample 2011-05-17 05:04:08 -07:00
Chris Robinson 2abeed069b Improve handling of format support for MMDevApi 2011-05-17 04:54:09 -07:00
Chris Robinson 569106acd3 Fix COM initialization with MMDevApi 2011-05-16 10:31:38 -07:00
Chris Robinson 507d2f8afb Properly set the channel order for WinMM playback 2011-05-16 10:28:17 -07:00
Chris Robinson 54769a823e Implement playback using MMDevApi 2011-05-16 04:13:37 -07:00
Chris Robinson cf3287f8ec Fix closing of waveOut devices 2011-05-16 03:16:47 -07:00
Chris Robinson c5fbc9f05c Define GUIDs in ALc.c instead of the individual backend source files 2011-05-15 21:47:50 -07:00
Chris Robinson 2940b0f390 Add a skeleton backend for MMDevApi 2011-05-15 20:26:25 -07:00
Chris Robinson 442e41f825 Add an option to use Wine's Windows headers when building under MinGW 2011-05-15 20:25:09 -07:00
Chris Robinson e95b718cf4 Improve handling of device channels with DSound 2011-05-15 05:24:27 -07:00
Chris Robinson 143c08bfcb Use channel config strings in log output 2011-05-15 04:49:18 -07:00
Chris Robinson b3902c8950 Add functions to get strings from the device format 2011-05-15 04:03:15 -07:00
Chris Robinson 7210796cb8 Combine multiple loops 2011-05-15 02:12:42 -07:00
Chris Robinson 949cfe2ceb Re-enable duplication for stereo sounds 2011-05-15 00:18:28 -07:00
Chris Robinson c39f27d5f8 Set the duplicate-stereo flag properly after the device format is reset 2011-05-13 21:27:46 -07:00
Chris Robinson c9ac4f06ff Remove an unused macro and rename another 2011-05-07 04:35:49 -07:00
Chris Robinson 674b14faa3 Make a couple more pointers restricted 2011-05-07 04:32:53 -07:00
Chris Robinson a8fdcd30a5 Some minor cleanups 2011-05-07 04:31:43 -07:00
Chris Robinson db3cb23c1d Add an option to reverse Z panning of mono sources
Applications that are not built around OpenAL's orientation system need to
convert their given vectors to it. Depending on how this is done, it can lead
to proper stereo (left-right) panning with improper surround sound (front-back)
panning, which thusly sounds correct with stereo output and incorrect with 4+
channel output.

This option is intended to help fix playback of such applications on surround
sound systems, without having to resort to forcing stereo output.
2011-05-07 03:54:46 -07:00
Chris Robinson 7972f7a2da Combine duplicate arrays 2011-05-06 23:25:15 -07:00
Chris Robinson 8c3d50be28 Skip LFE with HRTF output 2011-05-06 04:46:21 -07:00
Chris Robinson 4198f3062c Remove the device channel matrix 2011-05-06 04:42:54 -07:00
Chris Robinson c2a6efc77a Use the panning algorithms to build the multi-channel source matrix
This prevents the stereo-duplication option from working. It should hopefully
be restored soon.
2011-05-06 04:37:10 -07:00
Chris Robinson 6928487aa1 Remove some redundancy 2011-05-06 02:53:22 -07:00
Chris Robinson e608317e70 Fix device flag value 2011-05-06 01:59:32 -07:00
Chris Robinson 64b5d7a519 Check the correct flag in the dsound backend 2011-05-06 01:53:13 -07:00
Chris Robinson eef41e76d7 Don't clear the request flag if a new context doesn't specify a frequency 2011-05-06 01:50:19 -07:00
Chris Robinson d583f7f307 Update sources automatically when device parameters change 2011-05-06 01:06:01 -07:00
Chris Robinson f97eca34df Minor source update fixes 2011-05-06 00:51:12 -07:00
Chris Robinson 1363e044ac Remove the head_dampen option
Now that HRTF is properly supported, it's not needed
2011-05-06 00:38:10 -07:00
Chris Robinson 9967d4eadd Mark a couple more pointers as restricted 2011-05-06 00:33:58 -07:00
Chris Robinson 2972de1047 Use function pointers to call the source mixer 2011-05-06 00:20:40 -07:00
Chris Robinson 38db4115fc Fix a comment 2011-05-05 22:36:26 -07:00
Chris Robinson 0e824914b2 Use signed samples for 8-bit storage 2011-05-05 18:54:10 -07:00
Chris Robinson 7832a6d1d8 Add a warning when HRTF is disabled 2011-05-03 20:23:35 -07:00
Chris Robinson 0cce93a1f8 Fix WinMM backend compilation 2011-05-03 17:12:05 -07:00
Chris Robinson 679f2480c9 Add a device flag for channel config requests 2011-05-03 17:11:07 -07:00
Chris Robinson db7ca2c7ca Combine the mono and multi-channel mixer templates 2011-05-03 16:34:28 -07:00
Chris Robinson e2850df07e Use the same filters for mono sources and multi-channel 2011-05-03 16:18:46 -07:00
Chris Robinson 48def2fecb Properly handle elevations that don't have angles up to 180 2011-05-03 03:15:28 -07:00
Chris Robinson ba236ad108 Add a frequency-request device flag 2011-05-03 02:29:26 -07:00
Chris Robinson 9a28402b90 Fix non-spatialized mono sources 2011-05-02 18:33:32 -07:00
Chris Robinson 8c705cc4ea Mark some mixer pointers as restricted 2011-05-02 17:14:57 -07:00
Chris Robinson 24aaa24b5c Check for the C99 restrict keyword
GCC does not default to C99 mode yet, so does not know restrict, however it
still allows using __restrict in its place
2011-05-02 17:06:32 -07:00
Chris Robinson fd795a7eeb Add casts to silence some warnings 2011-05-02 15:04:35 -07:00
Chris Robinson abdc254e55 Use a pointer-to-arrays for the local HRTF coefficients 2011-05-02 13:35:13 -07:00
Chris Robinson f843b7e2e3 Implement HRTF mixers for multi-channel sources 2011-05-02 02:22:30 -07:00
Chris Robinson eea86ab891 Use flags instead of separate bools 2011-05-01 18:18:37 -07:00
Chris Robinson d7a452588c Document the hrtf config option 2011-05-01 14:16:05 -07:00
Chris Robinson 56d9418119 Add an HRTF filter for mono sources
The data is based on the KEMAR HRTF data provided by MIT, which can be found at
<http://sound.media.mit.edu/resources/KEMAR.html>. The compact measurements
were used. See hrtf_tables.inc for more information.

The filter is only available for stereo output, using a 44100hz playback rate.
Note also that it currently only applies to mono sounds, and the cf_level and
head_dampen config options are ignored while it is active.
2011-05-01 13:59:44 -07:00
Chris Robinson 0109104027 Add some source fields for HRTF data 2011-05-01 13:21:56 -07:00
Chris Robinson c9c965ef85 Add a device flag for enabling HRTF 2011-05-01 13:19:23 -07:00
Chris Robinson af3adf5244 Be silent if the PulseAudio context fails to connect while probing 2011-04-30 17:58:37 -07:00
Chris Robinson 43f97b4da1 Use the new format names when possible 2011-04-29 11:55:59 -07:00
Chris Robinson 63f07db303 Add the SOFT moniker for the loopback extension functions and enums 2011-04-28 11:37:23 -07:00
Chris Robinson 2e9ca89dd3 Reorder some cases 2011-04-27 21:05:46 -07:00
Chris Robinson 8586aeabe8 Minor fix for 24-bit conversions to float and double 2011-04-27 19:26:27 -07:00
Chris Robinson c031b4d5d0 Allow MSVC to build a static lib
Based on a patch by Michał Cichoń <michcic@gmail.com>
2011-04-27 16:33:44 -07:00
Chris Robinson d91b3464a4 Fix 24-bit sample loading for big-endian 2011-04-27 15:46:30 -07:00
Chris Robinson 650d94e1a6 Change some sample type values 2011-04-27 12:20:50 -07:00
Chris Robinson 543134a6d2 Add loopback device properties to the ALCenum list 2011-04-27 12:18:24 -07:00
Chris Robinson ab2626f9df Add a couple in-progress extensions to the extension lists 2011-04-27 12:15:16 -07:00
Chris Robinson d50f9249d6 Add support for packed 24-bit samples 2011-04-26 18:17:50 -07:00
Chris Robinson 10a9753183 Add a compatibility option to treat cone angles as half angles
All previous versions of the library treated the source cone angles as half
angles, which is contrary to the spec. Setting the __ALSOFT_HALF_ANGLE_CONES
environment variable to "true" or "1" restores the buggy behavior for
compatibility with applications that expect it.

This is not a config file option because new apps should not be made to depend
on the old behavior.
2011-04-22 23:17:27 -07:00
Chris Robinson 1e8718fe87 Convert full-width cone angle source properties to half-width
The spec intends the property values to be the full angle encompassed by the
cones, but the calculation interprets them as the angle from the center
point.
2011-04-22 22:59:58 -07:00
Chris Robinson 678d0b87d0 Apply the device matrix to the multi-channel source matrix
Mono sources and effects already output according to the available output
device channels. Multiplying the device matrix with the source matrix results
in a matrix that has the same effect as applying the source matrix followed by
the device matrix, so all the channel remixing can be done in one place.
2011-04-14 21:03:37 -07:00
Chris Robinson 319dfce300 Fix LFE channel output 2011-04-14 15:20:25 -07:00
Chris Robinson c3e86933d0 Only set relevant device matrix entries 2011-04-12 17:08:02 -07:00
Chris Robinson 936acf8c10 Allow the buffer_samples API to be retrieved 2011-04-10 09:48:37 -07:00
Chris Robinson 358d402cd0 Move Convert_IMA4 into the template declarations 2011-03-21 09:42:03 -07:00
Chris Robinson d11593eb5c Use nested loops when converting data 2011-03-19 12:40:14 -07:00
Chris Robinson 4bdedc7fde Use more appropriate enum values 2011-03-18 07:28:06 -07:00
Chris Robinson 9be625d6e8 Add some new format names 2011-03-17 08:42:25 -07:00
Chris Robinson 898b245bb2 Make LoadData return an error if the dst format is not valid or compatible 2011-03-16 19:39:31 -07:00
Chris Robinson be37f10fed Add alIsBufferFormatSupportedSOFT 2011-03-16 13:57:00 -07:00
Chris Robinson a87f651b4d Combine ConvertInput* and ConvertOutput* helpers
The UserFmt* enum types are a complete set of all recognized channel configs and
sample types, so casting Fmt* enum types to them is valid.
2011-03-16 13:32:57 -07:00
Chris Robinson 575195a283 Add alGetBufferSamplesSOFT 2011-03-16 12:56:39 -07:00
Chris Robinson 5621ee4239 Buffers made with alBufferSamplesSOFT always reference the internal format
This is not necessarily the same as the format of the originating data
2011-03-16 12:24:53 -07:00
Chris Robinson f5c4e67aef Add alBufferSubSamplesSOFT 2011-03-16 12:13:17 -07:00
Chris Robinson 2f7de9d696 Add alBufferSamplesSOFT, as an initial start to AL_SOFT_buffer_samples 2011-03-16 11:29:22 -07:00
Chris Robinson 9cee723e92 Pass the number of compressed frames to LoadData 2011-03-16 10:00:24 -07:00
Chris Robinson 4a1c0fedca Use the defines for the UserFmt* and Fmt* types 2011-03-16 05:47:07 -07:00
Chris Robinson b510958f9e NoErr should be noErr 2011-03-15 11:52:18 -07:00
Chris Robinson d6c8bb35b4 Add a CoreAudio backend
Code courtesy of Garin Hiebert <garinh@cheesetoast.net>
2011-03-15 04:58:56 -07:00
Chris Robinson 4899674b6c Move ALC_ENUMERATE_ALL_EXT enums to alc.h, to match other systems' headers 2011-03-14 09:04:41 -07:00
Chris Robinson 4eeed5376f Invert the device matrix row/column
It is accessed now as mat[target][source]
2011-03-13 08:46:54 -07:00
Chris Robinson 77ad538e68 Remove more unneeded casts 2011-03-13 07:11:09 -07:00
Chris Robinson b699aca65b Remove unnecessary casts 2011-03-13 06:34:41 -07:00
Chris Robinson ffd19fe961 Rename ConvertData* to ConvertInput* 2011-03-13 00:05:19 -08:00
Chris Robinson 208912ceac Fix DSound floating-point sample assumptions 2011-03-12 22:52:33 -08:00
Chris Robinson 89c253ce41 Recognize ALC_EXT_DEDICATED with openal-info 2011-03-12 21:28:40 -08:00
Chris Robinson b1d4508d90 Use a loop to make sure the requested effect is not disabled 2011-03-12 20:54:49 -08:00
Chris Robinson 8249dc8559 Combine multiple lists into one 2011-03-12 20:37:22 -08:00
Chris Robinson 59fe4f8a36 Don't return enums for disabled effects 2011-03-12 20:27:46 -08:00
Chris Robinson acb493665d Add dedicated to the list of excludefx 2011-03-12 20:15:21 -08:00
Chris Robinson 73123bc313 Add support for ALC_EXT_DEDICATED 2011-03-12 20:11:25 -08:00
Chris Robinson 947aa1342c Add ALC_EXT_DEDICATED to alext.h 2011-03-12 07:19:42 -08:00
Chris Robinson 20c8f9fe31 Add AL_EXT_FOLDBACK to alext.h 2011-03-12 06:35:45 -08:00
Chris Robinson 66ee3bc217 Better protect the device for multi-threading access 2011-03-12 06:23:17 -08:00
Chris Robinson 031a2a1b1e Implement a basic non-real-time loopback device
Currently it behaves just like a normal device except contexts are only
processed during calls to alcRenderSamples. Additionally, the ALC_SYNC and
ALC_REFRESH context attributes are not valid for these devices, and there are
two new context attributes to specify the rendering format: ALC_FORMAT_TYPE
and ALC_FORMAT_CHANNELS. These each take one of the type and channel enums
added.
This stuff is subject to change.
2011-03-11 00:13:44 -08:00
Chris Robinson 9c87b73ad5 Fix a compiler warning about checking differing enum types
One of the enum types is actually a subset of the other, and share the same
integer values for compatible enumation values
2011-03-10 01:27:42 -08:00
Chris Robinson 97139a530f Filter NaN sample values when converting floats and doubles 2011-03-04 16:18:03 -08:00
Chris Robinson b483b1e380 Release 1.13 2011-02-15 12:10:44 -08:00
Chris Robinson d9126eeb2c Finalize the loop points extension as AL_SOFT_loop_points 2011-02-09 22:54:12 -08:00
Chris Robinson d8048e136f BufferFreq doesn't need to be a float 2011-02-09 11:19:33 -08:00
Chris Robinson fd64fea279 Use the correct winmm device ID type 2011-02-08 10:33:18 -08:00
Chris Robinson 667fd24c84 Add an option to force usage of static MSVC runtimes 2011-02-08 00:34:25 -08:00
Chris Robinson 9ffb38d6be Prevent unused variable warnings when monotonic clock isn't available 2011-02-08 00:22:22 -08:00
Chris Robinson 13397feccf Don't inline CalcLimitedHfRatio 2011-02-07 23:58:40 -08:00
Chris Robinson 57b05fb6d4 Add a missing enum value for PA_API_VERSION 11 2011-02-07 23:53:07 -08:00
Chris Robinson 41a2c25b5a Include cguid.h for GUID_NULL 2011-02-07 22:46:54 -08:00
Chris Robinson b214249290 Check for internal visibility, since that's what is used 2011-02-07 13:55:48 -08:00
Chris Robinson 8dad1c382b Add the standard "revision" number to the library name 2011-02-06 02:39:22 -08:00
Chris Robinson 6713a47d38 Fix looping on loop points 2011-02-06 01:07:37 -08:00
Chris Robinson 02e0223fe8 Re-enable channel-config-specific layout options 2011-01-23 23:57:39 -08:00
Chris Robinson 6c4add67e1 Don't duplicate the device list append function 2011-01-23 23:16:55 -08:00
Chris Robinson 2b7324c06e Avoid using a variable for array sizes and const variable initialization 2011-01-23 22:30:18 -08:00
Chris Robinson c95bd6cadf Fix loop handling when traversing the buffer queue backwards 2011-01-23 15:04:42 -08:00
Chris Robinson d73694d3e6 More improvements to the AudioIO backend 2010-12-09 23:53:32 -08:00
Chris Robinson 5d607f9484 Better check format type returned by AUDIO_SETINFO 2010-12-09 23:47:11 -08:00
Chris Robinson 816c38e9e4 Add signed 8-bit output to the AudioIO backend 2010-12-09 23:43:29 -08:00
Chris Robinson 09130e5dcd Fix some AudioIO backend warnings 2010-12-09 23:41:54 -08:00
Chris Robinson b87447787a Cleanup some ok() macros 2010-12-09 23:32:47 -08:00
Chris Robinson 5a548f8366 Another fix for float-to-int conversions 2010-12-09 22:47:06 -08:00
Chris Robinson 023658605e Constify some variables 2010-12-09 19:47:08 -08:00
Chris Robinson 031111014b Be consistent with variable name use 2010-12-09 18:09:47 -08:00
Chris Robinson b5b9303e66 Fix up some conversions 2010-12-09 17:37:57 -08:00
Chris Robinson 3deaa2fad3 Combine stereo and multi-channel templates 2010-12-09 17:14:12 -08:00
Chris Robinson c3cd4c80b2 Use a matrix when rendering to the dry buffer 2010-12-09 16:37:23 -08:00
Chris Robinson be3f3c4bf0 Make better use of the type range when converting from float 2010-12-09 05:06:29 -08:00
Chris Robinson d1ca8b4470 Use simple loops instead of several explicit lines for the mono mixer 2010-12-09 05:05:42 -08:00
Chris Robinson 72e3386c6a Be a bit more generic with the multi-channel mixer loops 2010-12-09 04:41:52 -08:00
Chris Robinson 4010a6b745 Remove some now-unused functions 2010-12-08 02:56:24 -08:00
Chris Robinson 0218957eb5 Use "internal" visibility by default
All functions callable by apps, directly or indirectly (eg, through function
pointers), are marked as "protected", and internal visibility can provide
benefits over hidden when we know functions won't be called by other modules.
2010-12-08 02:32:09 -08:00
Chris Robinson cd65ab7dfa Add an IMA4 encoder 2010-12-05 03:34:19 -08:00
Chris Robinson c4adf541c1 Set the playing flag to TRUE when the dsound buffer starts playing 2010-12-04 20:22:12 -08:00
Chris Robinson 88e3a22772 Separate device format into 'channel config' and 'sample type' components 2010-12-04 19:50:00 -08:00
Chris Robinson 191803ad53 Fix wave and null device calculations when the timer wraps 2010-12-03 23:48:59 -08:00
Chris Robinson 1b7be672fa No need to clamp the app-specified pitch 2010-12-03 23:08:26 -08:00
Chris Robinson 9581325b9c Rename SrcFmt -> UserFmt 2010-12-03 22:33:41 -08:00
Chris Robinson 59990acc2a Add functions to write (signed) byte and ushort samples to devices 2010-12-02 16:36:37 -08:00
Chris Robinson 0bf7716332 Remove incomplete EXT_buffer_sub_data, as it's (temporarily) superceded by SOFT_buffer_sub_data
The EXT version can come back when other implementations take an interest
2010-12-02 01:46:43 -08:00
Chris Robinson 3339e5dc6a Use memcpy to copy a string instead of sprintf 2010-12-02 01:12:49 -08:00
Chris Robinson 30b3f68ddf Make a function static 2010-12-02 01:05:29 -08:00
Chris Robinson 75552f2765 Remove unused macro 2010-12-01 23:21:18 -08:00
Chris Robinson 4a0bf9fd44 Template-ize the device buffer writing functions 2010-12-01 21:50:49 -08:00
Chris Robinson fa28098a83 Some general cleanups 2010-12-01 20:34:29 -08:00
Chris Robinson 646409cbdf Don't limit IMA4 decoding to 2 channels 2010-12-01 18:36:07 -08:00
Chris Robinson 9e8fb89a52 Rename OUTPUTCHANNELS to something more descriptive 2010-12-01 18:33:17 -08:00
Chris Robinson 14441e0e53 Rename some variables 2010-12-01 14:19:57 -08:00
Chris Robinson 7f5814c4d8 Use min/max to clamp the IMA4 sample and index values 2010-12-01 14:07:21 -08:00
Chris Robinson e3afc3587e Fix handling of -32768 in the muLaw encoder 2010-12-01 02:24:39 -08:00
Chris Robinson 8a52c44d15 Don'f fail if realloc returns NULL for 0 sizes 2010-12-01 02:00:41 -08:00
Chris Robinson e6e18732b5 Make sure the muLaw sample encoder is inlined 2010-11-30 18:06:54 -08:00
Chris Robinson 3f8ae8f1ce Fix IMA4 decoding 2010-11-30 16:54:30 -08:00
Chris Robinson a79129835c Fix some comments and line spacing 2010-11-30 13:04:48 -08:00
Chris Robinson 6fcaecdc9f Fix int-to-double conversion 2010-11-30 12:26:37 -08:00
Chris Robinson 8dc8090888 Make muLaw decoding work similar to encoding 2010-11-30 01:26:43 -08:00
Chris Robinson 4db2f1ec7d Implement a muLaw encoder 2010-11-30 01:20:40 -08:00
Chris Robinson 06fa030260 Rename the IMA4 block decoder function 2010-11-30 01:00:37 -08:00
Chris Robinson 018f9fc278 Handle all channel configurations for double 2010-11-30 00:32:13 -08:00
Chris Robinson c0f6d46cd8 Allow LoadData to handle IMA4 data 2010-11-30 00:28:02 -08:00
Chris Robinson 8e43b1fe04 Let the storable format enum values share the input format enum values where they overlap 2010-11-29 23:57:07 -08:00
Chris Robinson 04df919a1c Clean up muLaw format loading a bit 2010-11-29 23:46:49 -08:00
Chris Robinson f82e628f08 Allow converting IMA4 to types other than short 2010-11-29 22:55:46 -08:00
Chris Robinson 07a7c03adb Check for a couple missing input formats and remove an unneeded default case 2010-11-29 22:07:07 -08:00
Chris Robinson e800f8c0a7 Let DecomposeInputType verify the format passed to alBufferSubDataSOFT 2010-11-29 22:01:54 -08:00
Chris Robinson d02dbef9d2 Pass the input channel config and sample type to LoadData 2010-11-29 21:55:47 -08:00
Chris Robinson 55262bc6e9 Make the decompose functions return false when given an unhandled format 2010-11-29 20:34:33 -08:00
Chris Robinson 51ed22afb9 Combine two switch blocks 2010-11-29 20:17:15 -08:00
Chris Robinson 0cf32e2946 Reorder some parameters for consistency 2010-11-29 19:48:18 -08:00
Chris Robinson 693ab4d8bc Fix source offset calculations 2010-11-29 19:36:33 -08:00
Chris Robinson e4869b447c Store the decomposed source format in the buffer 2010-11-29 19:27:33 -08:00
Chris Robinson 547f356131 Keep a handle on the buffer with the format when queueing new buffers 2010-11-29 17:35:22 -08:00
Chris Robinson 017ab1b3fd Add IMA4 as a source data type 2010-11-29 17:14:22 -08:00
Chris Robinson 07ef566de1 Add support for (unsigned) int input formats 2010-11-29 17:00:22 -08:00
Chris Robinson 3c8a29a179 Reorder and rename parameters for the format decomposition functions 2010-11-29 15:19:39 -08:00
Chris Robinson f74032d244 Uninline a couple more functions 2010-11-29 15:01:27 -08:00
Chris Robinson eb489ac886 Store double formats as float
The mixer will cut it down to float anyway, so it's unnecessary and needlessly
creates more mixer functions
2010-11-28 23:07:57 -08:00
Chris Robinson 986628b8b2 Avoid temp storage 2010-11-28 22:50:27 -08:00
Chris Robinson da4f5c6f96 Uninline some functions
Also add -Winline to the compiler command line to watch for future inline problems
2010-11-28 19:52:17 -08:00
Chris Robinson f09ae196a8 Avoid making Resampler[Pre]Padding static 2010-11-28 17:37:14 -08:00
Chris Robinson 3e702ae2bc Improve static builds with MinGW 2010-11-28 16:19:11 -08:00
Chris Robinson 490d1c6a8e Prefer GCC's constructor, even in Windows 2010-11-28 14:27:07 -08:00
Chris Robinson 0e748d98d5 Rename some stuff for consistency 2010-11-28 13:08:51 -08:00
Chris Robinson c41e893361 Don't store the internal format in the buffer
The type and channel config are good enough
2010-11-28 12:53:35 -08:00
Chris Robinson 2fd8d6916b Support rear formats directly in the mixer 2010-11-27 22:55:20 -08:00
Chris Robinson b4eaf1ab5a Avoid a potential overflow when setting the source offset 2010-11-27 22:22:14 -08:00
Chris Robinson 3c8e9a7f24 Clean up the source byte offset code 2010-11-27 22:13:29 -08:00
Chris Robinson 67ec72284d Remove some unneeded muLaw special cases 2010-11-27 22:06:51 -08:00
Chris Robinson 754a122aa0 Support muLaw with LoadData 2010-11-27 22:00:24 -08:00
Chris Robinson 0486d633c6 Remove an unnecessary assert 2010-11-27 20:41:09 -08:00
Chris Robinson 09b063002b Be much more flexible with loading and converting buffer data 2010-11-27 20:33:37 -08:00
Chris Robinson 8828f4d0e5 Don't inline the decompose function 2010-11-27 15:37:51 -08:00
Chris Robinson 9495cf71fd Decompose input buffer formats and use it when converting uncompressed data 2010-11-27 15:33:33 -08:00
Chris Robinson 4d0f70439c Rename a function 2010-11-27 14:30:13 -08:00
Chris Robinson d3bb5d4fcb Add a basic WaveOut device
It can still use more work, but it seems to work
2010-11-27 14:13:40 -08:00
Chris Robinson 61315d4dfb Shorten a couple really long lines 2010-11-27 00:51:21 -08:00
Chris Robinson 2a123a4760 Support 64-bit double buffers directly 2010-11-27 00:32:53 -08:00
Chris Robinson 4689ffb72d muLaw converts to 16-bit, not float 2010-11-27 00:17:19 -08:00
Chris Robinson d2895c7985 Use the enums in a couple more places 2010-11-27 00:15:07 -08:00
Chris Robinson 6abb9d151e Use enums when selecting the mixer 2010-11-26 23:42:30 -08:00
Chris Robinson 08827efae3 Remove an unnecessary 'struct' keyword 2010-11-26 20:35:47 -08:00
Chris Robinson 16c05d2c05 Avoid some more code duplication 2010-11-26 20:22:14 -08:00
Chris Robinson 4d4d699780 Fix step calculations 2010-11-26 20:17:05 -08:00
Chris Robinson ca1ec4b221 Fix some comments 2010-11-26 18:01:29 -08:00
Chris Robinson facb922f3e Properly clamp high pitch values 2010-11-26 17:47:43 -08:00
Chris Robinson d7987677ac Subtract the fractional position separately 2010-11-26 15:51:13 -08:00
Chris Robinson 5fbbfa482f Rename some variables to be clearer 2010-11-26 12:59:45 -08:00
Chris Robinson 7287b5cd3c Make the buffer padding size dependent on the resampler 2010-11-26 02:53:15 -08:00
Chris Robinson 0692cf304d Don't allocate padding with the buffer itself 2010-11-26 02:31:17 -08:00
Chris Robinson 88ebe7e4c1 Decrease the buffer size more 2010-11-26 02:14:11 -08:00
Chris Robinson d647ed60e8 Convert a few divisions to multiplications 2010-11-26 01:35:40 -08:00
Chris Robinson 1cb29ece08 Make a FRACTIONONE macro, and use it 2010-11-26 01:21:46 -08:00
Chris Robinson de8b7fbc5f Share the interpolation functions and use them in the reverb effect 2010-11-26 01:07:54 -08:00
Chris Robinson 98d78f7ff9 Slightly improve channel gain calculations 2010-11-26 00:24:54 -08:00
Chris Robinson 3d1eb7dcad Add a missing break statement 2010-11-25 23:13:50 -08:00
Chris Robinson e1c0b5ea24 Call MixSource directly, instead of through a function pointer 2010-11-25 23:09:18 -08:00
Chris Robinson 30820c1bde Add a cubic resampler 2010-11-25 23:05:44 -08:00
Chris Robinson 46b9802902 Force the point resampler when the source plays at normal speed 2010-11-25 22:15:38 -08:00
Chris Robinson b6fb28de1a Pass a pointer to the samplers so they can read just the samples they need 2010-11-25 22:04:17 -08:00
Chris Robinson 17fc4ba5e3 Add "pre-padding" to the temp buffer
This will help to implement resamplers that want previous samples
2010-11-25 21:42:15 -08:00
Chris Robinson 596514f7aa Make the buffer queue a double-linked list 2010-11-25 18:30:57 -08:00
Chris Robinson 6e3a6f90e9 Be less wave-in-specific in the winmm backend 2010-11-25 17:37:34 -08:00
Chris Robinson fbd6439bd2 Make the first enumerated DirectSound device use the default, and fix the name 2010-11-25 14:35:32 -08:00
Chris Robinson f72c735f9e Make sure the target buffer size is frame aligned 2010-11-25 14:23:03 -08:00
Chris Robinson 9b23fc5db8 Minor rework of the template macros 2010-11-25 14:07:52 -08:00
Chris Robinson aaff188b3b Remove the cosine resampler
It's too slow when auxiliary sends are being used.
2010-11-25 13:49:51 -08:00
Chris Robinson 2595d85cca Double-up the interpolation functions (and make them use doubles) 2010-11-25 13:45:35 -08:00
Chris Robinson 2c96c29938 Rename some "template" functions 2010-11-25 13:22:54 -08:00
Chris Robinson 65919b7037 Always make sure there's a sample available to mix, for pending click removal 2010-11-25 13:17:51 -08:00
Chris Robinson f1bb7c33f1 Fix a bunch of comments 2010-11-25 12:07:12 -08:00
Chris Robinson 8e7a4046d0 Don't "convert" LOKI quad to MCFORMATS quad 2010-11-25 11:49:35 -08:00
Chris Robinson f4e0c6a348 Use a temporary stack buffer to store the source buffer data to be mixed
This makes it much easier to pack the playable buffer data so that it's a
continuous data stream. This could normally be broken up otherwise, such as
when using loop points or small queued buffers.
Currently it defaults to a 16KB stack buffer, but this can be overridden at
compile time.
2010-11-25 11:16:19 -08:00
Chris Robinson 885a5e9f90 Use 0-based access for the buffer data 2010-11-24 20:44:21 -08:00
Chris Robinson 22e3e9a18b Support 8-bit sample data in the mixer 2010-11-24 19:41:25 -08:00
Chris Robinson 445cde2dd4 Check the sample byte size after the resampler 2010-11-24 17:23:09 -08:00
Chris Robinson 3dd58cf765 Get rid of a useless union 2010-11-24 17:10:41 -08:00
Chris Robinson cd87061749 Change the mixer macros to declare functions 2010-11-24 17:07:50 -08:00
Chris Robinson 1311e051d9 Set mixer sample pos and frac when needed 2010-11-24 14:54:46 -08:00
Chris Robinson c9363832fe Fully progress the source to a stopped state when no buffers are playable 2010-11-24 10:16:53 -08:00
Chris Robinson 6ab22e7cbb Reorder some stored source params 2010-11-24 10:01:50 -08:00
Chris Robinson bb13f7f234 Make sure the buffer padding is always cleared before looking for the next buffer 2010-11-24 07:20:41 -08:00
Chris Robinson 13ba253aec Calculate ALSA buffer length with the other values 2010-11-23 07:24:27 -08:00
Chris Robinson c58469e234 Handle the timer wrapping in the wave and null output devices 2010-11-21 11:34:16 -08:00
Chris Robinson 57a03bc2b5 Use helpers to write 16- and 32-bit values 2010-11-21 06:54:41 -08:00
Chris Robinson a7b4e0b5c5 Get rid of the device timer stuff 2010-11-21 06:32:59 -08:00
Chris Robinson bbb45e326c Avoid temporary float variables 2010-11-21 03:58:37 -08:00
Chris Robinson dfe3e402a8 Pass the data pointer and proper sampler to the mixer macro 2010-11-21 03:33:33 -08:00
Chris Robinson e884da50ea Move aluMixData back to ALu.c 2010-11-21 02:51:18 -08:00
Chris Robinson 4c576631ad Hide AL_EXTX_sample_buffer_object
It hasn't been going anywhere, and it's current use is dubious. Leave the code
in for now, but it may be removed later.
2010-11-21 02:36:34 -08:00
Chris Robinson 5f6f046d25 Use a double when converting millisecond offsets to byte offsets 2010-11-07 21:41:04 -08:00
Chris Robinson 65e033c64b Increase the max number of source sends to 4, and decrease the default to 1 2010-11-06 15:08:54 -07:00
Chris Robinson 07346da740 Use the number of sends the device is configured for in more places 2010-11-06 14:27:33 -07:00
Chris Robinson c95f182a90 Remove unnecessary comments and reorder some lines 2010-11-06 14:07:30 -07:00
Chris Robinson 289c74788e Use a switch to select the appropriate channel-count mixer 2010-11-06 12:21:46 -07:00
Chris Robinson 65a8d9278d Declare a variable where it's used 2010-11-01 12:44:56 -07:00
Chris Robinson 887c830d56 Remove explicit use of -funroll-loops
I'm not sure this really helps much.
2010-10-15 15:21:23 -07:00
Chris Robinson 6db4e08efb Fix fpu state handling with _controlfp 2010-10-15 10:35:47 -07:00
Chris Robinson 4f81bce800 Improve a macro to reference its parameter 2010-10-11 12:24:28 -07:00
Chris Robinson 932fcfa8bf Fix 64-bit compilation 2010-10-11 12:24:07 -07:00
Chris Robinson 314832e35a Rename a couple more variables 2010-10-10 04:00:50 -07:00
Chris Robinson 6a9dd185e6 Improve naming and placement of a couple variables 2010-10-10 03:47:57 -07:00
Chris Robinson 2172827f43 Simplify a calculation 2010-10-09 15:24:24 -07:00
Chris Robinson 90e9cd970a Check resampler mode first when selecting the mixer 2010-09-26 13:53:02 -07:00
Chris Robinson 845ca95888 Get a variable where it's needed 2010-09-26 13:30:46 -07:00
Chris Robinson 92d8d2f8a4 Pass the device to the mix function and localize the dry mix and click buffers 2010-09-26 12:23:22 -07:00
Chris Robinson 7cd52b2857 Clarify a check 2010-09-26 01:29:28 -07:00
Chris Robinson 9fbd6c6c3f Make the SourceMix function a method of the ALsource struct 2010-09-26 01:15:27 -07:00
Chris Robinson 08fdc5fa98 Avoid updating the source in MixSource 2010-09-25 23:49:57 -07:00
Chris Robinson 7e362249ff Convert shorts so that 0 remains 0 2010-09-25 21:13:40 -07:00
Chris Robinson 9fa2db0227 Don't try to open the ALSA device a second time if the first fails 2010-09-25 20:20:10 -07:00
Chris Robinson 4c75a72a90 Avoid some code duplication 2010-09-25 20:18:07 -07:00
Chris Robinson a0b2778161 Localize more variables and remove unnecessary ones 2010-09-25 14:19:36 -07:00
Chris Robinson bc6d94cd25 Avoid sampling beyond the end of the loop 2010-09-24 23:32:50 -07:00
Chris Robinson e70141e57b Localize some variables to the mixer macros
The hope here is to help break the mixer loops into separate functions, so the
macro expansion doesn't cause the MixSource function to explode in size and
kill compilation speed.
2010-09-24 23:24:01 -07:00
Chris Robinson 2e70b82eaa Use separate mixing loops for the dry path and wet paths
This will increase CPU use a bit for sources that use auxiliary sends
(particularly with the cosine resampler), but it makes it more scalable when
adding more sends per source. Additionally, it will improve performance of
sources that don't use auxiliary sends.

The cosine resampler can probably be improved by using a lookup table instead
of calling cos() as there are 14 bits of fractional precision, so a 16384-
element float array would cover it all. This can also be reduced to a half or
quarter size if the precision loss is found to be acceptable.
2010-09-24 18:29:49 -07:00
Chris Robinson 117ed52091 Better protect against negative attenuation 2010-09-24 13:16:09 -07:00
Chris Robinson e9d5e53dbe Use ALfloat for the dry mix buffer 2010-09-23 17:44:02 -07:00
Chris Robinson e308694521 Remove an unnecessary variable 2010-09-23 16:51:30 -07:00
Chris Robinson 7e498012ff Rename some variables to be less confusing 2010-09-23 16:49:33 -07:00
Chris Robinson f98bf32be8 Fix line indentation 2010-09-23 15:08:30 -07:00
Chris Robinson 0f2ed8c84d Be consistent with a variable's use 2010-09-23 13:49:21 -07:00
Chris Robinson 8d67cdfaab Convert float to short similar to short to float 2010-09-23 08:43:39 -07:00
Chris Robinson 67ba924ed3 Store and mix signed 16-bit samples 2010-09-22 23:38:06 -07:00
Chris Robinson 764cfb6a0d Allow the mixing loop to access the different sample formats 2010-09-22 20:53:04 -07:00
Chris Robinson 4e3c945093 Compact some reused code 2010-09-22 18:49:46 -07:00
Chris Robinson 8558bc1d41 Allow accessing the buffer data as multiple data types in the mixer
Also reduces a bunch of indentation
2010-09-22 17:19:35 -07:00
Chris Robinson 03f56419d0 Duplicate stereo sources by default
The mixer is smart enough now to handle proper volume adjustments depending on
the number of output speakers
2010-09-22 11:09:34 -07:00
Chris Robinson 3ee9d24c3f Use separate macros for the different mixing loops 2010-09-22 09:38:24 -07:00
Chris Robinson 9b1ad6c873 Consolidate some code 2010-09-21 19:49:47 -07:00
Chris Robinson 97daaade8a Reduce some more indentation 2010-09-21 16:54:33 -07:00
Chris Robinson 8a1d5a21c3 Reduce some indentation 2010-09-21 15:12:08 -07:00
Chris Robinson 301a8ba3a4 Improve some range checks 2010-09-21 14:41:43 -07:00
Chris Robinson 2d4453968d Implement AL_SOFT_buffer_sub_data using the current AL_EXT_buffer_sub_data spec
This extension can be useful for some applications, but a full EXT version may
not be ready for some time. Using the SOFT moniker will help differentiate it
in case changes need to be done for the EXT version.
2010-09-21 09:43:35 -07:00
Chris Robinson a2580a85a8 Improve range checking for BufferSubData 2010-09-19 14:00:55 -07:00
Chris Robinson 8a0e15ff37 Store air absorption factor locally 2010-09-12 00:10:33 -07:00
Chris Robinson 8953a73fb1 Prevent effectiveDist from going negative 2010-09-11 23:57:15 -07:00
Chris Robinson 1a2883ae9a Pass dry-path click buffers as parameters to MixSource 2010-09-11 23:43:12 -07:00
Chris Robinson a2358ffc41 Source positions at and after the end of the buffer queue go back to 0 2010-09-09 18:27:14 -07:00
Chris Robinson 33eb2786f8 Avoid another non-descript incrementing variable 2010-09-09 16:47:54 -07:00
Chris Robinson c8ec4fd505 Avoid using a single-letter iterator value 2010-09-09 14:53:14 -07:00
Chris Robinson fe98ab5398 Combine multiple checks into one 2010-09-08 17:29:16 -07:00
Chris Robinson 76be7eb1e7 Better protect against sample overflow when converting float to short 2010-09-08 16:26:19 -07:00
Chris Robinson 6e32812bc1 Remove unnecessary check 2010-09-07 22:38:29 -07:00
Chris Robinson 50a7be6385 Fix comparison check 2010-09-07 22:23:37 -07:00
Chris Robinson 3763c95784 Add missing else 2010-09-07 16:36:42 -07:00
Chris Robinson 5fa5252648 Watch for a few more negative sizes 2010-09-07 16:33:17 -07:00
Chris Robinson 1e82561a22 Check for invalid negative sizes 2010-09-07 16:18:24 -07:00
Chris Robinson ba32a52bb6 Remove unused struct member 2010-09-07 16:07:35 -07:00
Chris Robinson 6027fda0ce Combine two arrays into one 2010-08-16 16:07:44 -07:00
Chris Robinson e5cae87502 Handle clicks by watching changes between updates 2010-08-16 14:53:21 -07:00
Chris Robinson ba0ec1b4ba Use the correct value to dereference an array 2010-08-16 11:42:18 -07:00
Chris Robinson 2acd6f52f1 Add a bit more information to the README 2010-08-15 16:55:11 -07:00
Chris Robinson b520228bc6 Consolidate some click removal calculations 2010-08-15 15:55:02 -07:00
Chris Robinson 343a8241ab Use a multiplier instead of if checks 2010-08-15 15:09:38 -07:00
Chris Robinson 00525274a0 Add click removal for wet sends 2010-08-15 14:52:12 -07:00
Chris Robinson 2915d9dbd9 Use click removal to handle harsh panning/volume changes 2010-08-15 00:19:38 -07:00
Chris Robinson 0c49e5ba56 Use click removal when starting a source 2010-08-14 22:59:55 -07:00
Chris Robinson b65bc45236 Add a corrective, click-remval sample value offset
This will hopefully be more effective and rebust at removing clicks and pops,
particularly from starts and stops in the middle of a sound. Based on an idea
by Ben Davis.
2010-08-14 21:30:14 -07:00
Chris Robinson 94b7e5a227 Rename Null Output to No Output 2010-08-13 22:06:38 -07:00
Chris Robinson 928572d384 Start the device when creating a context without specified attributes 2010-08-12 17:24:55 -07:00
Chris Robinson 2d1988bb6f Output on available channels only, for the echo and modulator effects 2010-08-09 06:12:00 -07:00
Chris Robinson e007dc3614 Fix early/late reverb panning, and only output reverb on available channels 2010-08-09 05:11:58 -07:00
Chris Robinson 2ed929eadc Update device parameters using a separate function 2010-08-09 00:28:48 -07:00
Chris Robinson f1924fa7ea Decrement context count when it's removed from the list 2010-08-08 02:48:28 -07:00
Chris Robinson df822513fa Don't re-init and re-allocate some stuff when the device is left running 2010-08-07 09:13:51 -07:00
Chris Robinson 762bdaeee1 Panning init cleanup 2010-08-07 08:49:29 -07:00
Chris Robinson 8003278a55 Combine non-attenuated source calculation functions 2010-08-07 06:57:31 -07:00
Chris Robinson 5f22d30fc9 Calculate the source stepping value with the param calculations 2010-08-07 05:43:16 -07:00
Chris Robinson d6dc855511 Set up increment once 2010-08-07 02:32:16 -07:00
Chris Robinson 8f49f2c25e Forward some more source properties instead of duplicating 2010-08-07 01:35:43 -07:00
Chris Robinson f4304ca062 Calculate the actual stepping value in the source update method 2010-08-07 00:38:02 -07:00
Chris Robinson 044662e4b7 Calculate gain steps once during a source mix 2010-08-06 07:55:00 -07:00
Chris Robinson d9d8319c22 Effectslot's Effectstate should never be NULL 2010-08-06 04:52:34 -07:00
Chris Robinson 56b094767b Remove an unnecessary check
The number of buffers played should never exceed the buffer count when playing,
and the buffer list is used later on regardless
2010-08-06 01:14:37 -07:00
Chris Robinson 98bc60cfb7 Combine stereo and stereo-duplicate mixers
Use a separate update method for stereo sources to the set appropriate channel
gains
2010-08-05 01:07:20 -07:00
Chris Robinson 2aacca76de Improve a warning message 2010-08-04 06:18:23 -07:00
Chris Robinson aea615edb3 Prevent re-probing while opening a Pulse device if no devices were found 2010-08-04 05:07:56 -07:00
Chris Robinson f6b2dfd09f Only except a NULL device name for Pulse if some devices were found 2010-08-04 04:56:07 -07:00
Chris Robinson 7f9bcfbc7d Do not enumerate PulseAudio devices if a context fails to connect 2010-08-04 04:21:39 -07:00
Chris Robinson 22cc5af1fe pa_context_errno can return a positive value 2010-08-04 04:11:38 -07:00
Chris Robinson 0dc3f1984e Move the core mixer functions to a separate source file 2010-08-03 23:19:36 -07:00
Chris Robinson e74976e645 Use a callback to specify the source update method 2010-08-03 23:10:00 -07:00
Chris Robinson 91278608c5 Add some spacing 2010-08-03 01:44:52 -07:00
Chris Robinson 8e2d765671 Move active source iteration to aluMixData 2010-08-03 01:09:02 -07:00
Chris Robinson 2af39e51f8 Separate speaker/panning initialization into another source file 2010-08-03 00:21:36 -07:00
Chris Robinson 1504cf8812 Use millisecond resolution for the Null and Wave Writer backends 2010-08-02 22:34:19 -07:00
Chris Robinson 3bece35bbd Prevent getting bad time values when pa_stream_get_time fails
Also makes sure that reported time won't go backwards
2010-08-02 22:27:32 -07:00
Chris Robinson ae41ad5d1e Use a better method to time the Null and Wave Writer backends
This better compensates for precision loss when converting milliseconds to the
sample rate
2010-08-02 20:04:52 -07:00
Chris Robinson ee61f7a55c Make sure to request a tlength that's at least minreq*2
And warn if Pulse returns a minreq that's too large
2010-08-02 17:24:40 -07:00
Chris Robinson 241ddf76d4 Enforce the device time to be a multiple of the reported resolution 2010-08-01 16:31:00 -07:00
Chris Robinson fe6e73ede9 Be a bit more verbose when a device fails to open 2010-08-01 16:20:28 -07:00
Chris Robinson 8dab4c418c Use Pulse's stream time for the device time 2010-07-31 21:33:46 -07:00
Chris Robinson 41e2c41ecc Combine duplicate lines into the mixing macro 2010-07-31 21:09:59 -07:00
Chris Robinson 30108c7855 Remove an unnecessary variable 2010-07-31 19:14:27 -07:00
Chris Robinson 40ba9db406 Only call Pulse functions from under a lock 2010-07-31 16:07:04 -07:00
Chris Robinson f1f2941511 Add experimental device attributes for apps to query
These will be part of the ALC_EXT_device_time extension. Behavior, values,
etc, are all subject to change.
2010-07-30 20:23:55 -07:00
Chris Robinson d886fb118b Signal the Pulse mainloop on underruns
Otherwise there are (rare) risks of not getting signaled after writing the
amount of free audio data the client knows about, leading to a mixer loop
lockup
2010-07-30 20:20:40 -07:00
Chris Robinson 46d46c49a8 Store the time precision with the device 2010-07-25 12:54:50 -07:00
Chris Robinson 40dd1fb853 Add a backend callback to get the device time
The method should monotonically count the number of nanoseconds the device has
active contexts
2010-07-24 19:14:32 -07:00
Chris Robinson ab77fc14d5 Make killNow volatile 2010-07-24 10:54:12 -07:00
Chris Robinson e0d0edc0fc Close the log file last during shutdown 2010-07-22 09:16:28 -07:00
Chris Robinson 7eb124d99c Keep count of the number of samples played 2010-07-22 08:11:44 -07:00
Chris Robinson 88f236ccd3 Make sure the device is stopped if there was an error setting up the first context 2010-07-21 14:50:39 -07:00
Chris Robinson 34a0c998cd Use CLOCK_MONOTONIC when available 2010-07-18 06:21:16 -07:00
Chris Robinson e658df493a Update PulseAudio using period-sized chunks
To match the behavior of other backends
2010-07-17 23:30:25 -07:00
Chris Robinson b2a257b6b0 Rename examples to utilities
There is only the one, and it more accurately reflects what it is
2010-07-17 01:02:51 -07:00
Chris Robinson cde577690a Set the backend HAVE_ macros off before checking their availability 2010-07-17 00:37:54 -07:00
Chris Robinson c85d53cfbe Remove the commit count from the version number
As it's only updated for releases, it's kinda useless
2010-07-16 00:18:21 -07:00
Chris Robinson 26127cd2f2 Use a long single buffer for WaveIn's capture buffers 2010-07-12 16:37:44 -07:00
Chris Robinson 42d9140afe Check the buffer's original format when queueing on a source 2010-07-12 15:53:11 -07:00
Chris Robinson 52ad28df92 Fix WaveIn device cleanup order 2010-07-08 17:37:35 -07:00
Chris Robinson 58ba5fd0dd Add missing static keyword to WinMM vars 2010-07-08 15:10:59 -07:00
Chris Robinson 71cc91cc4a Use the existing ring buffer methods for WaveIn 2010-07-07 02:47:29 -07:00
Chris Robinson 6db3463d17 Fix backslash alignment for the single-channel mixer macro 2010-07-06 16:32:58 -07:00
Chris Robinson 969818db6f Use a 100ms capture buffer minimum for PulseAudio, with 50ms fragments
This better matches what is given with the WaveIn backend
2010-07-04 16:27:42 -07:00
Chris Robinson d4b191d27d Avoid retrying the requested ALSA format after it failed 2010-07-04 15:48:47 -07:00
Chris Robinson 6b23f2dfa3 snd_pcm_recover should automatically re-prepare the stream as needed 2010-06-16 02:17:41 -07:00
Chris Robinson 17e6840f92 Prevent multiple WinMM devices from getting the same name, as well 2010-06-10 15:54:04 -07:00
Chris Robinson a1f987c016 Improve handling of lost DirectSound buffers 2010-06-09 23:32:35 -07:00
Chris Robinson cff805e0a3 Prevent multiple DirectSound devices from getting the same name, too 2010-06-09 23:11:34 -07:00
Chris Robinson 25a941666a Prevent multiple PulseAudio devices from getting the same name 2010-06-09 18:22:05 -07:00
Chris Robinson 8c59849ea4 Make sure the log file is set before initializing the rest of the lib 2010-06-08 02:02:48 -07:00
Chris Robinson f9e569099c Make the Wave Writer backend optional 2010-06-08 02:01:11 -07:00
Chris Robinson 6fd6e4eacf Remove unused variable 2010-06-07 03:49:21 -07:00
Chris Robinson ce07270973 Remove support for the old 'refresh' config option 2010-06-06 00:53:29 -07:00
Chris Robinson ac05863f72 Make sure the effectslot map is properly initialized 2010-06-06 00:21:13 -07:00
Chris Robinson 7f6df7695c Use an array of active sources when mixing
Prevents iterating over all allocated sources during mixing updates
2010-06-06 00:17:50 -07:00
Chris Robinson ed58571079 Use non-blocking mode for ALSA capture 2010-06-05 19:56:58 -07:00
Chris Robinson 80fbe8a788 Allow prepending backend names with - to remove them from the available devices 2010-06-05 19:33:06 -07:00
Chris Robinson 0fdec056fa Improve error recovery when capturing from ALSA 2010-06-05 17:41:33 -07:00
Chris Robinson 1915fe831f Read capture data from ALSA when querying the available sample count 2010-06-05 04:43:26 -07:00
Chris Robinson 98c01a40e6 Improve locking when calling the backend capture API 2010-06-05 04:42:05 -07:00
Chris Robinson 33234d605a Don't attempt to look for more samples from Pulse after a disconnect 2010-06-05 00:09:45 -07:00
Chris Robinson c6defe9a6d Read PulseAudio's capture data into the ring buffer when querying the sample count
This ensures we have all the samples available that the app knows about
2010-06-04 00:27:31 -07:00
Chris Robinson 244daba501 Don't overwrite data when the ring buffer overflows 2010-06-04 00:24:23 -07:00
Chris Robinson 43dadcd9e4 Use a UIntMap for the Databuffers 2010-06-03 19:35:08 -07:00
Chris Robinson 90db244b5f Skip mixing after checking looping validity 2010-06-03 03:58:04 -07:00
Chris Robinson e98f1205e5 Increase the temp buffer sizes to construct device names with 2010-05-31 19:04:41 -07:00
Chris Robinson 2205f91e5f Fix Pulse mainloop handling for device probing 2010-05-31 19:02:20 -07:00
Chris Robinson da1dee351d Update device naming to be more apparent about the device being used 2010-05-31 19:00:33 -07:00
Chris Robinson 694e9a5fec Always add the default PulseAudio device when probing 2010-05-30 23:04:04 -07:00
Chris Robinson 9ac5d8ad2d List all available PulseAudio sources 2010-05-30 21:05:05 -07:00
Chris Robinson 706b9f2357 Probe and expose all available PulseAudio sinks for the enumerate_all extension 2010-05-30 20:33:57 -07:00
Chris Robinson 55b3675dee Add a separate function to create/connect a pulseaudio context 2010-05-30 19:05:19 -07:00
Chris Robinson dc86563486 Pass the threaded main loop object to context/stream_state_callback 2010-05-30 18:19:01 -07:00
Chris Robinson ebccfa93c3 Don't unload libs when they're not needed
Some libs don't really like being unloaded and reloaded all the time, and the
benefits aren't that great
2010-05-28 23:00:59 -07:00
Chris Robinson e9f4576d4d Watch for changes to the PulseAudio sink device 2010-05-28 17:03:57 -07:00
Chris Robinson 880902fecc Add a Null Output device
This device will mix and fully process contexts as normal
2010-05-28 16:41:52 -07:00
Chris Robinson 96cf5b7282 Avoid constantly loading and unloading PortAudio
It seems this triggers a bug in the lib, causing a crash. It may not be a bad
idea to remove dynamic unloading/reloading all through-out.
2010-05-28 02:22:17 -07:00
Chris Robinson f89c9f7ea4 Connect a pulse stream on device open, to make sure the device isn't suspended
Also use the opportunity to get the stream's device's name, so later reset
calls will use the same device
2010-05-27 22:12:54 -07:00
Chris Robinson 27a4c871fd Update head dampening after a device reset 2010-05-26 23:36:55 -07:00
Chris Robinson 37190b2caf Add a function to get the frame size from a format 2010-05-24 21:51:30 -07:00
Chris Robinson c6b948323e Better watch for invalid values in alBufferSubDataEXT 2010-05-24 21:33:43 -07:00
Chris Robinson ad2957ed98 Reduce the mixing buffer size 2010-05-24 20:10:35 -07:00
Chris Robinson c469bfcb9e Make error checking consistent in alBuffer.c 2010-05-24 19:52:27 -07:00
Chris Robinson 3d16be1822 Fix alBufferiv error handling 2010-05-24 19:02:36 -07:00
Chris Robinson ad10d4ee74 Increment pointer when read 2010-05-24 17:40:16 -07:00
Chris Robinson 3ba3c1ca31 Pass the number of blocks to ConvertDataIMA4 2010-05-24 17:31:49 -07:00
Chris Robinson 4ad28964de Be careful of integer overflows when allocating buffer storage 2010-05-24 17:08:12 -07:00
Chris Robinson 15ce7fb8f7 Cleanup some variables and names 2010-05-24 03:28:00 -07:00
Chris Robinson 80a22b6484 Reorder some error checks and watch for negative buffer sizes 2010-05-24 03:20:14 -07:00
Chris Robinson 8cfac7c6b8 Don't bother storing the loop points in the source 2010-05-24 01:07:13 -07:00
Chris Robinson 9ac6b4c2c8 Be big endian safe with the IMA4 decoder 2010-05-22 15:37:32 -07:00
Chris Robinson 861a933b7b Use the ALSOFT_LOGFILE env var to specify the output for AL_PRINT 2010-05-21 18:16:27 -07:00
Chris Robinson 58e5404d61 Loop points should be specified in sample offsets 2010-05-21 15:52:35 -07:00
Chris Robinson 3676fdc4db Add mono and stereo channel masks for DSound output 2010-05-21 14:12:09 -07:00
Chris Robinson e7a555a953 Add an empty efx-creative.h header
This is for compatibility with projects that include it in Windows. Everything
from it is already defined in efx.h.
2010-05-21 11:21:35 -07:00
Chris Robinson 1e6ba29cd2 Consistency fix 2010-05-19 11:06:13 -07:00
Chris Robinson c5c83882b3 Use unsigned types when finding the source offsets 2010-05-19 10:38:28 -07:00
Chris Robinson ea3bc0518c A couple formatting fixes 2010-05-19 10:36:24 -07:00
Chris Robinson 2d46a1001e Use a map for the filter list 2010-05-18 17:54:45 -07:00
Chris Robinson 4edd7fe647 Use a map for the effects list 2010-05-18 17:41:06 -07:00
Chris Robinson ad5dfab428 Improve compatbility with latest AL_EXT_buffer_sub_data spec 2010-05-18 17:15:21 -07:00
Chris Robinson 85838c1d57 Define ARGSTACK and CALLSTACK only when needed for testing shared libs 2010-05-16 08:03:05 -07:00
Chris Robinson 4fbb3ccf66 Always look for winmm under Windows
It's needed for timeGetTime, even if the WinMM backend is disabled
2010-05-16 07:52:43 -07:00
Chris Robinson 7f533c6a25 Change inline -> __inline for MSVC 2010-05-16 06:02:27 -07:00
Chris Robinson 6dab9d54d1 Use the loop start when filling in padding data for looping sources 2010-05-13 06:52:44 -07:00
Chris Robinson 7ed5d12717 Pay attention to the source loop points 2010-05-13 04:53:21 -07:00
Chris Robinson 23b2c1f902 Only use a modulo to loop when the source is static 2010-05-13 02:12:50 -07:00
Chris Robinson 5d579dd1a8 Store loop points as uncompressed frame offsets in the source when a buffer is attached 2010-05-13 02:10:33 -07:00
Chris Robinson 0ce138341c Add initial structural support for AL_EXT_loop_points 2010-05-13 02:03:48 -07:00
Chris Robinson 61b682131e Reduce some indentation 2010-05-12 07:30:45 -07:00
Chris Robinson a53e3dda75 Don't needlessly expose a variable for the backends 2010-05-12 07:27:12 -07:00
Chris Robinson 2220c22c90 Move incomplete extension declarations to alMain.h 2010-05-12 06:59:47 -07:00
Chris Robinson e3a70e5021 Scale output of effects to compensate for device down-mixing 2010-05-12 04:56:03 -07:00
Chris Robinson af4faaf666 Use a UIntMap for the effect slot list 2010-05-12 02:20:14 -07:00
Chris Robinson f2548570b8 Store the buffer's original data size and block alignment 2010-05-12 01:36:09 -07:00
Chris Robinson d678f1d88b Store looping locally when mixing a source 2010-05-12 00:38:04 -07:00
Chris Robinson f0ce1bff3e Simplify and fix applying a source offset 2010-05-11 11:59:41 -07:00
Chris Robinson ab3c23478d Use a separate function to convert compressed bytes to frames 2010-05-11 11:06:48 -07:00
Chris Robinson 4e65e9bfa9 Only a streaming source can have a processed buffer 2010-05-11 08:53:38 -07:00
Chris Robinson 053aeee5d3 Rename a variable 2010-05-11 07:09:44 -07:00
Chris Robinson 1652dc02ab Look for the map insertion point using a binary search 2010-05-10 11:40:57 -07:00
Chris Robinson 099c2f3593 Use a simple loop to set multi-channel dry gains 2010-05-09 22:06:10 -07:00
Chris Robinson 0378422fcb Use a map to store sources and buffers
And do a lookup using a binary search instead of linear
2010-05-01 19:59:41 -07:00
Chris Robinson 0760415d08 Use doubles instead of floats for the source offset calculations 2010-04-28 17:58:20 -07:00
Chris Robinson 5730ccaa39 Combine AL and ALC function lists 2010-04-28 15:07:38 -07:00
Chris Robinson aaa7082aa8 Get the offset even if the current buffer is 0 2010-04-28 14:08:10 -07:00
Chris Robinson 5d7815beed Only apply ambient gain to available output channels 2010-04-27 11:39:54 -07:00
Chris Robinson a0ee2ccce7 Add a couple missing values to the *GetEnumValue tables 2010-04-25 08:40:37 -07:00
Chris Robinson 110be7f500 Add an option to scale the output mix 2010-04-23 07:54:43 -07:00
Chris Robinson 52663e8a44 Combine two loops into one 2010-04-23 07:23:38 -07:00
Chris Robinson 8646647b3e Update stereodup config option comment 2010-04-22 05:22:20 -07:00
Chris Robinson a04dac670e Make stereo duplication a property of the device
Further, don't use it with mono/stereo output
2010-04-20 03:57:40 -07:00
Chris Robinson 81dd71dd71 Stop and start the backend device only if new attributes are being requested 2010-04-17 00:50:28 -07:00
Chris Robinson 4d15391c88 Disable PulseAudio's write callback when disconnecting 2010-04-17 00:27:13 -07:00
Chris Robinson fc2473f826 Translate the source position separately
This is to handle the case where an app specifies the same values for the
source and listener, and expects centered panning. This fails due to floating-
point errors in the matrix, causing the result to be ever-so-slightly off of 0.

This error would normally be hidden by the position normalization, which will
not lengthen a distance shorter than the reference distance so the panning
would be nearly imperceptible. But that also fails if the reference distance
is set to 0, causing the position to expand to a full unit.

Keep the 4x4 matrix calculations, however. It will still be useful for the
requested listener matrix extension.
2010-04-16 02:09:53 -07:00
Chris Robinson 000d848f0b Return immediately when no layout is specified 2010-04-11 23:03:36 -07:00
Chris Robinson 3c5007352e More type fixes 2010-04-11 20:17:43 -07:00
Chris Robinson e0fa497948 Use a linear scaling when sending multi-channel sources to auxiliary slots 2010-04-11 15:20:46 -07:00
Chris Robinson f082c73539 Use proper types for some variables 2010-04-11 14:33:22 -07:00
Chris Robinson d4f8bd665f Only consider output speakers for the panning LUT 2010-04-11 14:23:10 -07:00
Chris Robinson 58d0db6eab Don't use LIB_INSTALL_DIR since that can be the full path 2010-04-11 12:42:47 -07:00
Chris Robinson 1544ddf0a5 Avoid reading buffer 0's size 2010-04-10 17:31:08 -07:00
Chris Robinson 488ec612bb Avoid hard-coding the channel count 2010-04-09 14:21:47 -07:00
Chris Robinson 484702a2e5 Get the speaker arrangement before setting up the channel matrix 2010-04-09 12:27:30 -07:00
Chris Robinson 371058fb9e Re-enable stereo duplication 2010-04-08 23:00:14 -07:00
Chris Robinson 97b00670f5 Use the proper type for the speaker-to-channel map 2010-04-08 18:05:24 -07:00
Chris Robinson 704b386cc5 Scale the ring modulator's mono input when expanding to multi-channel 2010-04-08 16:34:51 -07:00
Chris Robinson aace50ebf4 Store the panning LUT in the device 2010-04-08 15:58:11 -07:00
Chris Robinson 49201e892b Use a single layout for the virtual speakers
The downmix should probably be improved to take the angles into account
2010-04-08 15:41:32 -07:00
Chris Robinson 19b8deba66 Apply a scalar to the output downmix so channels don't get more than a 100% mix
All channels get the same scalar so channel balance (should be) maintained
2010-04-08 15:23:03 -07:00
Chris Robinson 068c0076af Set the device channel matrix when the device is reset 2010-04-08 15:02:03 -07:00
Chris Robinson 37c93a6ad8 Always mix internally at 8.1
The channels are remixed when writing to the output buffer. Stereo duplication
is currently broken, but this can be restored later
2010-04-08 14:47:12 -07:00
Chris Robinson 1b1c76da34 Invert device channel map array 2010-04-08 13:11:21 -07:00
Chris Robinson 5db1aec1d1 Make some functions static 2010-04-08 12:14:18 -07:00
Chris Robinson 05ca5c88ef Update alsoftrc.sample for the ring modulator 2010-04-08 10:44:35 -07:00
Chris Robinson 980167c99b Modulate with the requested waveform in the ring modulator 2010-04-08 10:37:09 -07:00
Chris Robinson 76f8c1e4e2 Implement a basic high-pass cutoff filter in the ring modulator 2010-04-08 09:36:20 -07:00
Chris Robinson b273d616ad Implement a skeleton Ring Modulator effect 2010-04-08 08:54:28 -07:00
Chris Robinson c16b895460 Fix non-dynamic PortAudio 2010-04-01 03:25:39 -07:00
Chris Robinson ec1a4884d9 Release 1.12.854 2010-03-29 22:05:21 -07:00
Chris Robinson bb4ed8b1db ALC_EXT_thread_local_context is now complete 2010-03-29 22:01:54 -07:00
Chris Robinson 4e54f93477 Make sure ALbyte and ALCbyte are signed 2010-03-28 23:24:41 -07:00
Chris Robinson 4ac1772149 No need to continue supporting _OPENAL32LIB 2010-03-28 02:12:32 -07:00
Chris Robinson 3f235c51f6 Remove a couple unnecessary lines 2010-03-26 21:58:19 -07:00
Chris Robinson 7bdf0d5156 Shorten some variable names 2010-03-26 00:41:27 -07:00
Chris Robinson d2a6fd58a3 Use unsigned types for mono and stereo source count hints 2010-03-25 18:39:16 -07:00
Chris Robinson 05098862c5 Fix comparison check 2010-03-24 23:48:38 -07:00
Chris Robinson c8dd0246da Fix alcGetEnumValue error handling 2010-03-24 22:57:35 -07:00
Chris Robinson b3c389c0a0 Constify and properly type some variables and struct members 2010-03-24 22:43:08 -07:00
Chris Robinson 15b9fc9099 Improve timing for the wave file writer 2010-03-24 22:20:55 -07:00
Chris Robinson 1c14c13c2f Fix 8-bit output with the Wave Writer 2010-03-24 16:58:58 -07:00
Chris Robinson b79b92967f Fix comment typo 2010-03-24 16:53:11 -07:00
Chris Robinson f77832fea2 Remove unnecessary NULL check 2010-03-24 03:43:37 -07:00
Chris Robinson 4ce8a17afd Reduce some indentation 2010-03-24 02:23:00 -07:00
Chris Robinson 702c62c9a2 Use a global handle to store the process-wide context 2010-03-23 21:05:29 -07:00
Chris Robinson 661059bf0f Rename alcMakeCurrent to alcSetThreadContext 2010-03-23 19:49:31 -07:00
Chris Robinson 7fb4c4ce4f Move extension function declarations to alext.h/efx.h 2010-03-23 18:21:55 -07:00
Chris Robinson 52834744fa Return if PortAudio fails to load in Windows 2010-03-23 17:45:37 -07:00
Chris Robinson 3e9934e810 Mark extension functions with AL_API/ALC_API 2010-03-23 17:44:01 -07:00
Chris Robinson 556e9b8fee Use protected visibility for exported API functions
Protected visibility is like default visibility, in that functions will be
"exported" from the library. However, it also guarantees that references to the
functions from within the library will be to the library's version, even if the
symbols are overriden by the application.
2010-03-22 23:10:29 -07:00
Chris Robinson ef92c63455 Define AL_BUILD_LIBRARY only when building the lib 2010-03-22 23:09:04 -07:00
Chris Robinson ae8858c516 Whitespace fixes 2010-03-22 15:12:20 -07:00
Chris Robinson 0691486813 Support PortAudio in Windows 2010-03-21 20:57:15 -07:00
Chris Robinson 49db3bf916 Cleanup and fixup Release* functions 2010-03-20 21:49:02 -07:00
Chris Robinson 99f28f25b0 Avoid calling alDelete* from alGen* 2010-03-20 21:38:05 -07:00
Chris Robinson 27358c8ce8 Don't expose enums for unsupported filter types 2010-03-20 20:30:43 -07:00
Chris Robinson 899fbdfe08 Add EAX Reverb and Echo parameters to the enum table 2010-03-20 20:24:50 -07:00
Chris Robinson eea23a012a Relax circular layout requirement 2010-03-20 00:54:44 -07:00
Chris Robinson 2235a53824 Reduce some indentation 2010-03-19 20:49:23 -07:00
Chris Robinson 52f82f0b94 Simplify removing the context handle from the device's context array 2010-03-19 14:53:32 -07:00
Chris Robinson a572b13743 Don't use deprecated macros 2010-03-19 14:34:18 -07:00
Chris Robinson 4340a6f8fe Remove another unnecessary assert 2010-03-19 01:35:57 -07:00
Chris Robinson 4be97dc735 Use proper storage type for the new format 2010-03-19 01:26:56 -07:00
Chris Robinson 799126df0e Use a better method to derive the offset from the data pointer 2010-03-18 19:32:03 -07:00
Alam Arias a65ec178df cast AL_SIZE value to ALint 2010-03-18 16:33:16 -07:00
Alam Arias cf4027df38 Mingw64's shlobj.h wants at least 0x501 2010-03-18 16:33:08 -07:00
Alam Arias 127b845960 DXSDK_DIR is MSVC only 2010-03-18 16:32:53 -07:00
Alam Arias e3637b8452 use ALintptrEXT in alcReverb.c 2010-03-18 16:32:49 -07:00
Alam Arias 745d36dc76 add _CRT_NONSTDC_NO_DEPRECATE define for MSVC 2010-03-18 16:32:44 -07:00
Chris Robinson 2355d6a4a0 Use ptrdiff types for databuffer offset/length handling
Should improve addressing for non-32-bit systems
2010-03-18 14:46:33 -07:00
Chris Robinson e1965fe7d9 Convert CR/LF -> LF
Spotted by Alam Arias
2010-03-18 14:23:31 -07:00
Chris Robinson 781444d12c Use size_t where appropriate
Spotted by Alam Arias
2010-03-18 14:21:06 -07:00
Chris Robinson b6571904ce Define _WIN32_IE to 0x400 only if it's a lesser value by default
Spotted by Alam Arias
2010-03-18 13:46:18 -07:00
Chris Robinson 97b25755e7 Support capture with PortAudio 2010-03-18 01:58:25 -07:00
Chris Robinson bdfafb454b Create WAVEFORMATEXTENSIBLE wave files for multi-channel and float support 2010-03-18 00:53:58 -07:00
Chris Robinson 0fee20a415 Simplifiy verification loops 2010-03-17 20:16:02 -07:00
Chris Robinson 186e0f3b04 Use a separate list for NULL-device extensions 2010-03-17 19:46:20 -07:00
Chris Robinson 2e6c5e590f Remove old comment 2010-03-17 19:28:01 -07:00
Chris Robinson 9d4e715ee7 Don't return unsupported effects from alGetEnumValue 2010-03-17 19:20:52 -07:00
Chris Robinson 932e5ec9b8 The enums are the same as with mcforamts, so advertise AL_EXT_MULAW too 2010-03-17 19:15:51 -07:00
Chris Robinson af5fb1d535 Remove unnecessary assert 2010-03-17 19:04:02 -07:00
Chris Robinson 2953daebf5 GetSourceOffset can't fail 2010-03-17 15:19:00 -07:00
Chris Robinson dd82b68175 Handle source offsets for muLaw formats 2010-03-17 15:15:12 -07:00
Chris Robinson 05a081a7b8 Allow selecting databuffer 0 2010-03-17 14:04:25 -07:00
Chris Robinson 9c417d33cc clock_gettime is available only when _POSIX_TIMERS is greater than 0 2010-03-17 12:04:33 -07:00
Chris Robinson 975f92fc84 Remove unnecessary stop call 2010-03-16 21:29:55 -07:00
Chris Robinson 0097beb84c Remove unused variable 2010-03-16 21:12:36 -07:00
Chris Robinson 65e16cc8bb Remove unneeded checks 2010-03-16 20:29:01 -07:00
Chris Robinson 2687155200 Get rid of more alIs* calls 2010-03-16 20:23:46 -07:00
Chris Robinson 49321961e8 Effect and filter ID 0 is valid 2010-03-16 19:14:05 -07:00
Chris Robinson 65f43637ce Avoid some alIs* calls while under the context lock
Instead, use functions that verify the ID by searching the object list and
returning its handle
2010-03-16 18:54:36 -07:00
Chris Robinson f37cfc486d Pass the context to alSetError 2010-03-16 17:35:51 -07:00
Chris Robinson a337a59fb8 Use alext.h for extension definitions 2010-03-16 16:25:30 -07:00
Chris Robinson ec917e8e2f Rename some struct members for consistency 2010-03-16 15:37:41 -07:00
Chris Robinson 89d84131a4 Avoid dereferencing a NULL buffer when unqueueing buffer 0 2010-03-15 16:10:21 -07:00
Chris Robinson 2033f60d24 Print an error when realloc fails 2010-03-15 14:28:38 -07:00
Chris Robinson d4159430f0 Set the buffer length for Pulse when opening the device
So it will properly scale with context frequency requests
2010-03-14 22:20:55 -07:00
Chris Robinson 14255a38fc Pulse's buffer size is not a good measurement for the update size
Lie and say 20ms instead, since there is no stable method to get such info
2010-03-14 21:45:23 -07:00
Chris Robinson 6c5f1b78d7 Reduce the mixing buffers to the next power of two 2010-03-14 03:30:42 -07:00
Chris Robinson 1f2d3a0132 Remove duplication of setting source angles 2010-03-14 03:23:18 -07:00
Chris Robinson c23e4ed412 Add support for AL_EXT_DOUBLE 2010-03-13 21:32:45 -08:00
Chris Robinson 8c0118e7ae Add support for AL_EXT_MULAW_MCFORMATS 2010-03-13 21:03:34 -08:00
Chris Robinson 9d4107a515 Add AL_EXT_MULAW_MCFORMATS tokens to alext.h 2010-03-13 18:39:12 -08:00
Chris Robinson d07b9c2c11 Rewrite openal-info
Output is mostly unchanged, however there are differences. Device-specific info
(including context-specific info) is separated from NULL-device queries, and
there is now a distinction between ALC versions of the NULL device and the
opened device. Additionally, an alternate playback device can be queried by
specifying its name as the first parameter.
2010-03-13 18:03:01 -08:00
Chris Robinson 3108a6a997 Limit PulseAudio's buffer size to the requested length 2010-03-11 09:47:26 -08:00
Chris Robinson b734cab6fc Fix the retrieval of the RW offsets using the correct functions 2010-03-10 07:33:30 -08:00
Chris Robinson 08b1bed38b Remove a couple incorrect comments 2010-03-10 07:16:53 -08:00
Chris Robinson df6320eb0a Remove duplicate copyright notice 2010-03-09 11:25:34 -08:00
Chris Robinson 80f1ec0d7e Protect some capture calls with a mutex 2010-03-09 10:14:52 -08:00
Chris Robinson 98f4f5a4f0 Add EFX types and enums to alext.h
The types and enums are in efx.h, which is included by alext.h. It's done this
way because EFX has a lot if definitions which would polute alext.h
2010-03-09 09:28:22 -08:00
Chris Robinson 8feb089f5c Build device lists only when needed 2010-03-09 06:09:29 -08:00
Chris Robinson 2ba3a88ace Probe physical devices separately from appending them to the device list 2010-03-09 05:44:18 -08:00
Chris Robinson 1f10195c47 Use powf when available 2010-03-07 22:12:33 -08:00
Chris Robinson dc40702b53 Remove unused source param count macro 2010-03-07 21:48:25 -08:00
Chris Robinson a9e0e57797 Keep UpdateSize updated when apps request a frequency 2010-02-24 18:51:57 -08:00
Chris Robinson 9b47048231 Silence some warnings 2010-02-16 05:44:39 -08:00
Chris Robinson c05a3d3549 Better handle run-time linking 2010-02-16 05:43:36 -08:00
Chris Robinson 2083884ae3 Properly check for functions in Win32 shared libs 2010-02-16 05:36:49 -08:00
Chris Robinson 2f644c7ed5 Constify some globals 2010-02-15 22:01:23 -08:00
Chris Robinson d17c4a098c Avoid probing for devices on lib initialization 2010-02-10 16:34:43 -08:00
Chris Robinson 96303893bc Set ALSA playback buffer and period size based on time
So rate adjustments won't modify the latency
2010-02-08 17:42:44 -08:00
Chris Robinson a329f9c84a Fix retrieval of the current global context when changing it 2010-02-08 16:58:26 -08:00
Chris Robinson fc96e965ae Always disable ALSA's resampler for playback
This means the frequency config option won't have an effect if the ALSA device
can't set an alternate sampling rate
2010-02-08 16:38:53 -08:00
Chris Robinson 365a62d63a Improve source write offset calculations 2010-02-07 20:11:24 -08:00
Chris Robinson 3a87c3c3ba Avoid using periods with PulseAudio, and add an option to specify its buffer length 2010-02-07 19:29:48 -08:00
Chris Robinson 54dc26b145 Enable the WinMM backend only if the lib check succeeds 2010-02-05 12:07:51 -08:00
Chris Robinson 78c1629aeb Disable ALSA's resampler when no specific output rate is requested 2010-02-05 11:45:17 -08:00
Chris Robinson bd518ed264 Make sure all requested data gets written to PulseAudio 2010-01-24 00:03:23 -08:00
Chris Robinson 56379bffac Use pa_channel_map_superset only when available with PulseAudio 0.9.15
pa_channel_map_equal can otherwise detect some compatible output modes
2010-01-22 21:20:08 -08:00
Chris Robinson 11d7a0e394 Specify the buffer size to ALSA, so the period count remains flexible
Under conditions where the period size is fixed (such as dmix), ALSA will still
attempt to provide the requested number of periods even if the requested period
size needs to be dramatically altered. In these cases, it would be better to
increase or decrease the period count, instead of growing or shrinking the
total buffer size and significantly changing the latency.
2010-01-20 01:19:32 -08:00
Chris Robinson 3ad86a1c1b Release 1.11.753 2010-01-17 17:46:27 -08:00
Chris Robinson 931f5875cd Fix speaker angle fixup 2010-01-12 09:11:46 -08:00
Chris Robinson f22b02a762 al_print doesn't need to be inline 2010-01-12 09:05:57 -08:00
Chris Robinson 505f61ff43 Properly trace the invalid speaker angle 2010-01-12 08:50:32 -08:00
Chris Robinson 0c2efa1253 Use GCC's format attribute for al_printf 2010-01-12 08:48:25 -08:00
Chris Robinson 5921e18be2 Fix some typos with the solaris backend 2010-01-12 08:28:07 -08:00
Chris Robinson 5b9e6fe440 Only allow 8- and 16-bit, and mono and stereo, wave file output 2010-01-12 08:26:25 -08:00
Chris Robinson 35ba7d70dc Consolidate some source offset handling code 2010-01-12 02:43:37 -08:00
Chris Robinson 026b4b8f4c Better handle large second offsets 2010-01-12 02:26:38 -08:00
Chris Robinson c924a50bed Don't use a flag to set an error when applying the source offset 2010-01-12 02:22:38 -08:00
Chris Robinson 574792d2e1 Fix Windows compilation 2010-01-11 23:47:03 -08:00
Chris Robinson ab2eb5a603 Check buffer size when copying padding instead of the data pointer 2010-01-11 10:58:33 -08:00
Chris Robinson 3d7f925d52 Add helpful values to the resampler enum 2010-01-11 07:02:29 -08:00
Chris Robinson 93b584ff84 Add a cosine resampler 2010-01-11 06:19:54 -08:00
Chris Robinson 705849ca73 Add an option for point resampling 2010-01-11 05:37:20 -08:00
Chris Robinson 1a57f095e8 Hold global lock during mixing only when iterating device contexts 2010-01-11 03:57:49 -08:00
Chris Robinson ac9c2e8c4a Cleanup OSS/Solaris loop a bit 2010-01-10 12:40:59 -08:00
Chris Robinson 45f8efec41 Return the default config string for empty entries 2010-01-09 06:48:43 -08:00
Chris Robinson ef7df2416d Hide OSS and Solaris devices if their files can't be stat'd 2010-01-09 06:40:50 -08:00
Chris Robinson cb113315f7 Promote AL_EXT_source_distance_model to complete 2010-01-09 03:27:16 -08:00
Chris Robinson d61f90a9c2 Make sure the device handle is valid when getting the device error 2010-01-09 03:25:13 -08:00
Chris Robinson 3d11fb219e Hold the default context extensions globally 2010-01-09 02:48:18 -08:00
Chris Robinson 4d9a368f96 Suppress connection refused errors
Since that just typically means the server isn't running
2010-01-08 01:43:27 -08:00
Chris Robinson 43220b604c Reduce some indentation 2010-01-08 01:21:57 -08:00
Chris Robinson 91f28fa87c Try the PulseAudio backend first 2010-01-08 01:08:27 -08:00
Chris Robinson 82547db2db Prepare the pcm handle after recovering 2010-01-06 23:02:28 -08:00
Chris Robinson 64ac3a4739 Use snd_pcm_recover in more places 2010-01-06 22:55:20 -08:00
Chris Robinson c90616b963 Use snd_pcm_recover to recover from certain device errors 2010-01-06 22:50:30 -08:00
Chris Robinson 0ceaa01c3d Adjust PulseAudio's capture latency 2009-12-30 15:33:16 -08:00
Chris Robinson 7d7e503603 Properly retrieve device-inspecific errors 2009-12-28 23:29:49 -08:00
Chris Robinson 7ee81eac0f Store ALC errors with the device when possible 2009-12-28 23:19:13 -08:00
Chris Robinson c9a08fc7b2 Remove some unneeded functions 2009-12-28 13:12:45 -08:00
Chris Robinson 69ab93a824 Add a function to check if a config option is set to a non-empty value 2009-12-28 13:08:15 -08:00
Chris Robinson 179b660eee Enforce a lower bound playback frequency of 8khz 2009-12-28 12:47:59 -08:00
Chris Robinson a0ef7be9a5 Improve layout parsing and allow using long names for speakers 2009-12-28 11:49:55 -08:00
Chris Robinson 2a735b14dc Add an option to spawn a PulseAudio server on request 2009-12-28 10:49:31 -08:00
Chris Robinson a294dd8d9b Be more lenient in signaling the mainloop 2009-12-28 10:15:59 -08:00
Chris Robinson dc1522282b Try to find a compatible format from Pulse's default sink 2009-12-27 11:05:59 -08:00
Chris Robinson 528a4c6a02 Update panning for existing AL contexts when creating a new one 2009-12-26 20:27:14 -08:00
Chris Robinson 1770a8b62b A NULL Pulse context won't have a valid error code 2009-12-26 12:18:16 -08:00
Chris Robinson 77a0a6de95 Wait for the buffer attrib update from pulse to finish 2009-12-26 11:30:41 -08:00
Chris Robinson c1fd977e79 Consolidate pulse capture failure path 2009-12-26 10:46:29 -08:00
Chris Robinson cbbda93292 Make sure a proper channel map is set for pulse capture 2009-12-26 09:17:03 -08:00
Chris Robinson c6340ce12d Clean up some loading checks 2009-12-26 08:49:11 -08:00
Chris Robinson 7bc739e965 Use the proper logging function 2009-12-26 08:16:25 -08:00
Chris Robinson 9f988808c7 Start and stop PortAudio's stream in the proper methods 2009-12-26 08:14:28 -08:00
Chris Robinson 6e9ce47a4b GetConfigValueBool should take an int, not a float 2009-12-26 07:42:57 -08:00
Chris Robinson 89f59c0e1c Shorten a long line 2009-12-26 07:42:16 -08:00
Chris Robinson 3793919892 Capture as much as possible directly from PulseAudio 2009-12-25 17:56:57 -08:00
Chris Robinson d1699d50f6 Add missing newline 2009-12-25 15:18:32 -08:00
Chris Robinson b94d950b1e Undefine no longer needed macro 2009-12-25 14:11:43 -08:00
Chris Robinson d3e77a0f63 Fix incorrect error check 2009-12-25 13:19:25 -08:00
Chris Robinson 965a71ee90 Only use pa_stream_set_buffer_attr_callback when available (0.9.15+) 2009-12-25 13:17:21 -08:00
Chris Robinson fd794b44b2 Use pa_stream_begin_write when available for "zero-copy" writing
Must be compiled against 0.9.16 or newer to be available. It will fall back to
the old method if the function is not available at run-time.
2009-12-25 13:11:17 -08:00
Chris Robinson c9dcba17a9 Check error returns instead of error string 2009-12-25 13:05:04 -08:00
Chris Robinson 88de4e15b3 Mark the source for updating when setting a buffer on it
Changing the buffer can change the playback format, which can require different
source parameter calculations. Queueing a buffer on a source that has no
buffers (or only buffers without a format, eg. buffer 0) has the same effect
2009-12-25 05:09:29 -08:00
Chris Robinson 7f0c6629b9 Use a 64-bit value to scale potentially-large numbers 2009-12-24 15:41:45 -08:00
Chris Robinson f6ca39403d Use the Pulse server-specified playback rate by default 2009-12-24 15:35:22 -08:00
Chris Robinson ea7cf35ea4 Use pa_xmalloc instead of pa_xmalloc0 2009-12-24 07:38:46 -08:00
Chris Robinson accea119d9 Update pulse capture ring buffer in 25ms chunks 2009-12-24 04:08:04 -08:00
Chris Robinson 6ea7b43c90 Don't force latency adjustment with PulseAudio 2009-12-23 13:59:58 -08:00
Chris Robinson ddfad996a9 Don't render mono as stereo 2009-12-21 02:59:30 -08:00
Chris Robinson d786f99094 Scale samples down when converting stereo to mono 2009-12-21 02:27:25 -08:00
Chris Robinson 008563450c Improve some indentations 2009-12-20 21:50:00 -08:00
Chris Robinson 69c238a2e6 Properly retrieve the device frequency 2009-12-20 21:19:57 -08:00
Chris Robinson b3fdf170b1 Fix header comment for AL_PITCH 2009-12-16 22:50:54 -08:00
Chris Robinson 56cc038603 Improve initial decay calculations for sends to reverb 2009-12-10 18:37:36 -08:00
Chris Robinson 4c83c689a8 Use the specified reverb air absorption value for wet sends 2009-12-10 18:22:45 -08:00
Chris Robinson 2b7953c472 Track whether a context is suspended or not 2009-12-09 12:14:53 -08:00
Chris Robinson 368ef1cea9 Store the source state locally after making sure it needs mixing 2009-12-09 11:56:53 -08:00
Chris Robinson bd696a6b37 Fix IMA ADPCM offset calculation 2009-12-09 09:50:09 -08:00
Chris Robinson a9fc272e39 Prettify CMake checks for compiler switches 2009-12-09 08:46:35 -08:00
Chris Robinson c5952d1ddc Move the pkg-config file to the base directory 2009-12-09 07:36:06 -08:00
Chris Robinson 656a406377 Use an inline function to calculate the low-pass filter coefficient 2009-12-09 07:21:59 -08:00
Chris Robinson 5fcd6cc510 Split non-attenuated source calculations into a separate function 2009-12-09 07:02:26 -08:00
Chris Robinson e09b6020a6 Fix typo for reverb creation 2009-12-09 06:46:06 -08:00
Chris Robinson 84d2d623b6 Add a head-dampening option
This simulates occlusion of the player's head for sounds coming from behind,
when outputing to mono or stereo
2009-12-08 14:18:07 -08:00
Chris Robinson 1694e5bd12 Store the original pre-clamped distance instead of re-computing it 2009-12-08 10:12:18 -08:00
Chris Robinson 5170e251f9 Use the correct wet filter history offset 2009-12-07 12:45:23 -08:00
Chris Robinson 4697e946d3 Internally store 32-bit float buffer data, and mix accordingly 2009-12-07 10:35:30 -08:00
Chris Robinson 9286e3984c Move default channel order setting out of the header 2009-12-07 04:19:33 -08:00
Chris Robinson 88f3d747b7 Add a simple README 2009-12-07 01:05:01 -08:00
Chris Robinson d4427c54ea Change internal order to match WFX 2009-12-07 00:51:27 -08:00
Chris Robinson 92ade80557 Fix 6.1 channel order 2009-12-07 00:49:56 -08:00
Chris Robinson 8138446b8c Restrict setting some wet path values to active sends only 2009-12-06 03:59:12 -08:00
Chris Robinson 58ecc7fad4 Check for potential NULL accesses with the IsBadWritePtr wrapper 2009-12-04 01:33:50 -08:00
Chris Robinson 311cc5c62f Don't wait to accept stream connections 2009-12-03 04:43:38 -08:00
Chris Robinson 4a928ea9fa Don't signal on synchronous changes and don't wait to accept context changes 2009-12-02 08:10:45 -08:00
Chris Robinson c1eb444a39 Remove some assumptions of 16-bit internal storage 2009-12-02 05:55:33 -08:00
Chris Robinson b5270e0bb3 Use a channel-map to specify the output device channel order 2009-12-02 04:03:51 -08:00
Chris Robinson 6cfc31777b Add an option for real-time priority mixing
Default to disable for now, as a safety precaution
2009-12-01 23:15:09 -08:00
Chris Robinson 0e1e8503e0 Properly accept PulseAudio's mainloop signal on connection failure 2009-12-01 22:42:43 -08:00
Chris Robinson 974d0b4e91 Fix retrieved update size from pulseaudio 2009-12-01 12:21:02 -08:00
Chris Robinson d66832a39c Accept a signal only when the pulseaudio stream/context is ready 2009-12-01 12:18:34 -08:00
Chris Robinson d5889a941f Clamp dry gain after cone attenuation is applied 2009-12-01 03:32:04 -08:00
Chris Robinson 6248e986e5 Don't complain if the length given by pulse isn't an exact multiple of the period size 2009-12-01 02:23:38 -08:00
Chris Robinson 786b98a013 Avoid setting a field redundantly 2009-11-29 23:07:04 -08:00
Chris Robinson f63d8dbf38 Enable real-time priority for ALSA, OSS, and DirectSound mixing loops 2009-11-29 23:02:21 -08:00
Chris Robinson 15aad09ce7 Check for the pthread_setschedparam function 2009-11-29 22:44:56 -08:00
Chris Robinson fdd314f7f8 Reset the kill flag after the thread has stopped 2009-11-29 10:22:12 -08:00
Chris Robinson 39b456d88c Add AL_EXT_double tokens to alext.h 2009-11-28 22:19:04 -08:00
Chris Robinson d930fb8d3d Call the Update effect method for null effects 2009-11-28 20:09:41 -08:00
Chris Robinson 3ec64eb0fd Call to the right databuffer function 2009-11-28 19:37:29 -08:00
Chris Robinson d81c9819b8 Remove unnecessary returns 2009-11-28 19:36:05 -08:00
Chris Robinson 98ce1d14c1 Update AL_EXTX_source_distance_model to require explicit enabling
The in-progress spec has been updated to reflect this
2009-11-27 20:05:21 -08:00
Chris Robinson 69f9ab88b9 Send multi-channel sources to auxiliary effect slots
They are downmixed to mono using a volume-preserving scalar, and passed
through a 1-pole low-pass filter (not chained)
2009-11-26 00:39:32 -08:00
Chris Robinson 1a0676f0eb Mark sources for updating when a new context is made from the device 2009-11-25 16:28:19 -08:00
Chris Robinson 658923175f Update source parameters only when they need changing 2009-11-25 16:21:47 -08:00
Chris Robinson 877f4340ba Base air absorption on distance attenuation, try 2 2009-11-24 21:19:11 -08:00
Chris Robinson 35a9ccb41f Revert "Base air absorption on the distance attenuation"
This reverts commit c7c2f9385f.

Fundamentally wrong. The -6dB rolloff is every doubling of the distance, and
the (scaled) -0.05dB rolloff is supposed to be every additional unit.
2009-11-24 01:39:34 -08:00
Chris Robinson c7c2f9385f Base air absorption on the distance attenuation 2009-11-23 16:25:37 -08:00
Chris Robinson d1f3dbb9a1 Make modulation index 0 map to offset 0 2009-11-23 04:14:12 -08:00
Chris Robinson cfb30fe975 Use a 4x4 matrix to transform coordinates 2009-11-23 04:13:51 -08:00
Chris Robinson 2d1b378ef2 Transform all relevant vectors for converting world-space to listener-space 2009-11-22 22:36:20 -08:00
Chris Robinson 06bbee2449 Add a function for 1-pole filtering 2009-11-22 21:20:27 -08:00
Chris Robinson 85bc300dfa ALCAPIENTRY is deprecated in favor of ALC_APIENTRY 2009-11-22 21:15:11 -08:00
Chris Robinson 0662adacb9 Fix alBufferDataStatic extension name in alext.h 2009-11-22 21:12:43 -08:00
Chris Robinson 65a153da6c Update the drivers config file comment 2009-11-22 15:43:05 -08:00
Chris Robinson c5ee010640 The echo should not take energy-attenuated input 2009-11-22 15:37:32 -08:00
Chris Robinson 1b18b4a0ee Allow unlisted backends to stay available when the drivers list ends with , 2009-11-22 01:06:05 -08:00
Chris Robinson 58765aab6b Print available devices before trying to open one 2009-11-19 19:02:16 -08:00
Chris Robinson 1483af1baa Watch for NULL and empty device lists 2009-11-19 18:49:24 -08:00
Chris Robinson 13979793f6 Display supported filters using a comma-separated listing 2009-11-19 18:46:23 -08:00
Chris Robinson 9c228e7625 Be clearer if device opening or context setup fails 2009-11-19 18:25:29 -08:00
Chris Robinson 6a667b36d1 Reorganize and improve the reverb effect
Code supplied by Christopher Fitzgerald. This update also implements the echo
and modulation parameters.
2009-11-19 14:05:04 -08:00
Chris Robinson fe37f1968d Better approximate the wet attenuation model for reverb 2009-11-19 11:06:41 -08:00
Chris Robinson 4565bdd7ce Make sure there's at least a one-sample delay for the echo's first tap 2009-11-19 10:42:41 -08:00
Chris Robinson 5f3c07ca3b Apply the reverb room rolloff factor for EAX reverb too 2009-11-19 10:29:10 -08:00
Chris Robinson 8d1da6a8ca Move NextPowerOf2 to alMain.h 2009-11-19 09:50:15 -08:00
Chris Robinson ff8e09495d Move aluCart2LUTpos to alu.h 2009-11-19 09:24:35 -08:00
Chris Robinson e868eef679 Make sure to set the new echo sample buffer and remove an unused macro 2009-11-18 17:34:08 -08:00
162 changed files with 57033 additions and 17706 deletions
+6
View File
@@ -1 +1,7 @@
build
winbuild
win64build
include/SLES
include/sndio.h
include/sys
openal-soft.kdev4
+3298 -1280
View File
File diff suppressed because it is too large Load Diff
+1090 -1158
View File
File diff suppressed because it is too large Load Diff
+351 -160
View File
@@ -19,8 +19,12 @@
*/
#ifdef _WIN32
#ifdef __MINGW32__
#define _WIN32_IE 0x501
#else
#define _WIN32_IE 0x400
#endif
#endif
#include "config.h"
@@ -28,219 +32,381 @@
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "alMain.h"
#ifdef _WIN32_IE
#include <shlobj.h>
#endif
#include "alMain.h"
#include "compat.h"
typedef struct ConfigEntry {
char *key;
char *value;
} ConfigEntry;
typedef struct ConfigBlock {
char *name;
ConfigEntry *entries;
size_t entryCount;
unsigned int entryCount;
} ConfigBlock;
static ConfigBlock cfgBlock;
static ConfigBlock *cfgBlocks;
static size_t cfgCount;
static char buffer[1024];
static char *lstrip(char *line)
{
while(isspace(line[0]))
line++;
return line;
}
static char *rstrip(char *line)
{
size_t len = strlen(line);
while(len > 0 && isspace(line[len-1]))
len--;
line[len] = 0;
return line;
}
static int readline(FILE *f, char **output, size_t *maxlen)
{
size_t len = 0;
int c;
while((c=fgetc(f)) != EOF && (c == '\r' || c == '\n'))
;
if(c == EOF)
return 0;
do {
if(len+1 >= *maxlen)
{
void *temp = NULL;
size_t newmax;
newmax = (*maxlen ? (*maxlen)<<1 : 32);
if(newmax > *maxlen)
temp = realloc(*output, newmax);
if(!temp)
{
ERR("Failed to realloc "SZFMT" bytes from "SZFMT"!\n", newmax, *maxlen);
return 0;
}
*output = temp;
*maxlen = newmax;
}
(*output)[len++] = c;
(*output)[len] = '\0';
} while((c=fgetc(f)) != EOF && c != '\r' && c != '\n');
return 1;
}
static char *expdup(const char *str)
{
char *output = NULL;
size_t maxlen = 0;
size_t len = 0;
while(*str != '\0')
{
const char *addstr;
size_t addstrlen;
size_t i;
if(str[0] != '$')
{
const char *next = strchr(str, '$');
addstr = str;
addstrlen = next ? (size_t)(next-str) : strlen(str);
str += addstrlen;
}
else
{
str++;
if(*str == '$')
{
const char *next = strchr(str+1, '$');
addstr = str;
addstrlen = next ? (size_t)(next-str) : strlen(str);
str += addstrlen;
}
else
{
char envname[1024];
size_t k = 0;
while((isalnum(*str) || *str == '_') && k < sizeof(envname)-1)
envname[k++] = *(str++);
envname[k++] = '\0';
if((addstr=getenv(envname)) == NULL)
continue;
addstrlen = strlen(addstr);
}
}
if(addstrlen == 0)
continue;
if(addstrlen >= maxlen-len)
{
void *temp = NULL;
size_t newmax;
newmax = len+addstrlen+1;
if(newmax > maxlen)
temp = realloc(output, newmax);
if(!temp)
{
ERR("Failed to realloc "SZFMT" bytes from "SZFMT"!\n", newmax, maxlen);
return output;
}
output = temp;
maxlen = newmax;
}
for(i = 0;i < addstrlen;i++)
output[len++] = addstr[i];
output[len] = '\0';
}
return output ? output : calloc(1, 1);
}
static void LoadConfigFromFile(FILE *f)
{
ConfigBlock *curBlock = cfgBlocks;
char curSection[128] = "";
char *buffer = NULL;
size_t maxlen = 0;
ConfigEntry *ent;
while(fgets(buffer, sizeof(buffer), f))
while(readline(f, &buffer, &maxlen))
{
size_t i = 0;
char *line, *comment;
char key[256] = "";
char value[256] = "";
while(isspace(buffer[i]))
i++;
if(!buffer[i] || buffer[i] == '#')
comment = strchr(buffer, '#');
if(comment) *(comment++) = 0;
line = rstrip(lstrip(buffer));
if(!line[0])
continue;
memmove(buffer, buffer+i, strlen(buffer+i)+1);
if(buffer[0] == '[')
if(line[0] == '[')
{
ConfigBlock *nextBlock;
char *section = line+1;
char *endsection;
i = 1;
while(buffer[i] && buffer[i] != ']')
i++;
if(!buffer[i])
endsection = strchr(section, ']');
if(!endsection || section == endsection || endsection[1] != 0)
{
AL_PRINT("config parse error: bad line \"%s\"\n", buffer);
ERR("config parse error: bad line \"%s\"\n", line);
continue;
}
buffer[i] = 0;
*endsection = 0;
do {
i++;
if(buffer[i] && !isspace(buffer[i]))
{
if(buffer[i] != '#')
AL_PRINT("config warning: extra data after block: \"%s\"\n", buffer+i);
break;
}
} while(buffer[i]);
nextBlock = NULL;
for(i = 0;i < cfgCount;i++)
if(strcasecmp(section, "general") == 0)
curSection[0] = 0;
else
{
if(strcasecmp(cfgBlocks[i].name, buffer+1) == 0)
{
nextBlock = cfgBlocks+i;
// AL_PRINT("found block '%s'\n", nextBlock->name);
break;
}
strncpy(curSection, section, sizeof(curSection)-1);
curSection[sizeof(curSection)-1] = 0;
}
if(!nextBlock)
{
nextBlock = realloc(cfgBlocks, (cfgCount+1)*sizeof(ConfigBlock));
if(!nextBlock)
{
AL_PRINT("config parse error: error reallocating config blocks\n");
continue;
}
cfgBlocks = nextBlock;
nextBlock = cfgBlocks+cfgCount;
cfgCount++;
nextBlock->name = strdup(buffer+1);
nextBlock->entries = NULL;
nextBlock->entryCount = 0;
// AL_PRINT("found new block '%s'\n", nextBlock->name);
}
curBlock = nextBlock;
continue;
}
/* Look for the option name */
i = 0;
while(buffer[i] && buffer[i] != '#' && buffer[i] != '=' &&
!isspace(buffer[i]))
i++;
if(!buffer[i] || buffer[i] == '#' || i == 0)
if(sscanf(line, "%255[^=] = \"%255[^\"]\"", key, value) == 2 ||
sscanf(line, "%255[^=] = '%255[^\']'", key, value) == 2 ||
sscanf(line, "%255[^=] = %255[^\n]", key, value) == 2)
{
AL_PRINT("config parse error: malformed option line: \"%s\"\n", buffer);
/* sscanf doesn't handle '' or "" as empty values, so clip it
* manually. */
if(strcmp(value, "\"\"") == 0 || strcmp(value, "''") == 0)
value[0] = 0;
}
else if(sscanf(line, "%255[^=] %255[=]", key, value) == 2)
{
/* Special case for 'key =' */
value[0] = 0;
}
else
{
ERR("config parse error: malformed option line: \"%s\"\n\n", line);
continue;
}
rstrip(key);
/* Seperate the option */
if(buffer[i] != '=')
if(curSection[0] != 0)
{
buffer[i++] = 0;
while(isspace(buffer[i]))
i++;
if(buffer[i] != '=')
{
AL_PRINT("config parse error: option without a value: \"%s\"\n", buffer);
continue;
}
size_t len = strlen(curSection);
memmove(&key[len+1], key, sizeof(key)-1-len);
key[len] = '/';
memcpy(key, curSection, len);
}
/* Find the start of the value */
buffer[i++] = 0;
while(isspace(buffer[i]))
i++;
/* Check if we already have this option set */
ent = curBlock->entries;
while((size_t)(ent-curBlock->entries) < curBlock->entryCount)
ent = cfgBlock.entries;
while((unsigned int)(ent-cfgBlock.entries) < cfgBlock.entryCount)
{
if(strcasecmp(ent->key, buffer) == 0)
if(strcasecmp(ent->key, key) == 0)
break;
ent++;
}
if((size_t)(ent-curBlock->entries) >= curBlock->entryCount)
if((unsigned int)(ent-cfgBlock.entries) >= cfgBlock.entryCount)
{
/* Allocate a new option entry */
ent = realloc(curBlock->entries, (curBlock->entryCount+1)*sizeof(ConfigEntry));
ent = realloc(cfgBlock.entries, (cfgBlock.entryCount+1)*sizeof(ConfigEntry));
if(!ent)
{
AL_PRINT("config parse error: error reallocating config entries\n");
ERR("config parse error: error reallocating config entries\n");
continue;
}
curBlock->entries = ent;
ent = curBlock->entries + curBlock->entryCount;
curBlock->entryCount++;
cfgBlock.entries = ent;
ent = cfgBlock.entries + cfgBlock.entryCount;
cfgBlock.entryCount++;
ent->key = strdup(buffer);
ent->key = strdup(key);
ent->value = NULL;
}
/* Look for the end of the line (Null term, new-line, or #-symbol) and
eat up the trailing whitespace */
memmove(buffer, buffer+i, strlen(buffer+i)+1);
i = 0;
while(buffer[i] && buffer[i] != '#' && buffer[i] != '\n')
i++;
do {
i--;
} while(isspace(buffer[i]));
buffer[++i] = 0;
free(ent->value);
ent->value = strdup(buffer);
ent->value = expdup(value);
// AL_PRINT("found '%s' = '%s'\n", ent->key, ent->value);
TRACE("found '%s' = '%s'\n", ent->key, ent->value);
}
free(buffer);
}
#ifdef _WIN32
void ReadALConfig(void)
{
WCHAR buffer[PATH_MAX];
const WCHAR *str;
FILE *f;
cfgBlocks = calloc(1, sizeof(ConfigBlock));
cfgBlocks->name = strdup("general");
cfgCount = 1;
#ifdef _WIN32
if(SHGetSpecialFolderPathA(NULL, buffer, CSIDL_APPDATA, FALSE) != FALSE)
if(SHGetSpecialFolderPathW(NULL, buffer, CSIDL_APPDATA, FALSE) != FALSE)
{
int p = strlen(buffer);
snprintf(buffer+p, sizeof(buffer)-p, "\\alsoft.ini");
f = fopen(buffer, "rt");
size_t p = lstrlenW(buffer);
_snwprintf(buffer+p, PATH_MAX-p, L"\\alsoft.ini");
TRACE("Loading config %ls...\n", buffer);
f = _wfopen(buffer, L"rt");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
}
if((str=_wgetenv(L"ALSOFT_CONF")) != NULL && *str)
{
TRACE("Loading config %ls...\n", str);
f = _wfopen(str, L"rt");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
}
}
#else
f = fopen("/etc/openal/alsoft.conf", "r");
void ReadALConfig(void)
{
char buffer[PATH_MAX];
const char *str;
FILE *f;
str = "/etc/openal/alsoft.conf";
TRACE("Loading config %s...\n", str);
f = al_fopen(str, "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
if(getenv("HOME") && *(getenv("HOME")))
if(!(str=getenv("XDG_CONFIG_DIRS")) || str[0] == 0)
str = "/etc/xdg";
strncpy(buffer, str, sizeof(buffer)-1);
buffer[sizeof(buffer)-1] = 0;
/* Go through the list in reverse, since "the order of base directories
* denotes their importance; the first directory listed is the most
* important". Ergo, we need to load the settings from the later dirs
* first so that the settings in the earlier dirs override them.
*/
while(1)
{
snprintf(buffer, sizeof(buffer), "%s/.alsoftrc", getenv("HOME"));
f = fopen(buffer, "r");
char *next = strrchr(buffer, ':');
if(next) *(next++) = 0;
else next = buffer;
if(next[0] != '/')
WARN("Ignoring XDG config dir: %s\n", next);
else
{
size_t len = strlen(next);
strncpy(next+len, "/alsoft.conf", buffer+sizeof(buffer)-next-len);
buffer[sizeof(buffer)-1] = 0;
TRACE("Loading config %s...\n", next);
f = al_fopen(next, "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
}
if(next == buffer)
break;
}
if((str=getenv("HOME")) != NULL && *str)
{
snprintf(buffer, sizeof(buffer), "%s/.alsoftrc", str);
TRACE("Loading config %s...\n", buffer);
f = al_fopen(buffer, "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
}
#endif
if(getenv("ALSOFT_CONF"))
if((str=getenv("XDG_CONFIG_HOME")) != NULL && str[0] != 0)
snprintf(buffer, sizeof(buffer), "%s/%s", str, "alsoft.conf");
else
{
f = fopen(getenv("ALSOFT_CONF"), "r");
buffer[0] = 0;
if((str=getenv("HOME")) != NULL && str[0] != 0)
snprintf(buffer, sizeof(buffer), "%s/.config/%s", str, "alsoft.conf");
}
if(buffer[0] != 0)
{
TRACE("Loading config %s...\n", buffer);
f = al_fopen(buffer, "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
}
if((str=getenv("ALSOFT_CONF")) != NULL && *str)
{
TRACE("Loading config %s...\n", str);
f = al_fopen(str, "r");
if(f)
{
LoadConfigFromFile(f);
@@ -248,73 +414,98 @@ void ReadALConfig(void)
}
}
}
#endif
void FreeALConfig(void)
{
size_t i;
unsigned int i;
for(i = 0;i < cfgCount;i++)
for(i = 0;i < cfgBlock.entryCount;i++)
{
size_t j;
for(j = 0;j < cfgBlocks[i].entryCount;j++)
{
free(cfgBlocks[i].entries[j].key);
free(cfgBlocks[i].entries[j].value);
}
free(cfgBlocks[i].entries);
free(cfgBlocks[i].name);
free(cfgBlock.entries[i].key);
free(cfgBlock.entries[i].value);
}
free(cfgBlocks);
cfgBlocks = NULL;
cfgCount = 0;
free(cfgBlock.entries);
}
const char *GetConfigValue(const char *blockName, const char *keyName, const char *def)
{
size_t i, j;
unsigned int i;
char key[256];
if(keyName)
if(!keyName)
return def;
if(blockName && strcasecmp(blockName, "general") != 0)
snprintf(key, sizeof(key), "%s/%s", blockName, keyName);
else
{
if(!blockName)
blockName = "general";
strncpy(key, keyName, sizeof(key)-1);
key[sizeof(key)-1] = 0;
}
for(i = 0;i < cfgCount;i++)
for(i = 0;i < cfgBlock.entryCount;i++)
{
if(strcasecmp(cfgBlock.entries[i].key, key) == 0)
{
if(strcasecmp(cfgBlocks[i].name, blockName) != 0)
continue;
for(j = 0;j < cfgBlocks[i].entryCount;j++)
{
if(strcasecmp(cfgBlocks[i].entries[j].key, keyName) == 0)
return cfgBlocks[i].entries[j].value;
}
TRACE("Found %s = \"%s\"\n", key, cfgBlock.entries[i].value);
if(cfgBlock.entries[i].value[0])
return cfgBlock.entries[i].value;
return def;
}
}
TRACE("Key %s not found\n", key);
return def;
}
int GetConfigValueInt(const char *blockName, const char *keyName, int def)
int ConfigValueExists(const char *blockName, const char *keyName)
{
const char *val = GetConfigValue(blockName, keyName, "");
if(!val[0]) return def;
return strtol(val, NULL, 0);
return !!val[0];
}
float GetConfigValueFloat(const char *blockName, const char *keyName, float def)
int ConfigValueStr(const char *blockName, const char *keyName, const char **ret)
{
const char *val = GetConfigValue(blockName, keyName, "");
if(!val[0]) return 0;
*ret = val;
return 1;
}
int ConfigValueInt(const char *blockName, const char *keyName, int *ret)
{
const char *val = GetConfigValue(blockName, keyName, "");
if(!val[0]) return 0;
*ret = strtol(val, NULL, 0);
return 1;
}
int ConfigValueUInt(const char *blockName, const char *keyName, unsigned int *ret)
{
const char *val = GetConfigValue(blockName, keyName, "");
if(!val[0]) return 0;
*ret = strtoul(val, NULL, 0);
return 1;
}
int ConfigValueFloat(const char *blockName, const char *keyName, float *ret)
{
const char *val = GetConfigValue(blockName, keyName, "");
if(!val[0]) return 0;
if(!val[0]) return def;
#ifdef HAVE_STRTOF
return strtof(val, NULL);
*ret = strtof(val, NULL);
#else
return (float)strtod(val, NULL);
*ret = (float)strtod(val, NULL);
#endif
return 1;
}
int GetConfigValueBool(const char *blockName, const char *keyName, float def)
int GetConfigValueBool(const char *blockName, const char *keyName, int def)
{
const char *val = GetConfigValue(blockName, keyName, "");
-212
View File
@@ -1,212 +0,0 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2009 by Chris Robinson.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <math.h>
#include <stdlib.h>
#include "alMain.h"
#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
// Just a soft maximum. Being higher will cause EchoUpdate to reallocate the
// sample buffer which may cause an abort if realloc fails
#define MAX_ECHO_FREQ 192000
typedef struct ALechoState {
// Must be first in all effects!
ALeffectState state;
ALfloat *SampleBuffer;
ALuint BufferLength;
// The echo is two tap. The delay is the number of samples from before the
// current offset
struct {
ALuint delay;
} Tap[2];
ALuint Offset;
// The LR gains for the first tap. The second tap uses the reverse
ALfloat GainL;
ALfloat GainR;
ALfloat FeedGain;
FILTER iirFilter;
ALfloat history[2];
} ALechoState;
// Find the next power of 2. Actually, this will return the input value if
// it is already a power of 2.
static ALuint NextPowerOf2(ALuint value)
{
ALuint powerOf2 = 1;
if(value)
{
value--;
while(value)
{
value >>= 1;
powerOf2 <<= 1;
}
}
return powerOf2;
}
ALvoid EchoDestroy(ALeffectState *effect)
{
ALechoState *state = (ALechoState*)effect;
if(state)
{
free(state->SampleBuffer);
state->SampleBuffer = NULL;
free(state);
}
}
ALboolean EchoDeviceUpdate(ALeffectState *effect, ALCdevice *Device)
{
ALechoState *state = (ALechoState*)effect;
ALuint maxlen, i;
// Use the next power of 2 for the buffer length, so the tap offsets can be
// wrapped using a mask instead of a modulo
maxlen = (ALuint)(AL_ECHO_MAX_DELAY * Device->Frequency);
maxlen += (ALuint)(AL_ECHO_MAX_LRDELAY * Device->Frequency);
maxlen = NextPowerOf2(maxlen+1);
if(maxlen != state->BufferLength)
{
void *temp;
temp = realloc(state->SampleBuffer, maxlen * sizeof(ALfloat));
if(!temp)
{
alSetError(AL_OUT_OF_MEMORY);
return AL_FALSE;
}
state->BufferLength = maxlen;
}
for(i = 0;i < state->BufferLength;i++)
state->SampleBuffer[i] = 0.0f;
return AL_TRUE;
}
ALvoid EchoUpdate(ALeffectState *effect, ALCcontext *Context, const ALeffect *Effect)
{
ALechoState *state = (ALechoState*)effect;
ALuint frequency = Context->Device->Frequency;
ALfloat lrpan, cw, a, g;
state->Tap[0].delay = (ALuint)(Effect->Echo.Delay * frequency);
state->Tap[1].delay = (ALuint)(Effect->Echo.LRDelay * frequency);
state->Tap[1].delay += state->Tap[0].delay;
lrpan = Effect->Echo.Spread*0.5f + 0.5f;
state->GainL = aluSqrt( lrpan);
state->GainR = aluSqrt(1.0f-lrpan);
state->FeedGain = Effect->Echo.Feedback;
cw = cos(2.0*M_PI * LOWPASSFREQCUTOFF / frequency);
g = 1.0f - Effect->Echo.Damping;
a = 0.0f;
if(g < 0.9999f) // 1-epsilon
a = (1 - g*cw - aluSqrt(2*g*(1-cw) - g*g*(1 - cw*cw))) / (1 - g);
state->iirFilter.coeff = a;
}
ALvoid EchoProcess(ALeffectState *effect, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[OUTPUTCHANNELS])
{
ALechoState *state = (ALechoState*)effect;
const ALuint mask = state->BufferLength-1;
const ALuint tap1 = state->Tap[0].delay;
const ALuint tap2 = state->Tap[1].delay;
ALuint offset = state->Offset;
const ALfloat gain = Slot->Gain;
ALfloat samp[2], smp;
ALuint i;
for(i = 0;i < SamplesToDo;i++,offset++)
{
// Sample first tap
smp = state->SampleBuffer[(offset-tap1) & mask];
samp[0] = smp * state->GainL;
samp[1] = smp * state->GainR;
// Sample second tap. Reverse LR panning
smp = state->SampleBuffer[(offset-tap2) & mask];
samp[0] += smp * state->GainR;
samp[1] += smp * state->GainL;
// Apply damping and feedback gain to the second tap, and mix in the
// new sample
smp = lpFilter2P(&state->iirFilter, 0, smp+SamplesIn[i]);
state->SampleBuffer[offset&mask] = smp * state->FeedGain;
// Apply slot gain
samp[0] *= gain;
samp[1] *= gain;
SamplesOut[i][FRONT_LEFT] += samp[0];
SamplesOut[i][FRONT_RIGHT] += samp[1];
SamplesOut[i][SIDE_LEFT] += samp[0];
SamplesOut[i][SIDE_RIGHT] += samp[1];
SamplesOut[i][BACK_LEFT] += samp[0];
SamplesOut[i][BACK_RIGHT] += samp[1];
}
state->Offset = offset;
}
ALeffectState *EchoCreate(void)
{
ALechoState *state;
state = malloc(sizeof(*state));
if(!state)
{
alSetError(AL_OUT_OF_MEMORY);
return NULL;
}
state->state.Destroy = EchoDestroy;
state->state.DeviceUpdate = EchoDeviceUpdate;
state->state.Update = EchoUpdate;
state->state.Process = EchoProcess;
state->BufferLength = 0;
state->SampleBuffer = NULL;
state->Tap[0].delay = 0;
state->Tap[1].delay = 0;
state->Offset = 0;
state->GainL = 0.0f;
state->GainR = 0.0f;
state->iirFilter.coeff = 0.0f;
state->iirFilter.history[0] = 0.0f;
state->iirFilter.history[1] = 0.0f;
return &state->state;
}
-843
View File
@@ -1,843 +0,0 @@
/**
* Reverb for the OpenAL cross platform audio library
* Copyright (C) 2008-2009 by Christopher Fitzgerald.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <math.h>
#include <stdlib.h>
#include "AL/al.h"
#include "AL/alc.h"
#include "alMain.h"
#include "alAuxEffectSlot.h"
#include "alEffect.h"
#include "alError.h"
#include "alu.h"
typedef struct DelayLine
{
// The delay lines use sample lengths that are powers of 2 to allow
// bitmasking instead of modulus wrapping.
ALuint Mask;
ALfloat *Line;
} DelayLine;
typedef struct ALverbState {
// Must be first in all effects!
ALeffectState state;
// All delay lines are allocated as a single buffer to reduce memory
// fragmentation and management code.
ALfloat *SampleBuffer;
ALuint TotalLength;
// Master effect low-pass filter (2 chained 1-pole filters).
FILTER LpFilter;
ALfloat LpHistory[2];
// Initial effect delay and decorrelation.
DelayLine Delay;
// The tap points for the initial delay. First tap goes to early
// reflections, the last four decorrelate to late reverb.
ALuint Tap[5];
struct {
// Total gain for early reflections.
ALfloat Gain;
// Early reflections are done with 4 delay lines.
ALfloat Coeff[4];
DelayLine Delay[4];
ALuint Offset[4];
// The gain for each output channel based on 3D panning.
ALfloat PanGain[OUTPUTCHANNELS];
} Early;
struct {
// Total gain for late reverb.
ALfloat Gain;
// Attenuation to compensate for modal density and decay rate.
ALfloat DensityGain;
// The feed-back and feed-forward all-pass coefficient.
ALfloat ApFeedCoeff;
// Mixing matrix coefficient.
ALfloat MixCoeff;
// Late reverb has 4 parallel all-pass filters.
ALfloat ApCoeff[4];
DelayLine ApDelay[4];
ALuint ApOffset[4];
// In addition to 4 cyclical delay lines.
ALfloat Coeff[4];
DelayLine Delay[4];
ALuint Offset[4];
// The cyclical delay lines are 1-pole low-pass filtered.
ALfloat LpCoeff[4];
ALfloat LpSample[4];
// The gain for each output channel based on 3D panning.
ALfloat PanGain[OUTPUTCHANNELS];
} Late;
// The current read offset for all delay lines.
ALuint Offset;
} ALverbState;
// All delay line lengths are specified in seconds.
// The lengths of the early delay lines.
static const ALfloat EARLY_LINE_LENGTH[4] =
{
0.0015f, 0.0045f, 0.0135f, 0.0405f
};
// The lengths of the late all-pass delay lines.
static const ALfloat ALLPASS_LINE_LENGTH[4] =
{
0.0151f, 0.0167f, 0.0183f, 0.0200f,
};
// The lengths of the late cyclical delay lines.
static const ALfloat LATE_LINE_LENGTH[4] =
{
0.0211f, 0.0311f, 0.0461f, 0.0680f
};
// The late cyclical delay lines have a variable length dependent on the
// effect's density parameter (inverted for some reason) and this multiplier.
static const ALfloat LATE_LINE_MULTIPLIER = 4.0f;
// Input into the late reverb is decorrelated between four channels. Their
// timings are dependent on a fraction and multiplier. See VerbUpdate() for
// the calculations involved.
static const ALfloat DECO_FRACTION = 1.0f / 32.0f;
static const ALfloat DECO_MULTIPLIER = 2.0f;
// The maximum length of initial delay for the master delay line (a sum of
// the maximum early reflection and late reverb delays).
static const ALfloat MASTER_LINE_LENGTH = 0.3f + 0.1f;
// Find the next power of 2. Actually, this will return the input value if
// it is already a power of 2.
static ALuint NextPowerOf2(ALuint value)
{
ALuint powerOf2 = 1;
if(value)
{
value--;
while(value)
{
value >>= 1;
powerOf2 <<= 1;
}
}
return powerOf2;
}
static ALuint CalcLengths(ALuint length[13], ALuint frequency)
{
ALuint samples, totalLength, index;
// All line lengths are powers of 2, calculated from their lengths, with
// an additional sample in case of rounding errors.
// See VerbUpdate() for an explanation of the additional calculation
// added to the master line length.
samples = (ALuint)
((MASTER_LINE_LENGTH +
(LATE_LINE_LENGTH[0] * (1.0f + LATE_LINE_MULTIPLIER) *
(DECO_FRACTION * ((DECO_MULTIPLIER * DECO_MULTIPLIER *
DECO_MULTIPLIER) - 1.0f)))) *
frequency) + 1;
length[0] = NextPowerOf2(samples);
totalLength = length[0];
for(index = 0;index < 4;index++)
{
samples = (ALuint)(EARLY_LINE_LENGTH[index] * frequency) + 1;
length[1 + index] = NextPowerOf2(samples);
totalLength += length[1 + index];
}
for(index = 0;index < 4;index++)
{
samples = (ALuint)(ALLPASS_LINE_LENGTH[index] * frequency) + 1;
length[5 + index] = NextPowerOf2(samples);
totalLength += length[5 + index];
}
for(index = 0;index < 4;index++)
{
samples = (ALuint)(LATE_LINE_LENGTH[index] *
(1.0f + LATE_LINE_MULTIPLIER) * frequency) + 1;
length[9 + index] = NextPowerOf2(samples);
totalLength += length[9 + index];
}
return totalLength;
}
// Basic delay line input/output routines.
static __inline ALfloat DelayLineOut(DelayLine *Delay, ALuint offset)
{
return Delay->Line[offset&Delay->Mask];
}
static __inline ALvoid DelayLineIn(DelayLine *Delay, ALuint offset, ALfloat in)
{
Delay->Line[offset&Delay->Mask] = in;
}
// Delay line output routine for early reflections.
static __inline ALfloat EarlyDelayLineOut(ALverbState *State, ALuint index)
{
return State->Early.Coeff[index] *
DelayLineOut(&State->Early.Delay[index],
State->Offset - State->Early.Offset[index]);
}
// Given an input sample, this function produces stereo output for early
// reflections.
static __inline ALvoid EarlyReflection(ALverbState *State, ALfloat in, ALfloat *out)
{
ALfloat d[4], v, f[4];
// Obtain the decayed results of each early delay line.
d[0] = EarlyDelayLineOut(State, 0);
d[1] = EarlyDelayLineOut(State, 1);
d[2] = EarlyDelayLineOut(State, 2);
d[3] = EarlyDelayLineOut(State, 3);
/* The following uses a lossless scattering junction from waveguide
* theory. It actually amounts to a householder mixing matrix, which
* will produce a maximally diffuse response, and means this can probably
* be considered a simple feedback delay network (FDN).
* N
* ---
* \
* v = 2/N / d_i
* ---
* i=1
*/
v = (d[0] + d[1] + d[2] + d[3]) * 0.5f;
// The junction is loaded with the input here.
v += in;
// Calculate the feed values for the delay lines.
f[0] = v - d[0];
f[1] = v - d[1];
f[2] = v - d[2];
f[3] = v - d[3];
// Refeed the delay lines.
DelayLineIn(&State->Early.Delay[0], State->Offset, f[0]);
DelayLineIn(&State->Early.Delay[1], State->Offset, f[1]);
DelayLineIn(&State->Early.Delay[2], State->Offset, f[2]);
DelayLineIn(&State->Early.Delay[3], State->Offset, f[3]);
// Output the results of the junction for all four lines.
out[0] = State->Early.Gain * f[0];
out[1] = State->Early.Gain * f[1];
out[2] = State->Early.Gain * f[2];
out[3] = State->Early.Gain * f[3];
}
// All-pass input/output routine for late reverb.
static __inline ALfloat LateAllPassInOut(ALverbState *State, ALuint index, ALfloat in)
{
ALfloat out;
out = State->Late.ApCoeff[index] *
DelayLineOut(&State->Late.ApDelay[index],
State->Offset - State->Late.ApOffset[index]);
out -= (State->Late.ApFeedCoeff * in);
DelayLineIn(&State->Late.ApDelay[index], State->Offset,
(State->Late.ApFeedCoeff * out) + in);
return out;
}
// Delay line output routine for late reverb.
static __inline ALfloat LateDelayLineOut(ALverbState *State, ALuint index)
{
return State->Late.Coeff[index] *
DelayLineOut(&State->Late.Delay[index],
State->Offset - State->Late.Offset[index]);
}
// Low-pass filter input/output routine for late reverb.
static __inline ALfloat LateLowPassInOut(ALverbState *State, ALuint index, ALfloat in)
{
State->Late.LpSample[index] = in +
((State->Late.LpSample[index] - in) * State->Late.LpCoeff[index]);
return State->Late.LpSample[index];
}
// Given four decorrelated input samples, this function produces stereo
// output for late reverb.
static __inline ALvoid LateReverb(ALverbState *State, ALfloat *in, ALfloat *out)
{
ALfloat d[4], f[4];
// Obtain the decayed results of the cyclical delay lines, and add the
// corresponding input channels attenuated by density. Then pass the
// results through the low-pass filters.
d[0] = LateLowPassInOut(State, 0, (State->Late.DensityGain * in[0]) +
LateDelayLineOut(State, 0));
d[1] = LateLowPassInOut(State, 1, (State->Late.DensityGain * in[1]) +
LateDelayLineOut(State, 1));
d[2] = LateLowPassInOut(State, 2, (State->Late.DensityGain * in[2]) +
LateDelayLineOut(State, 2));
d[3] = LateLowPassInOut(State, 3, (State->Late.DensityGain * in[3]) +
LateDelayLineOut(State, 3));
// To help increase diffusion, run each line through an all-pass filter.
// The order of the all-pass filters is selected so that the shortest
// all-pass filter will feed the shortest delay line.
d[0] = LateAllPassInOut(State, 1, d[0]);
d[1] = LateAllPassInOut(State, 3, d[1]);
d[2] = LateAllPassInOut(State, 0, d[2]);
d[3] = LateAllPassInOut(State, 2, d[3]);
/* Late reverb is done with a modified feedback delay network (FDN)
* topology. Four input lines are each fed through their own all-pass
* filter and then into the mixing matrix. The four outputs of the
* mixing matrix are then cycled back to the inputs. Each output feeds
* a different input to form a circlular feed cycle.
*
* The mixing matrix used is a 4D skew-symmetric rotation matrix derived
* using a single unitary rotational parameter:
*
* [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2
* [ -a, d, c, -b ]
* [ -b, -c, d, a ]
* [ -c, b, -a, d ]
*
* The rotation is constructed from the effect's diffusion parameter,
* yielding: 1 = x^2 + 3 y^2; where a, b, and c are the coefficient y
* with differing signs, and d is the coefficient x. The matrix is thus:
*
* [ x, y, -y, y ] x = 1 - (0.5 diffusion^3)
* [ -y, x, y, y ] y = sqrt((1 - x^2) / 3)
* [ y, -y, x, y ]
* [ -y, -y, -y, x ]
*
* To reduce the number of multiplies, the x coefficient is applied with
* the cyclical delay line coefficients. Thus only the y coefficient is
* applied when mixing, and is modified to be: y / x.
*/
f[0] = d[0] + (State->Late.MixCoeff * ( d[1] - d[2] + d[3]));
f[1] = d[1] + (State->Late.MixCoeff * (-d[0] + d[2] + d[3]));
f[2] = d[2] + (State->Late.MixCoeff * ( d[0] - d[1] + d[3]));
f[3] = d[3] + (State->Late.MixCoeff * (-d[0] - d[1] - d[2]));
// Output the results of the matrix for all four cyclical delay lines,
// attenuated by the late reverb gain (which is attenuated by the 'x'
// mix coefficient).
out[0] = State->Late.Gain * f[0];
out[1] = State->Late.Gain * f[1];
out[2] = State->Late.Gain * f[2];
out[3] = State->Late.Gain * f[3];
// The delay lines are fed circularly in the order:
// 0 -> 1 -> 3 -> 2 -> 0 ...
DelayLineIn(&State->Late.Delay[0], State->Offset, f[2]);
DelayLineIn(&State->Late.Delay[1], State->Offset, f[0]);
DelayLineIn(&State->Late.Delay[2], State->Offset, f[3]);
DelayLineIn(&State->Late.Delay[3], State->Offset, f[1]);
}
// Process the reverb for a given input sample, resulting in separate four-
// channel output for both early reflections and late reverb.
static __inline ALvoid ReverbInOut(ALverbState *State, ALfloat in, ALfloat *early, ALfloat *late)
{
ALfloat taps[4];
// Low-pass filter the incoming sample.
in = lpFilter2P(&State->LpFilter, 0, in);
// Feed the initial delay line.
DelayLineIn(&State->Delay, State->Offset, in);
// Calculate the early reflection from the first delay tap.
in = DelayLineOut(&State->Delay, State->Offset - State->Tap[0]);
EarlyReflection(State, in, early);
// Calculate the late reverb from the last four delay taps.
taps[0] = DelayLineOut(&State->Delay, State->Offset - State->Tap[1]);
taps[1] = DelayLineOut(&State->Delay, State->Offset - State->Tap[2]);
taps[2] = DelayLineOut(&State->Delay, State->Offset - State->Tap[3]);
taps[3] = DelayLineOut(&State->Delay, State->Offset - State->Tap[4]);
LateReverb(State, taps, late);
// Step all delays forward one sample.
State->Offset++;
}
// This destroys the reverb state. It should be called only when the effect
// slot has a different (or no) effect loaded over the reverb effect.
ALvoid VerbDestroy(ALeffectState *effect)
{
ALverbState *State = (ALverbState*)effect;
if(State)
{
free(State->SampleBuffer);
State->SampleBuffer = NULL;
free(State);
}
}
// NOTE: Temp, remove later.
static __inline ALint aluCart2LUTpos(ALfloat re, ALfloat im)
{
ALint pos = 0;
ALfloat denom = aluFabs(re) + aluFabs(im);
if(denom > 0.0f)
pos = (ALint)(QUADRANT_NUM*aluFabs(im) / denom + 0.5);
if(re < 0.0)
pos = 2 * QUADRANT_NUM - pos;
if(im < 0.0)
pos = LUT_NUM - pos;
return pos%LUT_NUM;
}
// This updates the device-dependant reverb state. This is called on
// initialization and any time the device parameters (eg. playback frequency,
// format) have been changed.
ALboolean VerbDeviceUpdate(ALeffectState *effect, ALCdevice *Device)
{
ALverbState *State = (ALverbState*)effect;
ALuint length[13], totalLength;
ALuint index;
totalLength = CalcLengths(length, Device->Frequency);
if(totalLength != State->TotalLength)
{
void *temp;
temp = realloc(State->SampleBuffer, totalLength * sizeof(ALfloat));
if(!temp)
{
alSetError(AL_OUT_OF_MEMORY);
return AL_FALSE;
}
State->TotalLength = totalLength;
State->SampleBuffer = temp;
// All lines share a single sample buffer
State->Delay.Mask = length[0] - 1;
State->Delay.Line = &State->SampleBuffer[0];
totalLength = length[0];
for(index = 0;index < 4;index++)
{
State->Early.Delay[index].Mask = length[1 + index] - 1;
State->Early.Delay[index].Line = &State->SampleBuffer[totalLength];
totalLength += length[1 + index];
}
for(index = 0;index < 4;index++)
{
State->Late.ApDelay[index].Mask = length[5 + index] - 1;
State->Late.ApDelay[index].Line = &State->SampleBuffer[totalLength];
totalLength += length[5 + index];
}
for(index = 0;index < 4;index++)
{
State->Late.Delay[index].Mask = length[9 + index] - 1;
State->Late.Delay[index].Line = &State->SampleBuffer[totalLength];
totalLength += length[9 + index];
}
}
for(index = 0;index < 4;index++)
{
State->Early.Offset[index] = (ALuint)(EARLY_LINE_LENGTH[index] *
Device->Frequency);
State->Late.ApOffset[index] = (ALuint)(ALLPASS_LINE_LENGTH[index] *
Device->Frequency);
}
for(index = 0;index < State->TotalLength;index++)
State->SampleBuffer[index] = 0.0f;
return AL_TRUE;
}
// This updates the reverb state. This is called any time the reverb effect
// is loaded into a slot.
ALvoid VerbUpdate(ALeffectState *effect, ALCcontext *Context, const ALeffect *Effect)
{
ALverbState *State = (ALverbState*)effect;
ALuint frequency = Context->Device->Frequency;
ALuint index;
ALfloat length, mixCoeff, cw, g, coeff;
ALfloat hfRatio = Effect->Reverb.DecayHFRatio;
// Calculate the master low-pass filter (from the master effect HF gain).
cw = cos(2.0*M_PI * Effect->Reverb.HFReference / frequency);
g = __max(Effect->Reverb.GainHF, 0.0001f);
State->LpFilter.coeff = 0.0f;
if(g < 0.9999f) // 1-epsilon
State->LpFilter.coeff = (1 - g*cw - aluSqrt(2*g*(1-cw) - g*g*(1 - cw*cw))) / (1 - g);
// Calculate the initial delay taps.
length = Effect->Reverb.ReflectionsDelay;
State->Tap[0] = (ALuint)(length * frequency);
length += Effect->Reverb.LateReverbDelay;
/* The four inputs to the late reverb are decorrelated to smooth the
* initial reverb and reduce harsh echos. The timings are calculated as
* multiples of a fraction of the smallest cyclical delay time. This
* result is then adjusted so that the first tap occurs immediately (all
* taps are reduced by the shortest fraction).
*
* offset[index] = ((FRACTION MULTIPLIER^index) - 1) delay
*/
for(index = 0;index < 4;index++)
{
length += LATE_LINE_LENGTH[0] *
(1.0f + (Effect->Reverb.Density * LATE_LINE_MULTIPLIER)) *
(DECO_FRACTION * (pow(DECO_MULTIPLIER, (ALfloat)index) - 1.0f));
State->Tap[1 + index] = (ALuint)(length * frequency);
}
// Calculate the early reflections gain (from the master effect gain, and
// reflections gain parameters).
State->Early.Gain = Effect->Reverb.Gain * Effect->Reverb.ReflectionsGain;
// Calculate the gain (coefficient) for each early delay line.
for(index = 0;index < 4;index++)
State->Early.Coeff[index] = pow(10.0f, EARLY_LINE_LENGTH[index] /
Effect->Reverb.LateReverbDelay *
-60.0f / 20.0f);
// Calculate the first mixing matrix coefficient (x).
mixCoeff = 1.0f - (0.5f * pow(Effect->Reverb.Diffusion, 3.0f));
// Calculate the late reverb gain (from the master effect gain, and late
// reverb gain parameters). Since the output is tapped prior to the
// application of the delay line coefficients, this gain needs to be
// attenuated by the 'x' mix coefficient from above.
State->Late.Gain = Effect->Reverb.Gain * Effect->Reverb.LateReverbGain * mixCoeff;
/* To compensate for changes in modal density and decay time of the late
* reverb signal, the input is attenuated based on the maximal energy of
* the outgoing signal. This is calculated as the ratio between a
* reference value and the current approximation of energy for the output
* signal.
*
* Reverb output matches exponential decay of the form Sum(a^n), where a
* is the attenuation coefficient, and n is the sample ranging from 0 to
* infinity. The signal energy can thus be approximated using the area
* under this curve, calculated as: 1 / (1 - a).
*
* The reference energy is calculated from a signal at the lowest (effect
* at 1.0) density with a decay time of one second.
*
* The coefficient is calculated as the average length of the cyclical
* delay lines. This produces a better result than calculating the gain
* for each line individually (most likely a side effect of diffusion).
*
* The final result is the square root of the ratio bound to a maximum
* value of 1 (no amplification).
*/
length = (LATE_LINE_LENGTH[0] + LATE_LINE_LENGTH[1] +
LATE_LINE_LENGTH[2] + LATE_LINE_LENGTH[3]);
g = length * (1.0f + LATE_LINE_MULTIPLIER) * 0.25f;
g = pow(10.0f, g * -60.0f / 20.0f);
g = 1.0f / (1.0f - (g * g));
length *= 1.0f + (Effect->Reverb.Density * LATE_LINE_MULTIPLIER) * 0.25f;
length = pow(10.0f, length / Effect->Reverb.DecayTime * -60.0f / 20.0f);
length = 1.0f / (1.0f - (length * length));
State->Late.DensityGain = __min(aluSqrt(g / length), 1.0f);
// Calculate the all-pass feed-back and feed-forward coefficient.
State->Late.ApFeedCoeff = 0.6f * pow(Effect->Reverb.Diffusion, 3.0f);
// Calculate the mixing matrix coefficient (y / x).
g = aluSqrt((1.0f - (mixCoeff * mixCoeff)) / 3.0f);
State->Late.MixCoeff = g / mixCoeff;
for(index = 0;index < 4;index++)
{
// Calculate the gain (coefficient) for each all-pass line.
State->Late.ApCoeff[index] = pow(10.0f, ALLPASS_LINE_LENGTH[index] /
Effect->Reverb.DecayTime *
-60.0f / 20.0f);
}
// If the HF limit parameter is flagged, calculate an appropriate limit
// based on the air absorption parameter.
if(Effect->Reverb.DecayHFLimit && Effect->Reverb.AirAbsorptionGainHF < 1.0f)
{
ALfloat limitRatio;
// For each of the cyclical delays, find the attenuation due to air
// absorption in dB (converting delay time to meters using the speed
// of sound). Then reversing the decay equation, solve for HF ratio.
// The delay length is cancelled out of the equation, so it can be
// calculated once for all lines.
limitRatio = 1.0f / (log10(Effect->Reverb.AirAbsorptionGainHF) *
SPEEDOFSOUNDMETRESPERSEC *
Effect->Reverb.DecayTime / -60.0f * 20.0f);
// Need to limit the result to a minimum of 0.1, just like the HF
// ratio parameter.
limitRatio = __max(limitRatio, 0.1f);
// Using the limit calculated above, apply the upper bound to the
// HF ratio.
hfRatio = __min(hfRatio, limitRatio);
}
// Calculate the low-pass filter frequency.
cw = cos(2.0*M_PI * Effect->Reverb.HFReference / frequency);
for(index = 0;index < 4;index++)
{
// Calculate the length (in seconds) of each cyclical delay line.
length = LATE_LINE_LENGTH[index] * (1.0f + (Effect->Reverb.Density *
LATE_LINE_MULTIPLIER));
// Calculate the delay offset for the cyclical delay lines.
State->Late.Offset[index] = (ALuint)(length * frequency);
// Calculate the gain (coefficient) for each cyclical line.
State->Late.Coeff[index] = pow(10.0f, length / Effect->Reverb.DecayTime *
-60.0f / 20.0f);
// Eventually this should boost the high frequencies when the ratio
// exceeds 1.
coeff = 0.0f;
if (hfRatio < 1.0f)
{
// Calculate the decay equation for each low-pass filter.
g = pow(10.0f, length / (Effect->Reverb.DecayTime * hfRatio) *
-60.0f / 20.0f) / State->Late.Coeff[index];
g = __max(g, 0.1f);
g *= g;
// Calculate the gain (coefficient) for each low-pass filter.
if(g < 0.9999f) // 1-epsilon
coeff = (1 - g*cw - aluSqrt(2*g*(1-cw) - g*g*(1 - cw*cw))) / (1 - g);
// Very low decay times will produce minimal output, so apply an
// upper bound to the coefficient.
coeff = __min(coeff, 0.98f);
}
State->Late.LpCoeff[index] = coeff;
// Attenuate the cyclical line coefficients by the mixing coefficient
// (x).
State->Late.Coeff[index] *= mixCoeff;
}
// Calculate the 3D-panning gains for the early reflections and late
// reverb (for EAX mode).
{
ALfloat earlyPan[3] = { Effect->Reverb.ReflectionsPan[0], Effect->Reverb.ReflectionsPan[1], Effect->Reverb.ReflectionsPan[2] };
ALfloat latePan[3] = { Effect->Reverb.LateReverbPan[0], Effect->Reverb.LateReverbPan[1], Effect->Reverb.LateReverbPan[2] };
ALfloat *speakerGain, dirGain, ambientGain;
ALfloat length;
ALint pos;
length = earlyPan[0]*earlyPan[0] + earlyPan[1]*earlyPan[1] + earlyPan[2]*earlyPan[2];
if(length > 1.0f)
{
length = 1.0f / aluSqrt(length);
earlyPan[0] *= length;
earlyPan[1] *= length;
earlyPan[2] *= length;
}
length = latePan[0]*latePan[0] + latePan[1]*latePan[1] + latePan[2]*latePan[2];
if(length > 1.0f)
{
length = 1.0f / aluSqrt(length);
latePan[0] *= length;
latePan[1] *= length;
latePan[2] *= length;
}
// This code applies directional reverb just like the mixer applies
// directional sources. It diffuses the sound toward all speakers
// as the magnitude of the panning vector drops, which is only an
// approximation of the expansion of sound across the speakers from
// the panning direction.
pos = aluCart2LUTpos(earlyPan[2], earlyPan[0]);
speakerGain = &Context->PanningLUT[OUTPUTCHANNELS * pos];
dirGain = aluSqrt((earlyPan[0] * earlyPan[0]) + (earlyPan[2] * earlyPan[2]));
ambientGain = (1.0 - dirGain);
for(index = 0;index < OUTPUTCHANNELS;index++)
State->Early.PanGain[index] = dirGain * speakerGain[index] + ambientGain;
pos = aluCart2LUTpos(latePan[2], latePan[0]);
speakerGain = &Context->PanningLUT[OUTPUTCHANNELS * pos];
dirGain = aluSqrt((latePan[0] * latePan[0]) + (latePan[2] * latePan[2]));
ambientGain = (1.0 - dirGain);
for(index = 0;index < OUTPUTCHANNELS;index++)
State->Late.PanGain[index] = dirGain * speakerGain[index] + ambientGain;
}
}
// This processes the reverb state, given the input samples and an output
// buffer.
ALvoid VerbProcess(ALeffectState *effect, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[OUTPUTCHANNELS])
{
ALverbState *State = (ALverbState*)effect;
ALuint index;
ALfloat early[4], late[4], out[4];
ALfloat gain = Slot->Gain;
for(index = 0;index < SamplesToDo;index++)
{
// Process reverb for this sample.
ReverbInOut(State, SamplesIn[index], early, late);
// Mix early reflections and late reverb.
out[0] = (early[0] + late[0]) * gain;
out[1] = (early[1] + late[1]) * gain;
out[2] = (early[2] + late[2]) * gain;
out[3] = (early[3] + late[3]) * gain;
// Output the results.
SamplesOut[index][FRONT_LEFT] += out[0];
SamplesOut[index][FRONT_RIGHT] += out[1];
SamplesOut[index][FRONT_CENTER] += out[3];
SamplesOut[index][SIDE_LEFT] += out[0];
SamplesOut[index][SIDE_RIGHT] += out[1];
SamplesOut[index][BACK_LEFT] += out[0];
SamplesOut[index][BACK_RIGHT] += out[1];
SamplesOut[index][BACK_CENTER] += out[2];
}
}
// This processes the EAX reverb state, given the input samples and an output
// buffer.
ALvoid EAXVerbProcess(ALeffectState *effect, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[OUTPUTCHANNELS])
{
ALverbState *State = (ALverbState*)effect;
ALuint index;
ALfloat early[4], late[4];
ALfloat gain = Slot->Gain;
for(index = 0;index < SamplesToDo;index++)
{
// Process reverb for this sample.
ReverbInOut(State, SamplesIn[index], early, late);
// Unfortunately, while the number and configuration of gains for
// panning adjust according to OUTPUTCHANNELS, the output from the
// reverb engine is not so scalable.
SamplesOut[index][FRONT_LEFT] +=
(State->Early.PanGain[FRONT_LEFT]*early[0] +
State->Late.PanGain[FRONT_LEFT]*late[0]) * gain;
SamplesOut[index][FRONT_RIGHT] +=
(State->Early.PanGain[FRONT_RIGHT]*early[1] +
State->Late.PanGain[FRONT_RIGHT]*late[1]) * gain;
SamplesOut[index][FRONT_CENTER] +=
(State->Early.PanGain[FRONT_CENTER]*early[3] +
State->Late.PanGain[FRONT_CENTER]*late[3]) * gain;
SamplesOut[index][SIDE_LEFT] +=
(State->Early.PanGain[SIDE_LEFT]*early[0] +
State->Late.PanGain[SIDE_LEFT]*late[0]) * gain;
SamplesOut[index][SIDE_RIGHT] +=
(State->Early.PanGain[SIDE_RIGHT]*early[1] +
State->Late.PanGain[SIDE_RIGHT]*late[1]) * gain;
SamplesOut[index][BACK_LEFT] +=
(State->Early.PanGain[BACK_LEFT]*early[0] +
State->Late.PanGain[BACK_LEFT]*late[0]) * gain;
SamplesOut[index][BACK_RIGHT] +=
(State->Early.PanGain[BACK_RIGHT]*early[1] +
State->Late.PanGain[BACK_RIGHT]*late[1]) * gain;
SamplesOut[index][BACK_CENTER] +=
(State->Early.PanGain[BACK_CENTER]*early[2] +
State->Late.PanGain[BACK_CENTER]*late[2]) * gain;
}
}
// This creates the reverb state. It should be called only when the reverb
// effect is loaded into a slot that doesn't already have a reverb effect.
ALeffectState *VerbCreate(void)
{
ALverbState *State = NULL;
ALuint index;
State = malloc(sizeof(ALverbState));
if(!State)
{
alSetError(AL_OUT_OF_MEMORY);
return NULL;
}
State->state.Destroy = VerbDestroy;
State->state.DeviceUpdate = VerbDeviceUpdate;
State->state.Update = VerbUpdate;
State->state.Process = VerbProcess;
State->TotalLength = 0;
State->SampleBuffer = NULL;
State->LpFilter.coeff = 0.0f;
State->LpFilter.history[0] = 0.0f;
State->LpFilter.history[1] = 0.0f;
State->Delay.Mask = 0;
State->Delay.Line = NULL;
State->Tap[0] = 0;
State->Tap[1] = 0;
State->Tap[2] = 0;
State->Tap[3] = 0;
State->Tap[4] = 0;
State->Early.Gain = 0.0f;
for(index = 0;index < 4;index++)
{
State->Early.Coeff[index] = 0.0f;
State->Early.Delay[index].Mask = 0;
State->Early.Delay[index].Line = NULL;
State->Early.Offset[index] = 0;
}
State->Late.Gain = 0.0f;
State->Late.DensityGain = 0.0f;
State->Late.ApFeedCoeff = 0.0f;
State->Late.MixCoeff = 0.0f;
for(index = 0;index < 4;index++)
{
State->Late.ApCoeff[index] = 0.0f;
State->Late.ApDelay[index].Mask = 0;
State->Late.ApDelay[index].Line = NULL;
State->Late.ApOffset[index] = 0;
State->Late.Coeff[index] = 0.0f;
State->Late.Delay[index].Mask = 0;
State->Late.Delay[index].Line = NULL;
State->Late.Offset[index] = 0;
State->Late.LpCoeff[index] = 0.0f;
State->Late.LpSample[index] = 0.0f;
}
// Panning is applied as an independent gain for each output channel.
for(index = 0;index < OUTPUTCHANNELS;index++)
{
State->Early.PanGain[index] = 0.0f;
State->Late.PanGain[index] = 0.0f;
}
State->Offset = 0;
return &State->state;
}
ALeffectState *EAXVerbCreate(void)
{
ALeffectState *State = VerbCreate();
if(State) State->Process = EAXVerbProcess;
return State;
}
+34 -30
View File
@@ -24,6 +24,8 @@
#include <stdlib.h>
#include "alMain.h"
#include "threads.h"
#include "compat.h"
struct RingBuffer {
@@ -34,26 +36,23 @@ struct RingBuffer {
ALint read_pos;
ALint write_pos;
CRITICAL_SECTION cs;
almtx_t mtx;
};
RingBuffer *CreateRingBuffer(ALsizei frame_size, ALsizei length)
{
RingBuffer *ring = calloc(1, sizeof(*ring));
RingBuffer *ring = calloc(1, sizeof(*ring) + ((length+1) * frame_size));
if(ring)
{
ring->mem = (ALubyte*)(ring+1);
ring->frame_size = frame_size;
ring->length = length+1;
ring->write_pos = 1;
ring->mem = malloc((length+1)*frame_size);
if(!ring->mem)
{
free(ring);
ring = NULL;
}
ring->read_pos = 0;
ring->write_pos = 0;
InitializeCriticalSection(&ring->cs);
almtx_init(&ring->mtx, almtx_plain);
}
return ring;
}
@@ -62,8 +61,7 @@ void DestroyRingBuffer(RingBuffer *ring)
{
if(ring)
{
DeleteCriticalSection(&ring->cs);
free(ring->mem);
almtx_destroy(&ring->mtx);
free(ring);
}
}
@@ -72,9 +70,9 @@ ALsizei RingBufferSize(RingBuffer *ring)
{
ALsizei s;
EnterCriticalSection(&ring->cs);
s = (ring->write_pos-ring->read_pos-1+ring->length) % ring->length;
LeaveCriticalSection(&ring->cs);
almtx_lock(&ring->mtx);
s = (ring->write_pos-ring->read_pos+ring->length) % ring->length;
almtx_unlock(&ring->mtx);
return s;
}
@@ -83,31 +81,37 @@ void WriteRingBuffer(RingBuffer *ring, const ALubyte *data, ALsizei len)
{
int remain;
EnterCriticalSection(&ring->cs);
almtx_lock(&ring->mtx);
remain = ring->length - ring->write_pos;
if((ring->read_pos-ring->write_pos+ring->length)%ring->length < len)
ring->read_pos = (ring->write_pos+len) % ring->length;
remain = (ring->read_pos-ring->write_pos-1+ring->length) % ring->length;
if(remain < len) len = remain;
if(remain < len)
if(len > 0)
{
memcpy(ring->mem+(ring->write_pos*ring->frame_size), data, remain*ring->frame_size);
memcpy(ring->mem, data+(remain*ring->frame_size), (len-remain)*ring->frame_size);
remain = ring->length - ring->write_pos;
if(remain < len)
{
memcpy(ring->mem+(ring->write_pos*ring->frame_size), data,
remain*ring->frame_size);
memcpy(ring->mem, data+(remain*ring->frame_size),
(len-remain)*ring->frame_size);
}
else
memcpy(ring->mem+(ring->write_pos*ring->frame_size), data,
len*ring->frame_size);
ring->write_pos += len;
ring->write_pos %= ring->length;
}
else
memcpy(ring->mem+(ring->write_pos*ring->frame_size), data, len*ring->frame_size);
ring->write_pos += len;
ring->write_pos %= ring->length;
LeaveCriticalSection(&ring->cs);
almtx_unlock(&ring->mtx);
}
void ReadRingBuffer(RingBuffer *ring, ALubyte *data, ALsizei len)
{
int remain;
EnterCriticalSection(&ring->cs);
almtx_lock(&ring->mtx);
remain = ring->length - ring->read_pos;
if(remain < len)
@@ -121,5 +125,5 @@ void ReadRingBuffer(RingBuffer *ring, ALubyte *data, ALsizei len)
ring->read_pos += len;
ring->read_pos %= ring->length;
LeaveCriticalSection(&ring->cs);
almtx_unlock(&ring->mtx);
}
-128
View File
@@ -1,128 +0,0 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdlib.h>
#include "alMain.h"
#include "alThunk.h"
#ifdef _WIN32
typedef struct {
ALuint (*func)(ALvoid*);
ALvoid *ptr;
HANDLE thread;
} ThreadInfo;
static DWORD CALLBACK StarterFunc(void *ptr)
{
ThreadInfo *inf = (ThreadInfo*)ptr;
ALint ret;
ret = inf->func(inf->ptr);
ExitThread((DWORD)ret);
return (DWORD)ret;
}
ALvoid *StartThread(ALuint (*func)(ALvoid*), ALvoid *ptr)
{
DWORD dummy;
ThreadInfo *inf = malloc(sizeof(ThreadInfo));
if(!inf) return 0;
inf->func = func;
inf->ptr = ptr;
inf->thread = CreateThread(NULL, 0, StarterFunc, inf, 0, &dummy);
if(!inf->thread)
{
free(inf);
return NULL;
}
return inf;
}
ALuint StopThread(ALvoid *thread)
{
ThreadInfo *inf = thread;
DWORD ret = 0;
WaitForSingleObject(inf->thread, INFINITE);
GetExitCodeThread(inf->thread, &ret);
CloseHandle(inf->thread);
free(inf);
return (ALuint)ret;
}
#else
#include <pthread.h>
typedef struct {
ALuint (*func)(ALvoid*);
ALvoid *ptr;
ALuint ret;
pthread_t thread;
} ThreadInfo;
static void *StarterFunc(void *ptr)
{
ThreadInfo *inf = (ThreadInfo*)ptr;
inf->ret = inf->func(inf->ptr);
return NULL;
}
ALvoid *StartThread(ALuint (*func)(ALvoid*), ALvoid *ptr)
{
ThreadInfo *inf = malloc(sizeof(ThreadInfo));
if(!inf) return NULL;
inf->func = func;
inf->ptr = ptr;
if(pthread_create(&inf->thread, NULL, StarterFunc, inf) != 0)
{
free(inf);
return NULL;
}
return inf;
}
ALuint StopThread(ALvoid *thread)
{
ThreadInfo *inf = thread;
ALuint ret;
pthread_join(inf->thread, NULL);
ret = inf->ret;
free(inf);
return ret;
}
#endif
-1111
View File
File diff suppressed because it is too large Load Diff
+45
View File
@@ -0,0 +1,45 @@
#ifndef ALSTRING_H
#define ALSTRING_H
#include <string.h>
#include "vector.h"
typedef char al_string_char_type;
TYPEDEF_VECTOR(al_string_char_type, al_string)
inline void al_string_deinit(al_string *str)
{ VECTOR_DEINIT(*str); }
#define AL_STRING_INIT(_x) do { (_x) = (al_string)NULL; } while(0)
#define AL_STRING_INIT_STATIC() ((al_string)NULL)
#define AL_STRING_DEINIT(_x) al_string_deinit(&(_x))
inline ALsizei al_string_length(const_al_string str)
{ return VECTOR_SIZE(str); }
inline ALboolean al_string_empty(const_al_string str)
{ return al_string_length(str) == 0; }
inline const al_string_char_type *al_string_get_cstr(const_al_string str)
{ return str ? &VECTOR_FRONT(str) : ""; }
void al_string_clear(al_string *str);
int al_string_cmp(const_al_string str1, const_al_string str2);
int al_string_cmp_cstr(const_al_string str1, const al_string_char_type *str2);
void al_string_copy(al_string *str, const_al_string from);
void al_string_copy_cstr(al_string *str, const al_string_char_type *from);
void al_string_append_char(al_string *str, const al_string_char_type c);
void al_string_append_cstr(al_string *str, const al_string_char_type *from);
void al_string_append_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to);
#ifdef _WIN32
#include <wchar.h>
/* Windows-only methods to deal with WideChar strings. */
void al_string_copy_wcstr(al_string *str, const wchar_t *from);
#endif
#endif /* ALSTRING_H */
+1387
View File
File diff suppressed because it is too large Load Diff
+232
View File
@@ -0,0 +1,232 @@
#include "config.h"
#include <stdlib.h>
#include "alMain.h"
#include "backends/base.h"
/* Base ALCbackend method implementations. */
void ALCbackend_Construct(ALCbackend *self, ALCdevice *device)
{
int ret;
self->mDevice = device;
ret = almtx_init(&self->mMutex, almtx_recursive);
assert(ret == althrd_success);
}
void ALCbackend_Destruct(ALCbackend *self)
{
almtx_destroy(&self->mMutex);
}
ALCboolean ALCbackend_reset(ALCbackend* UNUSED(self))
{
return ALC_FALSE;
}
ALCenum ALCbackend_captureSamples(ALCbackend* UNUSED(self), void* UNUSED(buffer), ALCuint UNUSED(samples))
{
return ALC_INVALID_DEVICE;
}
ALCuint ALCbackend_availableSamples(ALCbackend* UNUSED(self))
{
return 0;
}
ALint64 ALCbackend_getLatency(ALCbackend* UNUSED(self))
{
return 0;
}
void ALCbackend_lock(ALCbackend *self)
{
int ret = almtx_lock(&self->mMutex);
assert(ret == althrd_success);
}
void ALCbackend_unlock(ALCbackend *self)
{
int ret = almtx_unlock(&self->mMutex);
assert(ret == althrd_success);
}
/* Base ALCbackendFactory method implementations. */
void ALCbackendFactory_deinit(ALCbackendFactory* UNUSED(self))
{
}
/* Wrappers to use an old-style backend with the new interface. */
typedef struct PlaybackWrapper {
DERIVE_FROM_TYPE(ALCbackend);
const BackendFuncs *Funcs;
} PlaybackWrapper;
static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device, const BackendFuncs *funcs);
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, Destruct)
static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name);
static void PlaybackWrapper_close(PlaybackWrapper *self);
static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self);
static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self);
static void PlaybackWrapper_stop(PlaybackWrapper *self);
static DECLARE_FORWARD2(PlaybackWrapper, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ALCuint, availableSamples)
static ALint64 PlaybackWrapper_getLatency(PlaybackWrapper *self);
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, lock)
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(PlaybackWrapper)
DEFINE_ALCBACKEND_VTABLE(PlaybackWrapper);
static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device, const BackendFuncs *funcs)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(PlaybackWrapper, ALCbackend, self);
self->Funcs = funcs;
}
static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
return self->Funcs->OpenPlayback(device, name);
}
static void PlaybackWrapper_close(PlaybackWrapper *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
self->Funcs->ClosePlayback(device);
}
static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
return self->Funcs->ResetPlayback(device);
}
static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
return self->Funcs->StartPlayback(device);
}
static void PlaybackWrapper_stop(PlaybackWrapper *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
self->Funcs->StopPlayback(device);
}
static ALint64 PlaybackWrapper_getLatency(PlaybackWrapper *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
return self->Funcs->GetLatency(device);
}
typedef struct CaptureWrapper {
DERIVE_FROM_TYPE(ALCbackend);
const BackendFuncs *Funcs;
} CaptureWrapper;
static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device, const BackendFuncs *funcs);
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, Destruct)
static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name);
static void CaptureWrapper_close(CaptureWrapper *self);
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ALCboolean, reset)
static ALCboolean CaptureWrapper_start(CaptureWrapper *self);
static void CaptureWrapper_stop(CaptureWrapper *self);
static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples);
static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self);
static ALint64 CaptureWrapper_getLatency(CaptureWrapper *self);
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, lock)
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(CaptureWrapper)
DEFINE_ALCBACKEND_VTABLE(CaptureWrapper);
static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device, const BackendFuncs *funcs)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(CaptureWrapper, ALCbackend, self);
self->Funcs = funcs;
}
static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
return self->Funcs->OpenCapture(device, name);
}
static void CaptureWrapper_close(CaptureWrapper *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
self->Funcs->CloseCapture(device);
}
static ALCboolean CaptureWrapper_start(CaptureWrapper *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
self->Funcs->StartCapture(device);
return ALC_TRUE;
}
static void CaptureWrapper_stop(CaptureWrapper *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
self->Funcs->StopCapture(device);
}
static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
return self->Funcs->CaptureSamples(device, buffer, samples);
}
static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
return self->Funcs->AvailableSamples(device);
}
static ALint64 CaptureWrapper_getLatency(CaptureWrapper *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
return self->Funcs->GetLatency(device);
}
ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
PlaybackWrapper *backend;
backend = PlaybackWrapper_New(sizeof(*backend));
if(!backend) return NULL;
PlaybackWrapper_Construct(backend, device, funcs);
return STATIC_CAST(ALCbackend, backend);
}
if(type == ALCbackend_Capture)
{
CaptureWrapper *backend;
backend = CaptureWrapper_New(sizeof(*backend));
if(!backend) return NULL;
CaptureWrapper_Construct(backend, device, funcs);
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
+133
View File
@@ -0,0 +1,133 @@
#ifndef AL_BACKENDS_BASE_H
#define AL_BACKENDS_BASE_H
#include "alMain.h"
#include "threads.h"
struct ALCbackendVtable;
typedef struct ALCbackend {
const struct ALCbackendVtable *vtbl;
ALCdevice *mDevice;
almtx_t mMutex;
} ALCbackend;
void ALCbackend_Construct(ALCbackend *self, ALCdevice *device);
void ALCbackend_Destruct(ALCbackend *self);
ALCboolean ALCbackend_reset(ALCbackend *self);
ALCenum ALCbackend_captureSamples(ALCbackend *self, void *buffer, ALCuint samples);
ALCuint ALCbackend_availableSamples(ALCbackend *self);
ALint64 ALCbackend_getLatency(ALCbackend *self);
void ALCbackend_lock(ALCbackend *self);
void ALCbackend_unlock(ALCbackend *self);
struct ALCbackendVtable {
void (*const Destruct)(ALCbackend*);
ALCenum (*const open)(ALCbackend*, const ALCchar*);
void (*const close)(ALCbackend*);
ALCboolean (*const reset)(ALCbackend*);
ALCboolean (*const start)(ALCbackend*);
void (*const stop)(ALCbackend*);
ALCenum (*const captureSamples)(ALCbackend*, void*, ALCuint);
ALCuint (*const availableSamples)(ALCbackend*);
ALint64 (*const getLatency)(ALCbackend*);
void (*const lock)(ALCbackend*);
void (*const unlock)(ALCbackend*);
void (*const Delete)(void*);
};
#define DEFINE_ALCBACKEND_VTABLE(T) \
DECLARE_THUNK(T, ALCbackend, void, Destruct) \
DECLARE_THUNK1(T, ALCbackend, ALCenum, open, const ALCchar*) \
DECLARE_THUNK(T, ALCbackend, void, close) \
DECLARE_THUNK(T, ALCbackend, ALCboolean, reset) \
DECLARE_THUNK(T, ALCbackend, ALCboolean, start) \
DECLARE_THUNK(T, ALCbackend, void, stop) \
DECLARE_THUNK2(T, ALCbackend, ALCenum, captureSamples, void*, ALCuint) \
DECLARE_THUNK(T, ALCbackend, ALCuint, availableSamples) \
DECLARE_THUNK(T, ALCbackend, ALint64, getLatency) \
DECLARE_THUNK(T, ALCbackend, void, lock) \
DECLARE_THUNK(T, ALCbackend, void, unlock) \
static void T##_ALCbackend_Delete(void *ptr) \
{ T##_Delete(STATIC_UPCAST(T, ALCbackend, (ALCbackend*)ptr)); } \
\
static const struct ALCbackendVtable T##_ALCbackend_vtable = { \
T##_ALCbackend_Destruct, \
\
T##_ALCbackend_open, \
T##_ALCbackend_close, \
T##_ALCbackend_reset, \
T##_ALCbackend_start, \
T##_ALCbackend_stop, \
T##_ALCbackend_captureSamples, \
T##_ALCbackend_availableSamples, \
T##_ALCbackend_getLatency, \
T##_ALCbackend_lock, \
T##_ALCbackend_unlock, \
\
T##_ALCbackend_Delete, \
}
typedef enum ALCbackend_Type {
ALCbackend_Playback,
ALCbackend_Capture,
ALCbackend_Loopback
} ALCbackend_Type;
struct ALCbackendFactoryVtable;
typedef struct ALCbackendFactory {
const struct ALCbackendFactoryVtable *vtbl;
} ALCbackendFactory;
void ALCbackendFactory_deinit(ALCbackendFactory *self);
struct ALCbackendFactoryVtable {
ALCboolean (*const init)(ALCbackendFactory *self);
void (*const deinit)(ALCbackendFactory *self);
ALCboolean (*const querySupport)(ALCbackendFactory *self, ALCbackend_Type type);
void (*const probe)(ALCbackendFactory *self, enum DevProbe type);
ALCbackend* (*const createBackend)(ALCbackendFactory *self, ALCdevice *device, ALCbackend_Type type);
};
#define DEFINE_ALCBACKENDFACTORY_VTABLE(T) \
DECLARE_THUNK(T, ALCbackendFactory, ALCboolean, init) \
DECLARE_THUNK(T, ALCbackendFactory, void, deinit) \
DECLARE_THUNK1(T, ALCbackendFactory, ALCboolean, querySupport, ALCbackend_Type) \
DECLARE_THUNK1(T, ALCbackendFactory, void, probe, enum DevProbe) \
DECLARE_THUNK2(T, ALCbackendFactory, ALCbackend*, createBackend, ALCdevice*, ALCbackend_Type) \
\
static const struct ALCbackendFactoryVtable T##_ALCbackendFactory_vtable = { \
T##_ALCbackendFactory_init, \
T##_ALCbackendFactory_deinit, \
T##_ALCbackendFactory_querySupport, \
T##_ALCbackendFactory_probe, \
T##_ALCbackendFactory_createBackend, \
}
ALCbackendFactory *ALCpulseBackendFactory_getFactory(void);
ALCbackendFactory *ALCalsaBackendFactory_getFactory(void);
ALCbackendFactory *ALCossBackendFactory_getFactory(void);
ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void);
ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void);
ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
ALCbackendFactory *ALCloopbackFactory_getFactory(void);
ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type);
#endif /* AL_BACKENDS_BASE_H */
+707
View File
@@ -0,0 +1,707 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <alloca.h>
#include "alMain.h"
#include "alu.h"
#include <CoreServices/CoreServices.h>
#include <unistd.h>
#include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioToolbox.h>
typedef struct {
AudioUnit audioUnit;
ALuint frameSize;
ALdouble sampleRateRatio; // Ratio of hardware sample rate / requested sample rate
AudioStreamBasicDescription format; // This is the OpenAL format as a CoreAudio ASBD
AudioConverterRef audioConverter; // Sample rate converter if needed
AudioBufferList *bufferList; // Buffer for data coming from the input device
ALCvoid *resampleBuffer; // Buffer for returned RingBuffer data when resampling
RingBuffer *ring;
} ca_data;
static const ALCchar ca_device[] = "CoreAudio Default";
static void destroy_buffer_list(AudioBufferList* list)
{
if(list)
{
UInt32 i;
for(i = 0;i < list->mNumberBuffers;i++)
free(list->mBuffers[i].mData);
free(list);
}
}
static AudioBufferList* allocate_buffer_list(UInt32 channelCount, UInt32 byteSize)
{
AudioBufferList *list;
list = calloc(1, sizeof(AudioBufferList) + sizeof(AudioBuffer));
if(list)
{
list->mNumberBuffers = 1;
list->mBuffers[0].mNumberChannels = channelCount;
list->mBuffers[0].mDataByteSize = byteSize;
list->mBuffers[0].mData = malloc(byteSize);
if(list->mBuffers[0].mData == NULL)
{
free(list);
list = NULL;
}
}
return list;
}
static OSStatus ca_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
{
ALCdevice *device = (ALCdevice*)inRefCon;
ca_data *data = (ca_data*)device->ExtraData;
aluMixData(device, ioData->mBuffers[0].mData,
ioData->mBuffers[0].mDataByteSize / data->frameSize);
return noErr;
}
static OSStatus ca_capture_conversion_callback(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets,
AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void* inUserData)
{
ALCdevice *device = (ALCdevice*)inUserData;
ca_data *data = (ca_data*)device->ExtraData;
// Read from the ring buffer and store temporarily in a large buffer
ReadRingBuffer(data->ring, data->resampleBuffer, (ALsizei)(*ioNumberDataPackets));
// Set the input data
ioData->mNumberBuffers = 1;
ioData->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame;
ioData->mBuffers[0].mData = data->resampleBuffer;
ioData->mBuffers[0].mDataByteSize = (*ioNumberDataPackets) * data->format.mBytesPerFrame;
return noErr;
}
static OSStatus ca_capture_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
UInt32 inNumberFrames, AudioBufferList *ioData)
{
ALCdevice *device = (ALCdevice*)inRefCon;
ca_data *data = (ca_data*)device->ExtraData;
AudioUnitRenderActionFlags flags = 0;
OSStatus err;
// fill the bufferList with data from the input device
err = AudioUnitRender(data->audioUnit, &flags, inTimeStamp, 1, inNumberFrames, data->bufferList);
if(err != noErr)
{
ERR("AudioUnitRender error: %d\n", err);
return err;
}
WriteRingBuffer(data->ring, data->bufferList->mBuffers[0].mData, inNumberFrames);
return noErr;
}
static ALCenum ca_open_playback(ALCdevice *device, const ALCchar *deviceName)
{
ComponentDescription desc;
Component comp;
ca_data *data;
OSStatus err;
if(!deviceName)
deviceName = ca_device;
else if(strcmp(deviceName, ca_device) != 0)
return ALC_INVALID_VALUE;
/* open the default output unit */
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
comp = FindNextComponent(NULL, &desc);
if(comp == NULL)
{
ERR("FindNextComponent failed\n");
return ALC_INVALID_VALUE;
}
data = calloc(1, sizeof(*data));
err = OpenAComponent(comp, &data->audioUnit);
if(err != noErr)
{
ERR("OpenAComponent failed\n");
free(data);
return ALC_INVALID_VALUE;
}
/* init and start the default audio unit... */
err = AudioUnitInitialize(data->audioUnit);
if(err != noErr)
{
ERR("AudioUnitInitialize failed\n");
CloseComponent(data->audioUnit);
free(data);
return ALC_INVALID_VALUE;
}
al_string_copy_cstr(&device->DeviceName, deviceName);
device->ExtraData = data;
return ALC_NO_ERROR;
}
static void ca_close_playback(ALCdevice *device)
{
ca_data *data = (ca_data*)device->ExtraData;
AudioUnitUninitialize(data->audioUnit);
CloseComponent(data->audioUnit);
free(data);
device->ExtraData = NULL;
}
static ALCboolean ca_reset_playback(ALCdevice *device)
{
ca_data *data = (ca_data*)device->ExtraData;
AudioStreamBasicDescription streamFormat;
AURenderCallbackStruct input;
OSStatus err;
UInt32 size;
err = AudioUnitUninitialize(data->audioUnit);
if(err != noErr)
ERR("-- AudioUnitUninitialize failed.\n");
/* retrieve default output unit's properties (output side) */
size = sizeof(AudioStreamBasicDescription);
err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamFormat, &size);
if(err != noErr || size != sizeof(AudioStreamBasicDescription))
{
ERR("AudioUnitGetProperty failed\n");
return ALC_FALSE;
}
#if 0
TRACE("Output streamFormat of default output unit -\n");
TRACE(" streamFormat.mFramesPerPacket = %d\n", streamFormat.mFramesPerPacket);
TRACE(" streamFormat.mChannelsPerFrame = %d\n", streamFormat.mChannelsPerFrame);
TRACE(" streamFormat.mBitsPerChannel = %d\n", streamFormat.mBitsPerChannel);
TRACE(" streamFormat.mBytesPerPacket = %d\n", streamFormat.mBytesPerPacket);
TRACE(" streamFormat.mBytesPerFrame = %d\n", streamFormat.mBytesPerFrame);
TRACE(" streamFormat.mSampleRate = %5.0f\n", streamFormat.mSampleRate);
#endif
/* set default output unit's input side to match output side */
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, size);
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
return ALC_FALSE;
}
if(device->Frequency != streamFormat.mSampleRate)
{
device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize *
streamFormat.mSampleRate /
device->Frequency);
device->Frequency = streamFormat.mSampleRate;
}
/* FIXME: How to tell what channels are what in the output device, and how
* to specify what we're giving? eg, 6.0 vs 5.1 */
switch(streamFormat.mChannelsPerFrame)
{
case 1:
device->FmtChans = DevFmtMono;
break;
case 2:
device->FmtChans = DevFmtStereo;
break;
case 4:
device->FmtChans = DevFmtQuad;
break;
case 6:
device->FmtChans = DevFmtX51;
break;
case 7:
device->FmtChans = DevFmtX61;
break;
case 8:
device->FmtChans = DevFmtX71;
break;
default:
ERR("Unhandled channel count (%d), using Stereo\n", streamFormat.mChannelsPerFrame);
device->FmtChans = DevFmtStereo;
streamFormat.mChannelsPerFrame = 2;
break;
}
SetDefaultWFXChannelOrder(device);
/* use channel count and sample rate from the default output unit's current
* parameters, but reset everything else */
streamFormat.mFramesPerPacket = 1;
streamFormat.mFormatFlags = 0;
switch(device->FmtType)
{
case DevFmtUByte:
device->FmtType = DevFmtByte;
/* fall-through */
case DevFmtByte:
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
streamFormat.mBitsPerChannel = 8;
break;
case DevFmtUShort:
device->FmtType = DevFmtShort;
/* fall-through */
case DevFmtShort:
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
streamFormat.mBitsPerChannel = 16;
break;
case DevFmtUInt:
device->FmtType = DevFmtInt;
/* fall-through */
case DevFmtInt:
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
streamFormat.mBitsPerChannel = 32;
break;
case DevFmtFloat:
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
streamFormat.mBitsPerChannel = 32;
break;
}
streamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame *
streamFormat.mBitsPerChannel / 8;
streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame;
streamFormat.mFormatID = kAudioFormatLinearPCM;
streamFormat.mFormatFlags |= kAudioFormatFlagsNativeEndian |
kLinearPCMFormatFlagIsPacked;
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, sizeof(AudioStreamBasicDescription));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
return ALC_FALSE;
}
/* setup callback */
data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
input.inputProc = ca_callback;
input.inputProcRefCon = device;
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
return ALC_FALSE;
}
/* init the default audio unit... */
err = AudioUnitInitialize(data->audioUnit);
if(err != noErr)
{
ERR("AudioUnitInitialize failed\n");
return ALC_FALSE;
}
return ALC_TRUE;
}
static ALCboolean ca_start_playback(ALCdevice *device)
{
ca_data *data = (ca_data*)device->ExtraData;
OSStatus err;
err = AudioOutputUnitStart(data->audioUnit);
if(err != noErr)
{
ERR("AudioOutputUnitStart failed\n");
return ALC_FALSE;
}
return ALC_TRUE;
}
static void ca_stop_playback(ALCdevice *device)
{
ca_data *data = (ca_data*)device->ExtraData;
OSStatus err;
err = AudioOutputUnitStop(data->audioUnit);
if(err != noErr)
ERR("AudioOutputUnitStop failed\n");
}
static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
{
AudioStreamBasicDescription requestedFormat; // The application requested format
AudioStreamBasicDescription hardwareFormat; // The hardware format
AudioStreamBasicDescription outputFormat; // The AudioUnit output format
AURenderCallbackStruct input;
ComponentDescription desc;
AudioDeviceID inputDevice;
UInt32 outputFrameCount;
UInt32 propertySize;
UInt32 enableIO;
Component comp;
ca_data *data;
OSStatus err;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_HALOutput;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
// Search for component with given description
comp = FindNextComponent(NULL, &desc);
if(comp == NULL)
{
ERR("FindNextComponent failed\n");
return ALC_INVALID_VALUE;
}
data = calloc(1, sizeof(*data));
device->ExtraData = data;
// Open the component
err = OpenAComponent(comp, &data->audioUnit);
if(err != noErr)
{
ERR("OpenAComponent failed\n");
goto error;
}
// Turn off AudioUnit output
enableIO = 0;
err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
goto error;
}
// Turn on AudioUnit input
enableIO = 1;
err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
goto error;
}
// Get the default input device
propertySize = sizeof(AudioDeviceID);
err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &propertySize, &inputDevice);
if(err != noErr)
{
ERR("AudioHardwareGetProperty failed\n");
goto error;
}
if(inputDevice == kAudioDeviceUnknown)
{
ERR("No input device found\n");
goto error;
}
// Track the input device
err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
goto error;
}
// set capture callback
input.inputProc = ca_capture_callback;
input.inputProcRefCon = device;
err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
goto error;
}
// Initialize the device
err = AudioUnitInitialize(data->audioUnit);
if(err != noErr)
{
ERR("AudioUnitInitialize failed\n");
goto error;
}
// Get the hardware format
propertySize = sizeof(AudioStreamBasicDescription);
err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &hardwareFormat, &propertySize);
if(err != noErr || propertySize != sizeof(AudioStreamBasicDescription))
{
ERR("AudioUnitGetProperty failed\n");
goto error;
}
// Set up the requested format description
switch(device->FmtType)
{
case DevFmtUByte:
requestedFormat.mBitsPerChannel = 8;
requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked;
break;
case DevFmtShort:
requestedFormat.mBitsPerChannel = 16;
requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
break;
case DevFmtInt:
requestedFormat.mBitsPerChannel = 32;
requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
break;
case DevFmtFloat:
requestedFormat.mBitsPerChannel = 32;
requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked;
break;
case DevFmtByte:
case DevFmtUShort:
case DevFmtUInt:
ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
goto error;
}
switch(device->FmtChans)
{
case DevFmtMono:
requestedFormat.mChannelsPerFrame = 1;
break;
case DevFmtStereo:
requestedFormat.mChannelsPerFrame = 2;
break;
case DevFmtQuad:
case DevFmtX51:
case DevFmtX51Side:
case DevFmtX61:
case DevFmtX71:
ERR("%s not supported\n", DevFmtChannelsString(device->FmtChans));
goto error;
}
requestedFormat.mBytesPerFrame = requestedFormat.mChannelsPerFrame * requestedFormat.mBitsPerChannel / 8;
requestedFormat.mBytesPerPacket = requestedFormat.mBytesPerFrame;
requestedFormat.mSampleRate = device->Frequency;
requestedFormat.mFormatID = kAudioFormatLinearPCM;
requestedFormat.mReserved = 0;
requestedFormat.mFramesPerPacket = 1;
// save requested format description for later use
data->format = requestedFormat;
data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
// Use intermediate format for sample rate conversion (outputFormat)
// Set sample rate to the same as hardware for resampling later
outputFormat = requestedFormat;
outputFormat.mSampleRate = hardwareFormat.mSampleRate;
// Determine sample rate ratio for resampling
data->sampleRateRatio = outputFormat.mSampleRate / device->Frequency;
// The output format should be the requested format, but using the hardware sample rate
// This is because the AudioUnit will automatically scale other properties, except for sample rate
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, (void *)&outputFormat, sizeof(outputFormat));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
goto error;
}
// Set the AudioUnit output format frame count
outputFrameCount = device->UpdateSize * data->sampleRateRatio;
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed: %d\n", err);
goto error;
}
// Set up sample converter
err = AudioConverterNew(&outputFormat, &requestedFormat, &data->audioConverter);
if(err != noErr)
{
ERR("AudioConverterNew failed: %d\n", err);
goto error;
}
// Create a buffer for use in the resample callback
data->resampleBuffer = malloc(device->UpdateSize * data->frameSize * data->sampleRateRatio);
// Allocate buffer for the AudioUnit output
data->bufferList = allocate_buffer_list(outputFormat.mChannelsPerFrame, device->UpdateSize * data->frameSize * data->sampleRateRatio);
if(data->bufferList == NULL)
goto error;
data->ring = CreateRingBuffer(data->frameSize, (device->UpdateSize * data->sampleRateRatio) * device->NumUpdates);
if(data->ring == NULL)
goto error;
al_string_copy_cstr(&device->DeviceName, deviceName);
return ALC_NO_ERROR;
error:
DestroyRingBuffer(data->ring);
free(data->resampleBuffer);
destroy_buffer_list(data->bufferList);
if(data->audioConverter)
AudioConverterDispose(data->audioConverter);
if(data->audioUnit)
CloseComponent(data->audioUnit);
free(data);
device->ExtraData = NULL;
return ALC_INVALID_VALUE;
}
static void ca_close_capture(ALCdevice *device)
{
ca_data *data = (ca_data*)device->ExtraData;
DestroyRingBuffer(data->ring);
free(data->resampleBuffer);
destroy_buffer_list(data->bufferList);
AudioConverterDispose(data->audioConverter);
CloseComponent(data->audioUnit);
free(data);
device->ExtraData = NULL;
}
static void ca_start_capture(ALCdevice *device)
{
ca_data *data = (ca_data*)device->ExtraData;
OSStatus err = AudioOutputUnitStart(data->audioUnit);
if(err != noErr)
ERR("AudioOutputUnitStart failed\n");
}
static void ca_stop_capture(ALCdevice *device)
{
ca_data *data = (ca_data*)device->ExtraData;
OSStatus err = AudioOutputUnitStop(data->audioUnit);
if(err != noErr)
ERR("AudioOutputUnitStop failed\n");
}
static ALCenum ca_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
{
ca_data *data = (ca_data*)device->ExtraData;
AudioBufferList *list;
UInt32 frameCount;
OSStatus err;
// If no samples are requested, just return
if(samples == 0)
return ALC_NO_ERROR;
// Allocate a temporary AudioBufferList to use as the return resamples data
list = alloca(sizeof(AudioBufferList) + sizeof(AudioBuffer));
// Point the resampling buffer to the capture buffer
list->mNumberBuffers = 1;
list->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame;
list->mBuffers[0].mDataByteSize = samples * data->frameSize;
list->mBuffers[0].mData = buffer;
// Resample into another AudioBufferList
frameCount = samples;
err = AudioConverterFillComplexBuffer(data->audioConverter, ca_capture_conversion_callback,
device, &frameCount, list, NULL);
if(err != noErr)
{
ERR("AudioConverterFillComplexBuffer error: %d\n", err);
return ALC_INVALID_VALUE;
}
return ALC_NO_ERROR;
}
static ALCuint ca_available_samples(ALCdevice *device)
{
ca_data *data = device->ExtraData;
return RingBufferSize(data->ring) / data->sampleRateRatio;
}
static const BackendFuncs ca_funcs = {
ca_open_playback,
ca_close_playback,
ca_reset_playback,
ca_start_playback,
ca_stop_playback,
ca_open_capture,
ca_close_capture,
ca_start_capture,
ca_stop_capture,
ca_capture_samples,
ca_available_samples,
ALCdevice_GetLatencyDefault
};
ALCboolean alc_ca_init(BackendFuncs *func_list)
{
*func_list = ca_funcs;
return ALC_TRUE;
}
void alc_ca_deinit(void)
{
}
void alc_ca_probe(enum DevProbe type)
{
switch(type)
{
case ALL_DEVICE_PROBE:
AppendAllDevicesList(ca_device);
break;
case CAPTURE_DEVICE_PROBE:
AppendCaptureDeviceList(ca_device);
break;
}
}
File diff suppressed because it is too large Load Diff
+138
View File
@@ -0,0 +1,138 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2011 by Chris Robinson
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdlib.h>
#include "alMain.h"
#include "alu.h"
#include "backends/base.h"
typedef struct ALCloopback {
DERIVE_FROM_TYPE(ALCbackend);
} ALCloopback;
static void ALCloopback_Construct(ALCloopback *self, ALCdevice *device);
static DECLARE_FORWARD(ALCloopback, ALCbackend, void, Destruct)
static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name);
static void ALCloopback_close(ALCloopback *self);
static ALCboolean ALCloopback_reset(ALCloopback *self);
static ALCboolean ALCloopback_start(ALCloopback *self);
static void ALCloopback_stop(ALCloopback *self);
static DECLARE_FORWARD2(ALCloopback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCloopback, ALCbackend, ALCuint, availableSamples)
static DECLARE_FORWARD(ALCloopback, ALCbackend, ALint64, getLatency)
static DECLARE_FORWARD(ALCloopback, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCloopback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCloopback)
DEFINE_ALCBACKEND_VTABLE(ALCloopback);
static void ALCloopback_Construct(ALCloopback *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCloopback, ALCbackend, self);
}
static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
al_string_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
static void ALCloopback_close(ALCloopback* UNUSED(self))
{
}
static ALCboolean ALCloopback_reset(ALCloopback *self)
{
SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice);
return ALC_TRUE;
}
static ALCboolean ALCloopback_start(ALCloopback* UNUSED(self))
{
return ALC_TRUE;
}
static void ALCloopback_stop(ALCloopback* UNUSED(self))
{
}
typedef struct ALCloopbackFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCloopbackFactory;
#define ALCNULLBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCloopbackFactory, ALCbackendFactory) } }
ALCbackendFactory *ALCloopbackFactory_getFactory(void);
static ALCboolean ALCloopbackFactory_init(ALCloopbackFactory *self);
static DECLARE_FORWARD(ALCloopbackFactory, ALCbackendFactory, void, deinit)
static ALCboolean ALCloopbackFactory_querySupport(ALCloopbackFactory *self, ALCbackend_Type type);
static void ALCloopbackFactory_probe(ALCloopbackFactory *self, enum DevProbe type);
static ALCbackend* ALCloopbackFactory_createBackend(ALCloopbackFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCloopbackFactory);
ALCbackendFactory *ALCloopbackFactory_getFactory(void)
{
static ALCloopbackFactory factory = ALCNULLBACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
static ALCboolean ALCloopbackFactory_init(ALCloopbackFactory* UNUSED(self))
{
return ALC_TRUE;
}
static ALCboolean ALCloopbackFactory_querySupport(ALCloopbackFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Loopback)
return ALC_TRUE;
return ALC_FALSE;
}
static void ALCloopbackFactory_probe(ALCloopbackFactory* UNUSED(self), enum DevProbe UNUSED(type))
{
}
static ALCbackend* ALCloopbackFactory_createBackend(ALCloopbackFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Loopback)
{
ALCloopback *backend;
backend = ALCloopback_New(sizeof(*backend));
if(!backend) return NULL;
memset(backend, 0, sizeof(*backend));
ALCloopback_Construct(backend, device);
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
File diff suppressed because it is too large Load Diff
+228
View File
@@ -0,0 +1,228 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2010 by Chris Robinson
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdlib.h>
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#include "alMain.h"
#include "alu.h"
#include "threads.h"
#include "compat.h"
#include "backends/base.h"
typedef struct ALCnullBackend {
DERIVE_FROM_TYPE(ALCbackend);
volatile int killNow;
althrd_t thread;
} ALCnullBackend;
static int ALCnullBackend_mixerProc(void *ptr);
static void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device);
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, Destruct)
static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name);
static void ALCnullBackend_close(ALCnullBackend *self);
static ALCboolean ALCnullBackend_reset(ALCnullBackend *self);
static ALCboolean ALCnullBackend_start(ALCnullBackend *self);
static void ALCnullBackend_stop(ALCnullBackend *self);
static DECLARE_FORWARD2(ALCnullBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ALCuint, availableSamples)
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ALint64, getLatency)
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCnullBackend)
DEFINE_ALCBACKEND_VTABLE(ALCnullBackend);
static const ALCchar nullDevice[] = "No Output";
static void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCnullBackend, ALCbackend, self);
}
static int ALCnullBackend_mixerProc(void *ptr)
{
ALCnullBackend *self = (ALCnullBackend*)ptr;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
struct timespec now, start;
ALuint64 avail, done;
const long restTime = (long)((ALuint64)device->UpdateSize * 1000000000 /
device->Frequency / 2);
SetRTPriority();
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
done = 0;
if(altimespec_get(&start, AL_TIME_UTC) != AL_TIME_UTC)
{
ERR("Failed to get starting time\n");
return 1;
}
while(!self->killNow && device->Connected)
{
if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC)
{
ERR("Failed to get current time\n");
return 1;
}
avail = (now.tv_sec - start.tv_sec) * device->Frequency;
avail += (ALint64)(now.tv_nsec - start.tv_nsec) * device->Frequency / 1000000000;
if(avail < done)
{
/* Oops, time skipped backwards. Reset the number of samples done
* with one update available since we (likely) just came back from
* sleeping. */
done = avail - device->UpdateSize;
}
if(avail-done < device->UpdateSize)
al_nssleep(0, restTime);
else while(avail-done >= device->UpdateSize)
{
aluMixData(device, NULL, device->UpdateSize);
done += device->UpdateSize;
}
}
return 0;
}
static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name)
{
ALCdevice *device;
if(!name)
name = nullDevice;
else if(strcmp(name, nullDevice) != 0)
return ALC_INVALID_VALUE;
device = STATIC_CAST(ALCbackend, self)->mDevice;
al_string_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
static void ALCnullBackend_close(ALCnullBackend* UNUSED(self))
{
}
static ALCboolean ALCnullBackend_reset(ALCnullBackend *self)
{
SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice);
return ALC_TRUE;
}
static ALCboolean ALCnullBackend_start(ALCnullBackend *self)
{
self->killNow = 0;
if(althrd_create(&self->thread, ALCnullBackend_mixerProc, self) != althrd_success)
return ALC_FALSE;
return ALC_TRUE;
}
static void ALCnullBackend_stop(ALCnullBackend *self)
{
int res;
if(self->killNow)
return;
self->killNow = 1;
althrd_join(self->thread, &res);
}
typedef struct ALCnullBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCnullBackendFactory;
#define ALCNULLBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCnullBackendFactory, ALCbackendFactory) } }
ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
static ALCboolean ALCnullBackendFactory_init(ALCnullBackendFactory *self);
static DECLARE_FORWARD(ALCnullBackendFactory, ALCbackendFactory, void, deinit)
static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory *self, ALCbackend_Type type);
static void ALCnullBackendFactory_probe(ALCnullBackendFactory *self, enum DevProbe type);
static ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCnullBackendFactory);
ALCbackendFactory *ALCnullBackendFactory_getFactory(void)
{
static ALCnullBackendFactory factory = ALCNULLBACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
static ALCboolean ALCnullBackendFactory_init(ALCnullBackendFactory* UNUSED(self))
{
return ALC_TRUE;
}
static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
return ALC_TRUE;
return ALC_FALSE;
}
static void ALCnullBackendFactory_probe(ALCnullBackendFactory* UNUSED(self), enum DevProbe type)
{
switch(type)
{
case ALL_DEVICE_PROBE:
AppendAllDevicesList(nullDevice);
break;
case CAPTURE_DEVICE_PROBE:
break;
}
}
static ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCnullBackend *backend;
backend = ALCnullBackend_New(sizeof(*backend));
if(!backend) return NULL;
memset(backend, 0, sizeof(*backend));
ALCnullBackend_Construct(backend, device);
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
+424
View File
@@ -0,0 +1,424 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* This is an OpenAL backend for Android using the native audio APIs based on
* OpenSL ES 1.0.1. It is based on source code for the native-audio sample app
* bundled with NDK.
*/
#include "config.h"
#include <stdlib.h>
#include "alMain.h"
#include "alu.h"
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
/* Helper macros */
#define VCALL(obj, func) ((*(obj))->func((obj), EXTRACT_VCALL_ARGS
#define VCALL0(obj, func) ((*(obj))->func((obj) EXTRACT_VCALL_ARGS
typedef struct {
/* engine interfaces */
SLObjectItf engineObject;
SLEngineItf engine;
/* output mix interfaces */
SLObjectItf outputMix;
/* buffer queue player interfaces */
SLObjectItf bufferQueueObject;
void *buffer;
ALuint bufferSize;
ALuint curBuffer;
ALuint frameSize;
} osl_data;
static const ALCchar opensl_device[] = "OpenSL";
static SLuint32 GetChannelMask(enum DevFmtChannels chans)
{
switch(chans)
{
case DevFmtMono: return SL_SPEAKER_FRONT_CENTER;
case DevFmtStereo: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT;
case DevFmtQuad: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT;
case DevFmtX51: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT;
case DevFmtX61: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
SL_SPEAKER_BACK_CENTER|
SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT;
case DevFmtX71: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT|
SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT;
case DevFmtX51Side: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT;
}
return 0;
}
static const char *res_str(SLresult result)
{
switch(result)
{
case SL_RESULT_SUCCESS: return "Success";
case SL_RESULT_PRECONDITIONS_VIOLATED: return "Preconditions violated";
case SL_RESULT_PARAMETER_INVALID: return "Parameter invalid";
case SL_RESULT_MEMORY_FAILURE: return "Memory failure";
case SL_RESULT_RESOURCE_ERROR: return "Resource error";
case SL_RESULT_RESOURCE_LOST: return "Resource lost";
case SL_RESULT_IO_ERROR: return "I/O error";
case SL_RESULT_BUFFER_INSUFFICIENT: return "Buffer insufficient";
case SL_RESULT_CONTENT_CORRUPTED: return "Content corrupted";
case SL_RESULT_CONTENT_UNSUPPORTED: return "Content unsupported";
case SL_RESULT_CONTENT_NOT_FOUND: return "Content not found";
case SL_RESULT_PERMISSION_DENIED: return "Permission denied";
case SL_RESULT_FEATURE_UNSUPPORTED: return "Feature unsupported";
case SL_RESULT_INTERNAL_ERROR: return "Internal error";
case SL_RESULT_UNKNOWN_ERROR: return "Unknown error";
case SL_RESULT_OPERATION_ABORTED: return "Operation aborted";
case SL_RESULT_CONTROL_LOST: return "Control lost";
#ifdef SL_RESULT_READONLY
case SL_RESULT_READONLY: return "ReadOnly";
#endif
#ifdef SL_RESULT_ENGINEOPTION_UNSUPPORTED
case SL_RESULT_ENGINEOPTION_UNSUPPORTED: return "Engine option unsupported";
#endif
#ifdef SL_RESULT_SOURCE_SINK_INCOMPATIBLE
case SL_RESULT_SOURCE_SINK_INCOMPATIBLE: return "Source/Sink incompatible";
#endif
}
return "Unknown error code";
}
#define PRINTERR(x, s) do { \
if((x) != SL_RESULT_SUCCESS) \
ERR("%s: %s\n", (s), res_str((x))); \
} while(0)
/* this callback handler is called every time a buffer finishes playing */
static void opensl_callback(SLAndroidSimpleBufferQueueItf bq, void *context)
{
ALCdevice *Device = context;
osl_data *data = Device->ExtraData;
ALvoid *buf;
SLresult result;
buf = (ALbyte*)data->buffer + data->curBuffer*data->bufferSize;
aluMixData(Device, buf, data->bufferSize/data->frameSize);
result = VCALL(bq,Enqueue)(buf, data->bufferSize);
PRINTERR(result, "bq->Enqueue");
data->curBuffer = (data->curBuffer+1) % Device->NumUpdates;
}
static ALCenum opensl_open_playback(ALCdevice *Device, const ALCchar *deviceName)
{
osl_data *data = NULL;
SLresult result;
if(!deviceName)
deviceName = opensl_device;
else if(strcmp(deviceName, opensl_device) != 0)
return ALC_INVALID_VALUE;
data = calloc(1, sizeof(*data));
if(!data)
return ALC_OUT_OF_MEMORY;
// create engine
result = slCreateEngine(&data->engineObject, 0, NULL, 0, NULL, NULL);
PRINTERR(result, "slCreateEngine");
if(SL_RESULT_SUCCESS == result)
{
result = VCALL(data->engineObject,Realize)(SL_BOOLEAN_FALSE);
PRINTERR(result, "engine->Realize");
}
if(SL_RESULT_SUCCESS == result)
{
result = VCALL(data->engineObject,GetInterface)(SL_IID_ENGINE, &data->engine);
PRINTERR(result, "engine->GetInterface");
}
if(SL_RESULT_SUCCESS == result)
{
result = VCALL(data->engine,CreateOutputMix)(&data->outputMix, 0, NULL, NULL);
PRINTERR(result, "engine->CreateOutputMix");
}
if(SL_RESULT_SUCCESS == result)
{
result = VCALL(data->outputMix,Realize)(SL_BOOLEAN_FALSE);
PRINTERR(result, "outputMix->Realize");
}
if(SL_RESULT_SUCCESS != result)
{
if(data->outputMix != NULL)
VCALL0(data->outputMix,Destroy)();
data->outputMix = NULL;
if(data->engineObject != NULL)
VCALL0(data->engineObject,Destroy)();
data->engineObject = NULL;
data->engine = NULL;
free(data);
return ALC_INVALID_VALUE;
}
al_string_copy_cstr(&Device->DeviceName, deviceName);
Device->ExtraData = data;
return ALC_NO_ERROR;
}
static void opensl_close_playback(ALCdevice *Device)
{
osl_data *data = Device->ExtraData;
if(data->bufferQueueObject != NULL)
VCALL0(data->bufferQueueObject,Destroy)();
data->bufferQueueObject = NULL;
VCALL0(data->outputMix,Destroy)();
data->outputMix = NULL;
VCALL0(data->engineObject,Destroy)();
data->engineObject = NULL;
data->engine = NULL;
free(data);
Device->ExtraData = NULL;
}
static ALCboolean opensl_reset_playback(ALCdevice *Device)
{
osl_data *data = Device->ExtraData;
SLDataLocator_AndroidSimpleBufferQueue loc_bufq;
SLDataLocator_OutputMix loc_outmix;
SLDataFormat_PCM format_pcm;
SLDataSource audioSrc;
SLDataSink audioSnk;
SLInterfaceID id;
SLboolean req;
SLresult result;
Device->UpdateSize = (ALuint64)Device->UpdateSize * 44100 / Device->Frequency;
Device->UpdateSize = Device->UpdateSize * Device->NumUpdates / 2;
Device->NumUpdates = 2;
Device->Frequency = 44100;
Device->FmtChans = DevFmtStereo;
Device->FmtType = DevFmtShort;
SetDefaultWFXChannelOrder(Device);
id = SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
req = SL_BOOLEAN_TRUE;
loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
loc_bufq.numBuffers = Device->NumUpdates;
format_pcm.formatType = SL_DATAFORMAT_PCM;
format_pcm.numChannels = ChannelsFromDevFmt(Device->FmtChans);
format_pcm.samplesPerSec = Device->Frequency * 1000;
format_pcm.bitsPerSample = BytesFromDevFmt(Device->FmtType) * 8;
format_pcm.containerSize = format_pcm.bitsPerSample;
format_pcm.channelMask = GetChannelMask(Device->FmtChans);
format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN :
SL_BYTEORDER_BIGENDIAN;
audioSrc.pLocator = &loc_bufq;
audioSrc.pFormat = &format_pcm;
loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
loc_outmix.outputMix = data->outputMix;
audioSnk.pLocator = &loc_outmix;
audioSnk.pFormat = NULL;
if(data->bufferQueueObject != NULL)
VCALL0(data->bufferQueueObject,Destroy)();
data->bufferQueueObject = NULL;
result = VCALL(data->engine,CreateAudioPlayer)(&data->bufferQueueObject, &audioSrc, &audioSnk, 1, &id, &req);
PRINTERR(result, "engine->CreateAudioPlayer");
if(SL_RESULT_SUCCESS == result)
{
result = VCALL(data->bufferQueueObject,Realize)(SL_BOOLEAN_FALSE);
PRINTERR(result, "bufferQueue->Realize");
}
if(SL_RESULT_SUCCESS != result)
{
if(data->bufferQueueObject != NULL)
VCALL0(data->bufferQueueObject,Destroy)();
data->bufferQueueObject = NULL;
return ALC_FALSE;
}
return ALC_TRUE;
}
static ALCboolean opensl_start_playback(ALCdevice *Device)
{
osl_data *data = Device->ExtraData;
SLAndroidSimpleBufferQueueItf bufferQueue;
SLPlayItf player;
SLresult result;
ALuint i;
result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_BUFFERQUEUE, &bufferQueue);
PRINTERR(result, "bufferQueue->GetInterface");
if(SL_RESULT_SUCCESS == result)
{
result = VCALL(bufferQueue,RegisterCallback)(opensl_callback, Device);
PRINTERR(result, "bufferQueue->RegisterCallback");
}
if(SL_RESULT_SUCCESS == result)
{
data->frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
data->bufferSize = Device->UpdateSize * data->frameSize;
data->buffer = calloc(Device->NumUpdates, data->bufferSize);
if(!data->buffer)
{
result = SL_RESULT_MEMORY_FAILURE;
PRINTERR(result, "calloc");
}
}
/* enqueue the first buffer to kick off the callbacks */
for(i = 0;i < Device->NumUpdates;i++)
{
if(SL_RESULT_SUCCESS == result)
{
ALvoid *buf = (ALbyte*)data->buffer + i*data->bufferSize;
result = VCALL(bufferQueue,Enqueue)(buf, data->bufferSize);
PRINTERR(result, "bufferQueue->Enqueue");
}
}
data->curBuffer = 0;
if(SL_RESULT_SUCCESS == result)
{
result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_PLAY, &player);
PRINTERR(result, "bufferQueue->GetInterface");
}
if(SL_RESULT_SUCCESS == result)
{
result = VCALL(player,SetPlayState)(SL_PLAYSTATE_PLAYING);
PRINTERR(result, "player->SetPlayState");
}
if(SL_RESULT_SUCCESS != result)
{
if(data->bufferQueueObject != NULL)
VCALL0(data->bufferQueueObject,Destroy)();
data->bufferQueueObject = NULL;
free(data->buffer);
data->buffer = NULL;
data->bufferSize = 0;
return ALC_FALSE;
}
return ALC_TRUE;
}
static void opensl_stop_playback(ALCdevice *Device)
{
osl_data *data = Device->ExtraData;
SLPlayItf player;
SLAndroidSimpleBufferQueueItf bufferQueue;
SLresult result;
result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_PLAY, &player);
PRINTERR(result, "bufferQueue->GetInterface");
if(SL_RESULT_SUCCESS == result)
{
result = VCALL(player,SetPlayState)(SL_PLAYSTATE_STOPPED);
PRINTERR(result, "player->SetPlayState");
}
result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_BUFFERQUEUE, &bufferQueue);
PRINTERR(result, "bufferQueue->GetInterface");
if(SL_RESULT_SUCCESS == result)
{
result = VCALL0(bufferQueue,Clear)();
PRINTERR(result, "bufferQueue->Clear");
}
free(data->buffer);
data->buffer = NULL;
data->bufferSize = 0;
}
static const BackendFuncs opensl_funcs = {
opensl_open_playback,
opensl_close_playback,
opensl_reset_playback,
opensl_start_playback,
opensl_stop_playback,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
ALCdevice_GetLatencyDefault
};
ALCboolean alc_opensl_init(BackendFuncs *func_list)
{
*func_list = opensl_funcs;
return ALC_TRUE;
}
void alc_opensl_deinit(void)
{
}
void alc_opensl_probe(enum DevProbe type)
{
switch(type)
{
case ALL_DEVICE_PROBE:
AppendAllDevicesList(opensl_device);
break;
case CAPTURE_DEVICE_PROBE:
break;
}
}
+632
View File
@@ -0,0 +1,632 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <unistd.h>
#include <errno.h>
#include <math.h>
#include "alMain.h"
#include "alu.h"
#include "threads.h"
#include "compat.h"
#include "backends/base.h"
#include <sys/soundcard.h>
/*
* The OSS documentation talks about SOUND_MIXER_READ, but the header
* only contains MIXER_READ. Play safe. Same for WRITE.
*/
#ifndef SOUND_MIXER_READ
#define SOUND_MIXER_READ MIXER_READ
#endif
#ifndef SOUND_MIXER_WRITE
#define SOUND_MIXER_WRITE MIXER_WRITE
#endif
static const ALCchar oss_device[] = "OSS Default";
static const char *oss_driver = "/dev/dsp";
static const char *oss_capture = "/dev/dsp";
static int log2i(ALCuint x)
{
int y = 0;
while (x > 1)
{
x >>= 1;
y++;
}
return y;
}
typedef struct ALCplaybackOSS {
DERIVE_FROM_TYPE(ALCbackend);
int fd;
ALubyte *mix_data;
int data_size;
volatile int killNow;
althrd_t thread;
} ALCplaybackOSS;
static int ALCplaybackOSS_mixerProc(void *ptr);
static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device);
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, Destruct)
static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name);
static void ALCplaybackOSS_close(ALCplaybackOSS *self);
static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self);
static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self);
static void ALCplaybackOSS_stop(ALCplaybackOSS *self);
static DECLARE_FORWARD2(ALCplaybackOSS, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ALCuint, availableSamples)
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ALint64, getLatency)
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCplaybackOSS)
DEFINE_ALCBACKEND_VTABLE(ALCplaybackOSS);
static int ALCplaybackOSS_mixerProc(void *ptr)
{
ALCplaybackOSS *self = (ALCplaybackOSS*)ptr;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
ALint frameSize;
ssize_t wrote;
SetRTPriority();
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
while(!self->killNow && device->Connected)
{
ALint len = self->data_size;
ALubyte *WritePtr = self->mix_data;
aluMixData(device, WritePtr, len/frameSize);
while(len > 0 && !self->killNow)
{
wrote = write(self->fd, WritePtr, len);
if(wrote < 0)
{
if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
{
ERR("write failed: %s\n", strerror(errno));
ALCplaybackOSS_lock(self);
aluHandleDisconnect(device);
ALCplaybackOSS_unlock(self);
break;
}
al_nssleep(0, 1000000);
continue;
}
len -= wrote;
WritePtr += wrote;
}
}
return 0;
}
static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCplaybackOSS, ALCbackend, self);
}
static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
if(!name)
name = oss_device;
else if(strcmp(name, oss_device) != 0)
return ALC_INVALID_VALUE;
self->killNow = 0;
self->fd = open(oss_driver, O_WRONLY);
if(self->fd == -1)
{
ERR("Could not open %s: %s\n", oss_driver, strerror(errno));
return ALC_INVALID_VALUE;
}
al_string_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
static void ALCplaybackOSS_close(ALCplaybackOSS *self)
{
close(self->fd);
self->fd = -1;
}
static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
int numFragmentsLogSize;
int log2FragmentSize;
unsigned int periods;
audio_buf_info info;
ALuint frameSize;
int numChannels;
int ossFormat;
int ossSpeed;
char *err;
switch(device->FmtType)
{
case DevFmtByte:
ossFormat = AFMT_S8;
break;
case DevFmtUByte:
ossFormat = AFMT_U8;
break;
case DevFmtUShort:
case DevFmtInt:
case DevFmtUInt:
case DevFmtFloat:
device->FmtType = DevFmtShort;
/* fall-through */
case DevFmtShort:
ossFormat = AFMT_S16_NE;
break;
}
periods = device->NumUpdates;
numChannels = ChannelsFromDevFmt(device->FmtChans);
frameSize = numChannels * BytesFromDevFmt(device->FmtType);
ossSpeed = device->Frequency;
log2FragmentSize = log2i(device->UpdateSize * frameSize);
/* according to the OSS spec, 16 bytes are the minimum */
if (log2FragmentSize < 4)
log2FragmentSize = 4;
/* Subtract one period since the temp mixing buffer counts as one. Still
* need at least two on the card, though. */
if(periods > 2) periods--;
numFragmentsLogSize = (periods << 16) | log2FragmentSize;
#define CHECKERR(func) if((func) < 0) { \
err = #func; \
goto err; \
}
/* Don't fail if SETFRAGMENT fails. We can handle just about anything
* that's reported back via GETOSPACE */
ioctl(self->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize);
CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFMT, &ossFormat));
CHECKERR(ioctl(self->fd, SNDCTL_DSP_CHANNELS, &numChannels));
CHECKERR(ioctl(self->fd, SNDCTL_DSP_SPEED, &ossSpeed));
CHECKERR(ioctl(self->fd, SNDCTL_DSP_GETOSPACE, &info));
if(0)
{
err:
ERR("%s failed: %s\n", err, strerror(errno));
return ALC_FALSE;
}
#undef CHECKERR
if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels)
{
ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
return ALC_FALSE;
}
if(!((ossFormat == AFMT_S8 && device->FmtType == DevFmtByte) ||
(ossFormat == AFMT_U8 && device->FmtType == DevFmtUByte) ||
(ossFormat == AFMT_S16_NE && device->FmtType == DevFmtShort)))
{
ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device->FmtType), ossFormat);
return ALC_FALSE;
}
device->Frequency = ossSpeed;
device->UpdateSize = info.fragsize / frameSize;
device->NumUpdates = info.fragments + 1;
SetDefaultChannelOrder(device);
return ALC_TRUE;
}
static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
self->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
self->mix_data = calloc(1, self->data_size);
self->killNow = 0;
if(althrd_create(&self->thread, ALCplaybackOSS_mixerProc, self) != althrd_success)
{
free(self->mix_data);
self->mix_data = NULL;
return ALC_FALSE;
}
return ALC_TRUE;
}
static void ALCplaybackOSS_stop(ALCplaybackOSS *self)
{
int res;
if(self->killNow)
return;
self->killNow = 1;
althrd_join(self->thread, &res);
if(ioctl(self->fd, SNDCTL_DSP_RESET) != 0)
ERR("Error resetting device: %s\n", strerror(errno));
free(self->mix_data);
self->mix_data = NULL;
}
typedef struct ALCcaptureOSS {
DERIVE_FROM_TYPE(ALCbackend);
int fd;
ALubyte *read_data;
int data_size;
RingBuffer *ring;
int doCapture;
volatile int killNow;
althrd_t thread;
} ALCcaptureOSS;
static int ALCcaptureOSS_recordProc(void *ptr);
static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device);
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, Destruct)
static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name);
static void ALCcaptureOSS_close(ALCcaptureOSS *self);
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ALCboolean, reset)
static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self);
static void ALCcaptureOSS_stop(ALCcaptureOSS *self);
static ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples);
static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self);
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ALint64, getLatency)
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCcaptureOSS)
DEFINE_ALCBACKEND_VTABLE(ALCcaptureOSS);
static int ALCcaptureOSS_recordProc(void *ptr)
{
ALCcaptureOSS *self = (ALCcaptureOSS*)ptr;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
int frameSize;
int amt;
SetRTPriority();
althrd_setname(althrd_current(), "alsoft-record");
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
while(!self->killNow)
{
amt = read(self->fd, self->read_data, self->data_size);
if(amt < 0)
{
ERR("read failed: %s\n", strerror(errno));
ALCcaptureOSS_lock(self);
aluHandleDisconnect(device);
ALCcaptureOSS_unlock(self);
break;
}
if(amt == 0)
{
al_nssleep(0, 1000000);
continue;
}
if(self->doCapture)
WriteRingBuffer(self->ring, self->read_data, amt/frameSize);
}
return 0;
}
static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCcaptureOSS, ALCbackend, self);
}
static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
int numFragmentsLogSize;
int log2FragmentSize;
unsigned int periods;
audio_buf_info info;
ALuint frameSize;
int numChannels;
int ossFormat;
int ossSpeed;
char *err;
if(!name)
name = oss_device;
else if(strcmp(name, oss_device) != 0)
return ALC_INVALID_VALUE;
self->fd = open(oss_capture, O_RDONLY);
if(self->fd == -1)
{
ERR("Could not open %s: %s\n", oss_capture, strerror(errno));
return ALC_INVALID_VALUE;
}
switch(device->FmtType)
{
case DevFmtByte:
ossFormat = AFMT_S8;
break;
case DevFmtUByte:
ossFormat = AFMT_U8;
break;
case DevFmtShort:
ossFormat = AFMT_S16_NE;
break;
case DevFmtUShort:
case DevFmtInt:
case DevFmtUInt:
case DevFmtFloat:
ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
return ALC_INVALID_VALUE;
}
periods = 4;
numChannels = ChannelsFromDevFmt(device->FmtChans);
frameSize = numChannels * BytesFromDevFmt(device->FmtType);
ossSpeed = device->Frequency;
log2FragmentSize = log2i(device->UpdateSize * device->NumUpdates *
frameSize / periods);
/* according to the OSS spec, 16 bytes are the minimum */
if (log2FragmentSize < 4)
log2FragmentSize = 4;
numFragmentsLogSize = (periods << 16) | log2FragmentSize;
#define CHECKERR(func) if((func) < 0) { \
err = #func; \
goto err; \
}
CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize));
CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFMT, &ossFormat));
CHECKERR(ioctl(self->fd, SNDCTL_DSP_CHANNELS, &numChannels));
CHECKERR(ioctl(self->fd, SNDCTL_DSP_SPEED, &ossSpeed));
CHECKERR(ioctl(self->fd, SNDCTL_DSP_GETISPACE, &info));
if(0)
{
err:
ERR("%s failed: %s\n", err, strerror(errno));
close(self->fd);
self->fd = -1;
return ALC_INVALID_VALUE;
}
#undef CHECKERR
if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels)
{
ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
close(self->fd);
self->fd = -1;
return ALC_INVALID_VALUE;
}
if(!((ossFormat == AFMT_S8 && device->FmtType == DevFmtByte) ||
(ossFormat == AFMT_U8 && device->FmtType == DevFmtUByte) ||
(ossFormat == AFMT_S16_NE && device->FmtType == DevFmtShort)))
{
ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device->FmtType), ossFormat);
close(self->fd);
self->fd = -1;
return ALC_INVALID_VALUE;
}
self->ring = CreateRingBuffer(frameSize, device->UpdateSize * device->NumUpdates);
if(!self->ring)
{
ERR("Ring buffer create failed\n");
close(self->fd);
self->fd = -1;
return ALC_OUT_OF_MEMORY;
}
self->data_size = info.fragsize;
self->read_data = calloc(1, self->data_size);
self->killNow = 0;
if(althrd_create(&self->thread, ALCcaptureOSS_recordProc, self) != althrd_success)
{
device->ExtraData = NULL;
close(self->fd);
self->fd = -1;
return ALC_OUT_OF_MEMORY;
}
al_string_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
static void ALCcaptureOSS_close(ALCcaptureOSS *self)
{
int res;
self->killNow = 1;
althrd_join(self->thread, &res);
close(self->fd);
self->fd = -1;
DestroyRingBuffer(self->ring);
self->ring = NULL;
free(self->read_data);
self->read_data = NULL;
}
static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self)
{
self->doCapture = 1;
return ALC_TRUE;
}
static void ALCcaptureOSS_stop(ALCcaptureOSS *self)
{
self->doCapture = 0;
}
static ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples)
{
ReadRingBuffer(self->ring, buffer, samples);
return ALC_NO_ERROR;
}
static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self)
{
return RingBufferSize(self->ring);
}
typedef struct ALCossBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCossBackendFactory;
#define ALCOSSBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCossBackendFactory, ALCbackendFactory) } }
ALCbackendFactory *ALCossBackendFactory_getFactory(void);
static ALCboolean ALCossBackendFactory_init(ALCossBackendFactory *self);
static DECLARE_FORWARD(ALCossBackendFactory, ALCbackendFactory, void, deinit)
static ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory *self, ALCbackend_Type type);
static void ALCossBackendFactory_probe(ALCossBackendFactory *self, enum DevProbe type);
static ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCossBackendFactory);
ALCbackendFactory *ALCossBackendFactory_getFactory(void)
{
static ALCossBackendFactory factory = ALCOSSBACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
ALCboolean ALCossBackendFactory_init(ALCossBackendFactory* UNUSED(self))
{
ConfigValueStr("oss", "device", &oss_driver);
ConfigValueStr("oss", "capture", &oss_capture);
return ALC_TRUE;
}
ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Playback || type == ALCbackend_Capture)
return ALC_TRUE;
return ALC_FALSE;
}
void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProbe type)
{
switch(type)
{
case ALL_DEVICE_PROBE:
{
#ifdef HAVE_STAT
struct stat buf;
if(stat(oss_driver, &buf) == 0)
#endif
AppendAllDevicesList(oss_device);
}
break;
case CAPTURE_DEVICE_PROBE:
{
#ifdef HAVE_STAT
struct stat buf;
if(stat(oss_capture, &buf) == 0)
#endif
AppendCaptureDeviceList(oss_device);
}
break;
}
}
ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCplaybackOSS *backend;
backend = ALCplaybackOSS_New(sizeof(*backend));
if(!backend) return NULL;
memset(backend, 0, sizeof(*backend));
ALCplaybackOSS_Construct(backend, device);
return STATIC_CAST(ALCbackend, backend);
}
if(type == ALCbackend_Capture)
{
ALCcaptureOSS *backend;
backend = ALCcaptureOSS_New(sizeof(*backend));
if(!backend) return NULL;
memset(backend, 0, sizeof(*backend));
ALCcaptureOSS_Construct(backend, device);
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
+469
View File
@@ -0,0 +1,469 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "alMain.h"
#include "alu.h"
#include "compat.h"
#include <portaudio.h>
static const ALCchar pa_device[] = "PortAudio Default";
#ifdef HAVE_DYNLOAD
static void *pa_handle;
#define MAKE_FUNC(x) static __typeof(x) * p##x
MAKE_FUNC(Pa_Initialize);
MAKE_FUNC(Pa_Terminate);
MAKE_FUNC(Pa_GetErrorText);
MAKE_FUNC(Pa_StartStream);
MAKE_FUNC(Pa_StopStream);
MAKE_FUNC(Pa_OpenStream);
MAKE_FUNC(Pa_CloseStream);
MAKE_FUNC(Pa_GetDefaultOutputDevice);
MAKE_FUNC(Pa_GetDefaultInputDevice);
MAKE_FUNC(Pa_GetStreamInfo);
#undef MAKE_FUNC
#define Pa_Initialize pPa_Initialize
#define Pa_Terminate pPa_Terminate
#define Pa_GetErrorText pPa_GetErrorText
#define Pa_StartStream pPa_StartStream
#define Pa_StopStream pPa_StopStream
#define Pa_OpenStream pPa_OpenStream
#define Pa_CloseStream pPa_CloseStream
#define Pa_GetDefaultOutputDevice pPa_GetDefaultOutputDevice
#define Pa_GetDefaultInputDevice pPa_GetDefaultInputDevice
#define Pa_GetStreamInfo pPa_GetStreamInfo
#endif
static ALCboolean pa_load(void)
{
PaError err;
#ifdef HAVE_DYNLOAD
if(!pa_handle)
{
#ifdef _WIN32
# define PALIB "portaudio.dll"
#elif defined(__APPLE__) && defined(__MACH__)
# define PALIB "libportaudio.2.dylib"
#elif defined(__OpenBSD__)
# define PALIB "libportaudio.so"
#else
# define PALIB "libportaudio.so.2"
#endif
pa_handle = LoadLib(PALIB);
if(!pa_handle)
return ALC_FALSE;
#define LOAD_FUNC(f) do { \
p##f = GetSymbol(pa_handle, #f); \
if(p##f == NULL) \
{ \
CloseLib(pa_handle); \
pa_handle = NULL; \
return ALC_FALSE; \
} \
} while(0)
LOAD_FUNC(Pa_Initialize);
LOAD_FUNC(Pa_Terminate);
LOAD_FUNC(Pa_GetErrorText);
LOAD_FUNC(Pa_StartStream);
LOAD_FUNC(Pa_StopStream);
LOAD_FUNC(Pa_OpenStream);
LOAD_FUNC(Pa_CloseStream);
LOAD_FUNC(Pa_GetDefaultOutputDevice);
LOAD_FUNC(Pa_GetDefaultInputDevice);
LOAD_FUNC(Pa_GetStreamInfo);
#undef LOAD_FUNC
if((err=Pa_Initialize()) != paNoError)
{
ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
CloseLib(pa_handle);
pa_handle = NULL;
return ALC_FALSE;
}
}
#else
if((err=Pa_Initialize()) != paNoError)
{
ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
return ALC_FALSE;
}
#endif
return ALC_TRUE;
}
typedef struct {
PaStream *stream;
PaStreamParameters params;
ALuint update_size;
RingBuffer *ring;
} pa_data;
static int pa_callback(const void *UNUSED(inputBuffer), void *outputBuffer,
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
{
ALCdevice *device = (ALCdevice*)userData;
aluMixData(device, outputBuffer, framesPerBuffer);
return 0;
}
static int pa_capture_cb(const void *inputBuffer, void *UNUSED(outputBuffer),
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
{
ALCdevice *device = (ALCdevice*)userData;
pa_data *data = (pa_data*)device->ExtraData;
WriteRingBuffer(data->ring, inputBuffer, framesPerBuffer);
return 0;
}
static ALCenum pa_open_playback(ALCdevice *device, const ALCchar *deviceName)
{
pa_data *data;
PaError err;
if(!deviceName)
deviceName = pa_device;
else if(strcmp(deviceName, pa_device) != 0)
return ALC_INVALID_VALUE;
data = (pa_data*)calloc(1, sizeof(pa_data));
data->update_size = device->UpdateSize;
data->params.device = -1;
if(!ConfigValueInt("port", "device", &data->params.device) ||
data->params.device < 0)
data->params.device = Pa_GetDefaultOutputDevice();
data->params.suggestedLatency = (device->UpdateSize*device->NumUpdates) /
(float)device->Frequency;
data->params.hostApiSpecificStreamInfo = NULL;
data->params.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2);
switch(device->FmtType)
{
case DevFmtByte:
data->params.sampleFormat = paInt8;
break;
case DevFmtUByte:
data->params.sampleFormat = paUInt8;
break;
case DevFmtUShort:
/* fall-through */
case DevFmtShort:
data->params.sampleFormat = paInt16;
break;
case DevFmtUInt:
/* fall-through */
case DevFmtInt:
data->params.sampleFormat = paInt32;
break;
case DevFmtFloat:
data->params.sampleFormat = paFloat32;
break;
}
retry_open:
err = Pa_OpenStream(&data->stream, NULL, &data->params, device->Frequency,
device->UpdateSize, paNoFlag, pa_callback, device);
if(err != paNoError)
{
if(data->params.sampleFormat == paFloat32)
{
data->params.sampleFormat = paInt16;
goto retry_open;
}
ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
free(data);
return ALC_INVALID_VALUE;
}
device->ExtraData = data;
al_string_copy_cstr(&device->DeviceName, deviceName);
return ALC_NO_ERROR;
}
static void pa_close_playback(ALCdevice *device)
{
pa_data *data = (pa_data*)device->ExtraData;
PaError err;
err = Pa_CloseStream(data->stream);
if(err != paNoError)
ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
free(data);
device->ExtraData = NULL;
}
static ALCboolean pa_reset_playback(ALCdevice *device)
{
pa_data *data = (pa_data*)device->ExtraData;
const PaStreamInfo *streamInfo;
streamInfo = Pa_GetStreamInfo(data->stream);
device->Frequency = streamInfo->sampleRate;
device->UpdateSize = data->update_size;
if(data->params.sampleFormat == paInt8)
device->FmtType = DevFmtByte;
else if(data->params.sampleFormat == paUInt8)
device->FmtType = DevFmtUByte;
else if(data->params.sampleFormat == paInt16)
device->FmtType = DevFmtShort;
else if(data->params.sampleFormat == paInt32)
device->FmtType = DevFmtInt;
else if(data->params.sampleFormat == paFloat32)
device->FmtType = DevFmtFloat;
else
{
ERR("Unexpected sample format: 0x%lx\n", data->params.sampleFormat);
return ALC_FALSE;
}
if(data->params.channelCount == 2)
device->FmtChans = DevFmtStereo;
else if(data->params.channelCount == 1)
device->FmtChans = DevFmtMono;
else
{
ERR("Unexpected channel count: %u\n", data->params.channelCount);
return ALC_FALSE;
}
SetDefaultChannelOrder(device);
return ALC_TRUE;
}
static ALCboolean pa_start_playback(ALCdevice *device)
{
pa_data *data = (pa_data*)device->ExtraData;
PaError err;
err = Pa_StartStream(data->stream);
if(err != paNoError)
{
ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err));
return ALC_FALSE;
}
return ALC_TRUE;
}
static void pa_stop_playback(ALCdevice *device)
{
pa_data *data = (pa_data*)device->ExtraData;
PaError err;
err = Pa_StopStream(data->stream);
if(err != paNoError)
ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
}
static ALCenum pa_open_capture(ALCdevice *device, const ALCchar *deviceName)
{
ALuint frame_size;
pa_data *data;
PaError err;
if(!deviceName)
deviceName = pa_device;
else if(strcmp(deviceName, pa_device) != 0)
return ALC_INVALID_VALUE;
data = (pa_data*)calloc(1, sizeof(pa_data));
if(data == NULL)
return ALC_OUT_OF_MEMORY;
frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
data->ring = CreateRingBuffer(frame_size, device->UpdateSize*device->NumUpdates);
if(data->ring == NULL)
goto error;
data->params.device = -1;
if(!ConfigValueInt("port", "capture", &data->params.device) ||
data->params.device < 0)
data->params.device = Pa_GetDefaultInputDevice();
data->params.suggestedLatency = 0.0f;
data->params.hostApiSpecificStreamInfo = NULL;
switch(device->FmtType)
{
case DevFmtByte:
data->params.sampleFormat = paInt8;
break;
case DevFmtUByte:
data->params.sampleFormat = paUInt8;
break;
case DevFmtShort:
data->params.sampleFormat = paInt16;
break;
case DevFmtInt:
data->params.sampleFormat = paInt32;
break;
case DevFmtFloat:
data->params.sampleFormat = paFloat32;
break;
case DevFmtUInt:
case DevFmtUShort:
ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
goto error;
}
data->params.channelCount = ChannelsFromDevFmt(device->FmtChans);
err = Pa_OpenStream(&data->stream, &data->params, NULL, device->Frequency,
paFramesPerBufferUnspecified, paNoFlag, pa_capture_cb, device);
if(err != paNoError)
{
ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
goto error;
}
al_string_copy_cstr(&device->DeviceName, deviceName);
device->ExtraData = data;
return ALC_NO_ERROR;
error:
DestroyRingBuffer(data->ring);
free(data);
return ALC_INVALID_VALUE;
}
static void pa_close_capture(ALCdevice *device)
{
pa_data *data = (pa_data*)device->ExtraData;
PaError err;
err = Pa_CloseStream(data->stream);
if(err != paNoError)
ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
DestroyRingBuffer(data->ring);
data->ring = NULL;
free(data);
device->ExtraData = NULL;
}
static void pa_start_capture(ALCdevice *device)
{
pa_data *data = device->ExtraData;
PaError err;
err = Pa_StartStream(data->stream);
if(err != paNoError)
ERR("Error starting stream: %s\n", Pa_GetErrorText(err));
}
static void pa_stop_capture(ALCdevice *device)
{
pa_data *data = (pa_data*)device->ExtraData;
PaError err;
err = Pa_StopStream(data->stream);
if(err != paNoError)
ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
}
static ALCenum pa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
{
pa_data *data = device->ExtraData;
ReadRingBuffer(data->ring, buffer, samples);
return ALC_NO_ERROR;
}
static ALCuint pa_available_samples(ALCdevice *device)
{
pa_data *data = device->ExtraData;
return RingBufferSize(data->ring);
}
static const BackendFuncs pa_funcs = {
pa_open_playback,
pa_close_playback,
pa_reset_playback,
pa_start_playback,
pa_stop_playback,
pa_open_capture,
pa_close_capture,
pa_start_capture,
pa_stop_capture,
pa_capture_samples,
pa_available_samples,
ALCdevice_GetLatencyDefault
};
ALCboolean alc_pa_init(BackendFuncs *func_list)
{
if(!pa_load())
return ALC_FALSE;
*func_list = pa_funcs;
return ALC_TRUE;
}
void alc_pa_deinit(void)
{
#ifdef HAVE_DYNLOAD
if(pa_handle)
{
Pa_Terminate();
CloseLib(pa_handle);
pa_handle = NULL;
}
#else
Pa_Terminate();
#endif
}
void alc_pa_probe(enum DevProbe type)
{
switch(type)
{
case ALL_DEVICE_PROBE:
AppendAllDevicesList(pa_device);
break;
case CAPTURE_DEVICE_PROBE:
AppendCaptureDeviceList(pa_device);
break;
}
}
File diff suppressed because it is too large Load Diff
+1161
View File
File diff suppressed because it is too large Load Diff
+295
View File
@@ -0,0 +1,295 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "alMain.h"
#include "alu.h"
#include "threads.h"
#include <sndio.h>
static const ALCchar sndio_device[] = "SndIO Default";
static ALCboolean sndio_load(void)
{
return ALC_TRUE;
}
typedef struct {
struct sio_hdl *sndHandle;
ALvoid *mix_data;
ALsizei data_size;
volatile int killNow;
althrd_t thread;
} sndio_data;
static int sndio_proc(void *ptr)
{
ALCdevice *device = ptr;
sndio_data *data = device->ExtraData;
ALsizei frameSize;
size_t wrote;
SetRTPriority();
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
while(!data->killNow && device->Connected)
{
ALsizei len = data->data_size;
ALubyte *WritePtr = data->mix_data;
aluMixData(device, WritePtr, len/frameSize);
while(len > 0 && !data->killNow)
{
wrote = sio_write(data->sndHandle, WritePtr, len);
if(wrote == 0)
{
ERR("sio_write failed\n");
ALCdevice_Lock(device);
aluHandleDisconnect(device);
ALCdevice_Unlock(device);
break;
}
len -= wrote;
WritePtr += wrote;
}
}
return 0;
}
static ALCenum sndio_open_playback(ALCdevice *device, const ALCchar *deviceName)
{
sndio_data *data;
if(!deviceName)
deviceName = sndio_device;
else if(strcmp(deviceName, sndio_device) != 0)
return ALC_INVALID_VALUE;
data = calloc(1, sizeof(*data));
data->killNow = 0;
data->sndHandle = sio_open(NULL, SIO_PLAY, 0);
if(data->sndHandle == NULL)
{
free(data);
ERR("Could not open device\n");
return ALC_INVALID_VALUE;
}
al_string_copy_cstr(&device->DeviceName, deviceName);
device->ExtraData = data;
return ALC_NO_ERROR;
}
static void sndio_close_playback(ALCdevice *device)
{
sndio_data *data = device->ExtraData;
sio_close(data->sndHandle);
free(data);
device->ExtraData = NULL;
}
static ALCboolean sndio_reset_playback(ALCdevice *device)
{
sndio_data *data = device->ExtraData;
struct sio_par par;
sio_initpar(&par);
par.rate = device->Frequency;
par.pchan = ((device->FmtChans != DevFmtMono) ? 2 : 1);
switch(device->FmtType)
{
case DevFmtByte:
par.bits = 8;
par.sig = 1;
break;
case DevFmtUByte:
par.bits = 8;
par.sig = 0;
break;
case DevFmtFloat:
case DevFmtShort:
par.bits = 16;
par.sig = 1;
break;
case DevFmtUShort:
par.bits = 16;
par.sig = 0;
break;
case DevFmtInt:
par.bits = 32;
par.sig = 1;
break;
case DevFmtUInt:
par.bits = 32;
par.sig = 0;
break;
}
par.le = SIO_LE_NATIVE;
par.round = device->UpdateSize;
par.appbufsz = device->UpdateSize * (device->NumUpdates-1);
if(!par.appbufsz) par.appbufsz = device->UpdateSize;
if(!sio_setpar(data->sndHandle, &par) || !sio_getpar(data->sndHandle, &par))
{
ERR("Failed to set device parameters\n");
return ALC_FALSE;
}
if(par.bits != par.bps*8)
{
ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8);
return ALC_FALSE;
}
device->Frequency = par.rate;
device->FmtChans = ((par.pchan==1) ? DevFmtMono : DevFmtStereo);
if(par.bits == 8 && par.sig == 1)
device->FmtType = DevFmtByte;
else if(par.bits == 8 && par.sig == 0)
device->FmtType = DevFmtUByte;
else if(par.bits == 16 && par.sig == 1)
device->FmtType = DevFmtShort;
else if(par.bits == 16 && par.sig == 0)
device->FmtType = DevFmtUShort;
else if(par.bits == 32 && par.sig == 1)
device->FmtType = DevFmtInt;
else if(par.bits == 32 && par.sig == 0)
device->FmtType = DevFmtUInt;
else
{
ERR("Unhandled sample format: %s %u-bit\n", (par.sig?"signed":"unsigned"), par.bits);
return ALC_FALSE;
}
device->UpdateSize = par.round;
device->NumUpdates = (par.bufsz/par.round) + 1;
SetDefaultChannelOrder(device);
return ALC_TRUE;
}
static ALCboolean sndio_start_playback(ALCdevice *device)
{
sndio_data *data = device->ExtraData;
if(!sio_start(data->sndHandle))
{
ERR("Error starting playback\n");
return ALC_FALSE;
}
data->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
data->mix_data = calloc(1, data->data_size);
data->killNow = 0;
if(althrd_create(&data->thread, sndio_proc, device) != althrd_success)
{
sio_stop(data->sndHandle);
free(data->mix_data);
data->mix_data = NULL;
return ALC_FALSE;
}
return ALC_TRUE;
}
static void sndio_stop_playback(ALCdevice *device)
{
sndio_data *data = device->ExtraData;
int res;
if(data->killNow)
return;
data->killNow = 1;
althrd_join(data->thread, &res);
if(!sio_stop(data->sndHandle))
ERR("Error stopping device\n");
free(data->mix_data);
data->mix_data = NULL;
}
static const BackendFuncs sndio_funcs = {
sndio_open_playback,
sndio_close_playback,
sndio_reset_playback,
sndio_start_playback,
sndio_stop_playback,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
ALCdevice_GetLatencyDefault
};
ALCboolean alc_sndio_init(BackendFuncs *func_list)
{
if(!sndio_load())
return ALC_FALSE;
*func_list = sndio_funcs;
return ALC_TRUE;
}
void alc_sndio_deinit(void)
{
}
void alc_sndio_probe(enum DevProbe type)
{
switch(type)
{
case ALL_DEVICE_PROBE:
AppendAllDevicesList(sndio_device);
break;
case CAPTURE_DEVICE_PROBE:
break;
}
}
+288
View File
@@ -0,0 +1,288 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <unistd.h>
#include <errno.h>
#include <math.h>
#include "alMain.h"
#include "alu.h"
#include "threads.h"
#include "compat.h"
#include <sys/audioio.h>
static const ALCchar solaris_device[] = "Solaris Default";
static const char *solaris_driver = "/dev/audio";
typedef struct {
int fd;
ALubyte *mix_data;
int data_size;
volatile int killNow;
althrd_t thread;
} solaris_data;
static int SolarisProc(void *ptr)
{
ALCdevice *Device = (ALCdevice*)ptr;
solaris_data *data = (solaris_data*)Device->ExtraData;
ALint frameSize;
int wrote;
SetRTPriority();
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
while(!data->killNow && Device->Connected)
{
ALint len = data->data_size;
ALubyte *WritePtr = data->mix_data;
aluMixData(Device, WritePtr, len/frameSize);
while(len > 0 && !data->killNow)
{
wrote = write(data->fd, WritePtr, len);
if(wrote < 0)
{
if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
{
ERR("write failed: %s\n", strerror(errno));
ALCdevice_Lock(Device);
aluHandleDisconnect(Device);
ALCdevice_Unlock(Device);
break;
}
al_nssleep(0, 1000000);
continue;
}
len -= wrote;
WritePtr += wrote;
}
}
return 0;
}
static ALCenum solaris_open_playback(ALCdevice *device, const ALCchar *deviceName)
{
solaris_data *data;
if(!deviceName)
deviceName = solaris_device;
else if(strcmp(deviceName, solaris_device) != 0)
return ALC_INVALID_VALUE;
data = (solaris_data*)calloc(1, sizeof(solaris_data));
data->killNow = 0;
data->fd = open(solaris_driver, O_WRONLY);
if(data->fd == -1)
{
free(data);
ERR("Could not open %s: %s\n", solaris_driver, strerror(errno));
return ALC_INVALID_VALUE;
}
al_string_copy_cstr(&device->DeviceName, deviceName);
device->ExtraData = data;
return ALC_NO_ERROR;
}
static void solaris_close_playback(ALCdevice *device)
{
solaris_data *data = (solaris_data*)device->ExtraData;
close(data->fd);
free(data);
device->ExtraData = NULL;
}
static ALCboolean solaris_reset_playback(ALCdevice *device)
{
solaris_data *data = (solaris_data*)device->ExtraData;
audio_info_t info;
ALuint frameSize;
int numChannels;
AUDIO_INITINFO(&info);
info.play.sample_rate = device->Frequency;
if(device->FmtChans != DevFmtMono)
device->FmtChans = DevFmtStereo;
numChannels = ChannelsFromDevFmt(device->FmtChans);
info.play.channels = numChannels;
switch(device->FmtType)
{
case DevFmtByte:
info.play.precision = 8;
info.play.encoding = AUDIO_ENCODING_LINEAR;
break;
case DevFmtUByte:
info.play.precision = 8;
info.play.encoding = AUDIO_ENCODING_LINEAR8;
break;
case DevFmtUShort:
case DevFmtInt:
case DevFmtUInt:
case DevFmtFloat:
device->FmtType = DevFmtShort;
/* fall-through */
case DevFmtShort:
info.play.precision = 16;
info.play.encoding = AUDIO_ENCODING_LINEAR;
break;
}
frameSize = numChannels * BytesFromDevFmt(device->FmtType);
info.play.buffer_size = device->UpdateSize*device->NumUpdates * frameSize;
if(ioctl(data->fd, AUDIO_SETINFO, &info) < 0)
{
ERR("ioctl failed: %s\n", strerror(errno));
return ALC_FALSE;
}
if(ChannelsFromDevFmt(device->FmtChans) != info.play.channels)
{
ERR("Could not set %d channels, got %d instead\n", ChannelsFromDevFmt(device->FmtChans), info.play.channels);
return ALC_FALSE;
}
if(!((info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR8 && device->FmtType == DevFmtUByte) ||
(info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR && device->FmtType == DevFmtByte) ||
(info.play.precision == 16 && info.play.encoding == AUDIO_ENCODING_LINEAR && device->FmtType == DevFmtShort) ||
(info.play.precision == 32 && info.play.encoding == AUDIO_ENCODING_LINEAR && device->FmtType == DevFmtInt)))
{
ERR("Could not set %s samples, got %d (0x%x)\n", DevFmtTypeString(device->FmtType),
info.play.precision, info.play.encoding);
return ALC_FALSE;
}
device->Frequency = info.play.sample_rate;
device->UpdateSize = (info.play.buffer_size/device->NumUpdates) + 1;
SetDefaultChannelOrder(device);
return ALC_TRUE;
}
static ALCboolean solaris_start_playback(ALCdevice *device)
{
solaris_data *data = (solaris_data*)device->ExtraData;
data->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
data->mix_data = calloc(1, data->data_size);
data->killNow = 0;
if(althrd_create(&data->thread, SolarisProc, device) != althrd_success)
{
free(data->mix_data);
data->mix_data = NULL;
return ALC_FALSE;
}
return ALC_TRUE;
}
static void solaris_stop_playback(ALCdevice *device)
{
solaris_data *data = (solaris_data*)device->ExtraData;
int res;
if(data->killNow)
return;
data->killNow = 1;
althrd_join(data->thread, &res);
if(ioctl(data->fd, AUDIO_DRAIN) < 0)
ERR("Error draining device: %s\n", strerror(errno));
free(data->mix_data);
data->mix_data = NULL;
}
static const BackendFuncs solaris_funcs = {
solaris_open_playback,
solaris_close_playback,
solaris_reset_playback,
solaris_start_playback,
solaris_stop_playback,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
ALCdevice_GetLatencyDefault
};
ALCboolean alc_solaris_init(BackendFuncs *func_list)
{
ConfigValueStr("solaris", "device", &solaris_driver);
*func_list = solaris_funcs;
return ALC_TRUE;
}
void alc_solaris_deinit(void)
{
}
void alc_solaris_probe(enum DevProbe type)
{
switch(type)
{
case ALL_DEVICE_PROBE:
{
#ifdef HAVE_STAT
struct stat buf;
if(stat(solaris_driver, &buf) == 0)
#endif
AppendAllDevicesList(solaris_device);
}
break;
case CAPTURE_DEVICE_PROBE:
break;
}
}
+377
View File
@@ -0,0 +1,377 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <errno.h>
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#include "alMain.h"
#include "alu.h"
#include "threads.h"
#include "compat.h"
typedef struct {
FILE *f;
long DataStart;
ALvoid *buffer;
ALuint size;
volatile int killNow;
althrd_t thread;
} wave_data;
static const ALCchar waveDevice[] = "Wave File Writer";
static const ALubyte SUBTYPE_PCM[] = {
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
0x00, 0x38, 0x9b, 0x71
};
static const ALubyte SUBTYPE_FLOAT[] = {
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
0x00, 0x38, 0x9b, 0x71
};
static const ALuint channel_masks[] = {
0, /* invalid */
0x4, /* Mono */
0x1 | 0x2, /* Stereo */
0, /* 3 channel */
0x1 | 0x2 | 0x10 | 0x20, /* Quad */
0, /* 5 channel */
0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20, /* 5.1 */
0x1 | 0x2 | 0x4 | 0x8 | 0x100 | 0x200 | 0x400, /* 6.1 */
0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20 | 0x200 | 0x400, /* 7.1 */
};
static void fwrite16le(ALushort val, FILE *f)
{
fputc(val&0xff, f);
fputc((val>>8)&0xff, f);
}
static void fwrite32le(ALuint val, FILE *f)
{
fputc(val&0xff, f);
fputc((val>>8)&0xff, f);
fputc((val>>16)&0xff, f);
fputc((val>>24)&0xff, f);
}
static int WaveProc(void *ptr)
{
ALCdevice *device = (ALCdevice*)ptr;
wave_data *data = (wave_data*)device->ExtraData;
struct timespec now, start;
ALint64 avail, done;
ALuint frameSize;
size_t fs;
const long restTime = (long)((ALuint64)device->UpdateSize * 1000000000 /
device->Frequency / 2);
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
done = 0;
if(altimespec_get(&start, AL_TIME_UTC) != AL_TIME_UTC)
{
ERR("Failed to get starting time\n");
return 1;
}
while(!data->killNow && device->Connected)
{
if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC)
{
ERR("Failed to get current time\n");
return 1;
}
avail = (now.tv_sec - start.tv_sec) * device->Frequency;
avail += (ALint64)(now.tv_nsec - start.tv_nsec) * device->Frequency / 1000000000;
if(avail < done)
{
/* Oops, time skipped backwards. Reset the number of samples done
* with one update available since we (likely) just came back from
* sleeping. */
done = avail - device->UpdateSize;
}
if(avail-done < device->UpdateSize)
al_nssleep(0, restTime);
else while(avail-done >= device->UpdateSize)
{
aluMixData(device, data->buffer, device->UpdateSize);
done += device->UpdateSize;
if(!IS_LITTLE_ENDIAN)
{
ALuint bytesize = BytesFromDevFmt(device->FmtType);
ALubyte *bytes = data->buffer;
ALuint i;
if(bytesize == 1)
{
for(i = 0;i < data->size;i++)
fputc(bytes[i], data->f);
}
else if(bytesize == 2)
{
for(i = 0;i < data->size;i++)
fputc(bytes[i^1], data->f);
}
else if(bytesize == 4)
{
for(i = 0;i < data->size;i++)
fputc(bytes[i^3], data->f);
}
}
else
{
fs = fwrite(data->buffer, frameSize, device->UpdateSize,
data->f);
(void)fs;
}
if(ferror(data->f))
{
ERR("Error writing to file\n");
ALCdevice_Lock(device);
aluHandleDisconnect(device);
ALCdevice_Unlock(device);
break;
}
}
}
return 0;
}
static ALCenum wave_open_playback(ALCdevice *device, const ALCchar *deviceName)
{
wave_data *data;
const char *fname;
fname = GetConfigValue("wave", "file", "");
if(!fname[0])
return ALC_INVALID_VALUE;
if(!deviceName)
deviceName = waveDevice;
else if(strcmp(deviceName, waveDevice) != 0)
return ALC_INVALID_VALUE;
data = (wave_data*)calloc(1, sizeof(wave_data));
data->f = al_fopen(fname, "wb");
if(!data->f)
{
free(data);
ERR("Could not open file '%s': %s\n", fname, strerror(errno));
return ALC_INVALID_VALUE;
}
al_string_copy_cstr(&device->DeviceName, deviceName);
device->ExtraData = data;
return ALC_NO_ERROR;
}
static void wave_close_playback(ALCdevice *device)
{
wave_data *data = (wave_data*)device->ExtraData;
fclose(data->f);
free(data);
device->ExtraData = NULL;
}
static ALCboolean wave_reset_playback(ALCdevice *device)
{
wave_data *data = (wave_data*)device->ExtraData;
ALuint channels=0, bits=0;
size_t val;
fseek(data->f, 0, SEEK_SET);
clearerr(data->f);
switch(device->FmtType)
{
case DevFmtByte:
device->FmtType = DevFmtUByte;
break;
case DevFmtUShort:
device->FmtType = DevFmtShort;
break;
case DevFmtUInt:
device->FmtType = DevFmtInt;
break;
case DevFmtUByte:
case DevFmtShort:
case DevFmtInt:
case DevFmtFloat:
break;
}
bits = BytesFromDevFmt(device->FmtType) * 8;
channels = ChannelsFromDevFmt(device->FmtChans);
fprintf(data->f, "RIFF");
fwrite32le(0xFFFFFFFF, data->f); // 'RIFF' header len; filled in at close
fprintf(data->f, "WAVE");
fprintf(data->f, "fmt ");
fwrite32le(40, data->f); // 'fmt ' header len; 40 bytes for EXTENSIBLE
// 16-bit val, format type id (extensible: 0xFFFE)
fwrite16le(0xFFFE, data->f);
// 16-bit val, channel count
fwrite16le(channels, data->f);
// 32-bit val, frequency
fwrite32le(device->Frequency, data->f);
// 32-bit val, bytes per second
fwrite32le(device->Frequency * channels * bits / 8, data->f);
// 16-bit val, frame size
fwrite16le(channels * bits / 8, data->f);
// 16-bit val, bits per sample
fwrite16le(bits, data->f);
// 16-bit val, extra byte count
fwrite16le(22, data->f);
// 16-bit val, valid bits per sample
fwrite16le(bits, data->f);
// 32-bit val, channel mask
fwrite32le(channel_masks[channels], data->f);
// 16 byte GUID, sub-type format
val = fwrite(((bits==32) ? SUBTYPE_FLOAT : SUBTYPE_PCM), 1, 16, data->f);
(void)val;
fprintf(data->f, "data");
fwrite32le(0xFFFFFFFF, data->f); // 'data' header len; filled in at close
if(ferror(data->f))
{
ERR("Error writing header: %s\n", strerror(errno));
return ALC_FALSE;
}
data->DataStart = ftell(data->f);
SetDefaultWFXChannelOrder(device);
return ALC_TRUE;
}
static ALCboolean wave_start_playback(ALCdevice *device)
{
wave_data *data = (wave_data*)device->ExtraData;
data->size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
data->buffer = malloc(data->size);
if(!data->buffer)
{
ERR("Buffer malloc failed\n");
return ALC_FALSE;
}
data->killNow = 0;
if(althrd_create(&data->thread, WaveProc, device) != althrd_success)
{
free(data->buffer);
data->buffer = NULL;
return ALC_FALSE;
}
return ALC_TRUE;
}
static void wave_stop_playback(ALCdevice *device)
{
wave_data *data = (wave_data*)device->ExtraData;
ALuint dataLen;
long size;
int res;
if(data->killNow)
return;
data->killNow = 1;
althrd_join(data->thread, &res);
free(data->buffer);
data->buffer = NULL;
size = ftell(data->f);
if(size > 0)
{
dataLen = size - data->DataStart;
if(fseek(data->f, data->DataStart-4, SEEK_SET) == 0)
fwrite32le(dataLen, data->f); // 'data' header len
if(fseek(data->f, 4, SEEK_SET) == 0)
fwrite32le(size-8, data->f); // 'WAVE' header len
}
}
static const BackendFuncs wave_funcs = {
wave_open_playback,
wave_close_playback,
wave_reset_playback,
wave_start_playback,
wave_stop_playback,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
ALCdevice_GetLatencyDefault
};
ALCboolean alc_wave_init(BackendFuncs *func_list)
{
*func_list = wave_funcs;
return ALC_TRUE;
}
void alc_wave_deinit(void)
{
}
void alc_wave_probe(enum DevProbe type)
{
if(!ConfigValueExists("wave", "file"))
return;
switch(type)
{
case ALL_DEVICE_PROBE:
AppendAllDevicesList(waveDevice);
break;
case CAPTURE_DEVICE_PROBE:
break;
}
}
+716
View File
@@ -0,0 +1,716 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <windows.h>
#include <mmsystem.h>
#include "alMain.h"
#include "alu.h"
#include "threads.h"
#ifndef WAVE_FORMAT_IEEE_FLOAT
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
#endif
typedef struct {
// MMSYSTEM Device
volatile ALboolean killNow;
althrd_t thread;
RefCount WaveBuffersCommitted;
WAVEHDR WaveBuffer[4];
union {
HWAVEIN In;
HWAVEOUT Out;
} WaveHandle;
WAVEFORMATEX Format;
RingBuffer *Ring;
} WinMMData;
TYPEDEF_VECTOR(al_string, vector_al_string)
static vector_al_string PlaybackDevices;
static vector_al_string CaptureDevices;
static void clear_devlist(vector_al_string *list)
{
VECTOR_FOR_EACH(al_string, *list, al_string_deinit);
VECTOR_RESIZE(*list, 0);
}
static void ProbePlaybackDevices(void)
{
al_string *iter, *end;
ALuint numdevs;
ALuint i;
clear_devlist(&PlaybackDevices);
numdevs = waveOutGetNumDevs();
VECTOR_RESERVE(PlaybackDevices, numdevs);
for(i = 0;i < numdevs;i++)
{
WAVEOUTCAPSW WaveCaps;
al_string dname;
AL_STRING_INIT(dname);
if(waveOutGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
{
ALuint count = 0;
do {
al_string_copy_wcstr(&dname, WaveCaps.szPname);
if(count != 0)
{
char str[64];
snprintf(str, sizeof(str), " #%d", count+1);
al_string_append_cstr(&dname, str);
}
count++;
iter = VECTOR_ITER_BEGIN(PlaybackDevices);
end = VECTOR_ITER_END(PlaybackDevices);
for(;iter != end;iter++)
{
if(al_string_cmp(*iter, dname) == 0)
break;
}
} while(iter != end);
TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i);
}
VECTOR_PUSH_BACK(PlaybackDevices, dname);
}
}
static void ProbeCaptureDevices(void)
{
al_string *iter, *end;
ALuint numdevs;
ALuint i;
clear_devlist(&CaptureDevices);
numdevs = waveInGetNumDevs();
VECTOR_RESERVE(CaptureDevices, numdevs);
for(i = 0;i < numdevs;i++)
{
WAVEINCAPSW WaveCaps;
al_string dname;
AL_STRING_INIT(dname);
if(waveInGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
{
ALuint count = 0;
do {
al_string_copy_wcstr(&dname, WaveCaps.szPname);
if(count != 0)
{
char str[64];
snprintf(str, sizeof(str), " #%d", count+1);
al_string_append_cstr(&dname, str);
}
count++;
iter = VECTOR_ITER_BEGIN(CaptureDevices);
end = VECTOR_ITER_END(CaptureDevices);
for(;iter != end;iter++)
{
if(al_string_cmp(*iter, dname) == 0)
break;
}
} while(iter != end);
TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i);
}
VECTOR_PUSH_BACK(CaptureDevices, dname);
}
}
/*
WaveOutProc
Posts a message to 'PlaybackThreadProc' everytime a WaveOut Buffer is completed and
returns to the application (for more data)
*/
static void CALLBACK WaveOutProc(HWAVEOUT UNUSED(device), UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR UNUSED(param2))
{
ALCdevice *Device = (ALCdevice*)instance;
WinMMData *data = Device->ExtraData;
if(msg != WOM_DONE)
return;
DecrementRef(&data->WaveBuffersCommitted);
PostThreadMessage(data->thread, msg, 0, param1);
}
FORCE_ALIGN static int PlaybackThreadProc(void *arg)
{
ALCdevice *Device = (ALCdevice*)arg;
WinMMData *data = Device->ExtraData;
WAVEHDR *WaveHdr;
MSG msg;
SetRTPriority();
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
while(GetMessage(&msg, NULL, 0, 0))
{
if(msg.message != WOM_DONE)
continue;
if(data->killNow)
{
if(ReadRef(&data->WaveBuffersCommitted) == 0)
break;
continue;
}
WaveHdr = ((WAVEHDR*)msg.lParam);
aluMixData(Device, WaveHdr->lpData, WaveHdr->dwBufferLength /
data->Format.nBlockAlign);
// Send buffer back to play more data
waveOutWrite(data->WaveHandle.Out, WaveHdr, sizeof(WAVEHDR));
IncrementRef(&data->WaveBuffersCommitted);
}
return 0;
}
/*
WaveInProc
Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and
returns to the application (with more data)
*/
static void CALLBACK WaveInProc(HWAVEIN UNUSED(device), UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR UNUSED(param2))
{
ALCdevice *Device = (ALCdevice*)instance;
WinMMData *data = Device->ExtraData;
if(msg != WIM_DATA)
return;
DecrementRef(&data->WaveBuffersCommitted);
PostThreadMessage(data->thread, msg, 0, param1);
}
static int CaptureThreadProc(void *arg)
{
ALCdevice *Device = (ALCdevice*)arg;
WinMMData *data = Device->ExtraData;
WAVEHDR *WaveHdr;
MSG msg;
althrd_setname(althrd_current(), "alsoft-record");
while(GetMessage(&msg, NULL, 0, 0))
{
if(msg.message != WIM_DATA)
continue;
/* Don't wait for other buffers to finish before quitting. We're
* closing so we don't need them. */
if(data->killNow)
break;
WaveHdr = ((WAVEHDR*)msg.lParam);
WriteRingBuffer(data->Ring, (ALubyte*)WaveHdr->lpData,
WaveHdr->dwBytesRecorded/data->Format.nBlockAlign);
// Send buffer back to capture more data
waveInAddBuffer(data->WaveHandle.In, WaveHdr, sizeof(WAVEHDR));
IncrementRef(&data->WaveBuffersCommitted);
}
return 0;
}
static ALCenum WinMMOpenPlayback(ALCdevice *Device, const ALCchar *deviceName)
{
WinMMData *data = NULL;
const al_string *iter, *end;
UINT DeviceID;
MMRESULT res;
if(VECTOR_SIZE(PlaybackDevices) == 0)
ProbePlaybackDevices();
// Find the Device ID matching the deviceName if valid
iter = VECTOR_ITER_BEGIN(PlaybackDevices);
end = VECTOR_ITER_END(PlaybackDevices);
for(;iter != end;iter++)
{
if(!al_string_empty(*iter) &&
(!deviceName || al_string_cmp_cstr(*iter, deviceName) == 0))
{
DeviceID = (UINT)(iter - VECTOR_ITER_BEGIN(PlaybackDevices));
break;
}
}
if(iter == end)
return ALC_INVALID_VALUE;
data = calloc(1, sizeof(*data));
if(!data)
return ALC_OUT_OF_MEMORY;
Device->ExtraData = data;
retry_open:
memset(&data->Format, 0, sizeof(WAVEFORMATEX));
if(Device->FmtType == DevFmtFloat)
{
data->Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
data->Format.wBitsPerSample = 32;
}
else
{
data->Format.wFormatTag = WAVE_FORMAT_PCM;
if(Device->FmtType == DevFmtUByte || Device->FmtType == DevFmtByte)
data->Format.wBitsPerSample = 8;
else
data->Format.wBitsPerSample = 16;
}
data->Format.nChannels = ((Device->FmtChans == DevFmtMono) ? 1 : 2);
data->Format.nBlockAlign = data->Format.wBitsPerSample *
data->Format.nChannels / 8;
data->Format.nSamplesPerSec = Device->Frequency;
data->Format.nAvgBytesPerSec = data->Format.nSamplesPerSec *
data->Format.nBlockAlign;
data->Format.cbSize = 0;
if((res=waveOutOpen(&data->WaveHandle.Out, DeviceID, &data->Format, (DWORD_PTR)&WaveOutProc, (DWORD_PTR)Device, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
{
if(Device->FmtType == DevFmtFloat)
{
Device->FmtType = DevFmtShort;
goto retry_open;
}
ERR("waveOutOpen failed: %u\n", res);
goto failure;
}
al_string_copy(&Device->DeviceName, VECTOR_ELEM(PlaybackDevices, DeviceID));
return ALC_NO_ERROR;
failure:
if(data->WaveHandle.Out)
waveOutClose(data->WaveHandle.Out);
free(data);
Device->ExtraData = NULL;
return ALC_INVALID_VALUE;
}
static void WinMMClosePlayback(ALCdevice *device)
{
WinMMData *data = (WinMMData*)device->ExtraData;
// Close the Wave device
waveOutClose(data->WaveHandle.Out);
data->WaveHandle.Out = 0;
free(data);
device->ExtraData = NULL;
}
static ALCboolean WinMMResetPlayback(ALCdevice *device)
{
WinMMData *data = (WinMMData*)device->ExtraData;
device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize *
data->Format.nSamplesPerSec /
device->Frequency);
device->UpdateSize = (device->UpdateSize*device->NumUpdates + 3) / 4;
device->NumUpdates = 4;
device->Frequency = data->Format.nSamplesPerSec;
if(data->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
{
if(data->Format.wBitsPerSample == 32)
device->FmtType = DevFmtFloat;
else
{
ERR("Unhandled IEEE float sample depth: %d\n", data->Format.wBitsPerSample);
return ALC_FALSE;
}
}
else if(data->Format.wFormatTag == WAVE_FORMAT_PCM)
{
if(data->Format.wBitsPerSample == 16)
device->FmtType = DevFmtShort;
else if(data->Format.wBitsPerSample == 8)
device->FmtType = DevFmtUByte;
else
{
ERR("Unhandled PCM sample depth: %d\n", data->Format.wBitsPerSample);
return ALC_FALSE;
}
}
else
{
ERR("Unhandled format tag: 0x%04x\n", data->Format.wFormatTag);
return ALC_FALSE;
}
if(data->Format.nChannels == 2)
device->FmtChans = DevFmtStereo;
else if(data->Format.nChannels == 1)
device->FmtChans = DevFmtMono;
else
{
ERR("Unhandled channel count: %d\n", data->Format.nChannels);
return ALC_FALSE;
}
SetDefaultWFXChannelOrder(device);
return ALC_TRUE;
}
static ALCboolean WinMMStartPlayback(ALCdevice *device)
{
WinMMData *data = (WinMMData*)device->ExtraData;
ALbyte *BufferData;
ALint BufferSize;
ALuint i;
data->killNow = AL_FALSE;
if(althrd_create(&data->thread, PlaybackThreadProc, device) != althrd_success)
return ALC_FALSE;
InitRef(&data->WaveBuffersCommitted, 0);
// Create 4 Buffers
BufferSize = device->UpdateSize*device->NumUpdates / 4;
BufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
BufferData = calloc(4, BufferSize);
for(i = 0;i < 4;i++)
{
memset(&data->WaveBuffer[i], 0, sizeof(WAVEHDR));
data->WaveBuffer[i].dwBufferLength = BufferSize;
data->WaveBuffer[i].lpData = ((i==0) ? (CHAR*)BufferData :
(data->WaveBuffer[i-1].lpData +
data->WaveBuffer[i-1].dwBufferLength));
waveOutPrepareHeader(data->WaveHandle.Out, &data->WaveBuffer[i], sizeof(WAVEHDR));
waveOutWrite(data->WaveHandle.Out, &data->WaveBuffer[i], sizeof(WAVEHDR));
IncrementRef(&data->WaveBuffersCommitted);
}
return ALC_TRUE;
}
static void WinMMStopPlayback(ALCdevice *device)
{
WinMMData *data = (WinMMData*)device->ExtraData;
void *buffer = NULL;
int i;
if(data->killNow)
return;
// Set flag to stop processing headers
data->killNow = AL_TRUE;
althrd_join(data->thread, &i);
// Release the wave buffers
for(i = 0;i < 4;i++)
{
waveOutUnprepareHeader(data->WaveHandle.Out, &data->WaveBuffer[i], sizeof(WAVEHDR));
if(i == 0) buffer = data->WaveBuffer[i].lpData;
data->WaveBuffer[i].lpData = NULL;
}
free(buffer);
}
static ALCenum WinMMOpenCapture(ALCdevice *Device, const ALCchar *deviceName)
{
const al_string *iter, *end;
ALbyte *BufferData = NULL;
DWORD CapturedDataSize;
WinMMData *data = NULL;
ALint BufferSize;
UINT DeviceID;
MMRESULT res;
ALuint i;
if(VECTOR_SIZE(CaptureDevices) == 0)
ProbeCaptureDevices();
// Find the Device ID matching the deviceName if valid
iter = VECTOR_ITER_BEGIN(CaptureDevices);
end = VECTOR_ITER_END(CaptureDevices);
for(;iter != end;iter++)
{
if(!al_string_empty(*iter) &&
(!deviceName || al_string_cmp_cstr(*iter, deviceName) == 0))
{
DeviceID = (UINT)(iter - VECTOR_ITER_BEGIN(CaptureDevices));
break;
}
}
if(iter == end)
return ALC_INVALID_VALUE;
switch(Device->FmtChans)
{
case DevFmtMono:
case DevFmtStereo:
break;
case DevFmtQuad:
case DevFmtX51:
case DevFmtX51Side:
case DevFmtX61:
case DevFmtX71:
return ALC_INVALID_ENUM;
}
switch(Device->FmtType)
{
case DevFmtUByte:
case DevFmtShort:
case DevFmtInt:
case DevFmtFloat:
break;
case DevFmtByte:
case DevFmtUShort:
case DevFmtUInt:
return ALC_INVALID_ENUM;
}
data = calloc(1, sizeof(*data));
if(!data)
return ALC_OUT_OF_MEMORY;
Device->ExtraData = data;
memset(&data->Format, 0, sizeof(WAVEFORMATEX));
data->Format.wFormatTag = ((Device->FmtType == DevFmtFloat) ?
WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM);
data->Format.nChannels = ChannelsFromDevFmt(Device->FmtChans);
data->Format.wBitsPerSample = BytesFromDevFmt(Device->FmtType) * 8;
data->Format.nBlockAlign = data->Format.wBitsPerSample *
data->Format.nChannels / 8;
data->Format.nSamplesPerSec = Device->Frequency;
data->Format.nAvgBytesPerSec = data->Format.nSamplesPerSec *
data->Format.nBlockAlign;
data->Format.cbSize = 0;
if((res=waveInOpen(&data->WaveHandle.In, DeviceID, &data->Format, (DWORD_PTR)&WaveInProc, (DWORD_PTR)Device, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
{
ERR("waveInOpen failed: %u\n", res);
goto failure;
}
// Allocate circular memory buffer for the captured audio
CapturedDataSize = Device->UpdateSize*Device->NumUpdates;
// Make sure circular buffer is at least 100ms in size
if(CapturedDataSize < (data->Format.nSamplesPerSec / 10))
CapturedDataSize = data->Format.nSamplesPerSec / 10;
data->Ring = CreateRingBuffer(data->Format.nBlockAlign, CapturedDataSize);
if(!data->Ring)
goto failure;
InitRef(&data->WaveBuffersCommitted, 0);
// Create 4 Buffers of 50ms each
BufferSize = data->Format.nAvgBytesPerSec / 20;
BufferSize -= (BufferSize % data->Format.nBlockAlign);
BufferData = calloc(4, BufferSize);
if(!BufferData)
goto failure;
for(i = 0;i < 4;i++)
{
memset(&data->WaveBuffer[i], 0, sizeof(WAVEHDR));
data->WaveBuffer[i].dwBufferLength = BufferSize;
data->WaveBuffer[i].lpData = ((i==0) ? (CHAR*)BufferData :
(data->WaveBuffer[i-1].lpData +
data->WaveBuffer[i-1].dwBufferLength));
data->WaveBuffer[i].dwFlags = 0;
data->WaveBuffer[i].dwLoops = 0;
waveInPrepareHeader(data->WaveHandle.In, &data->WaveBuffer[i], sizeof(WAVEHDR));
waveInAddBuffer(data->WaveHandle.In, &data->WaveBuffer[i], sizeof(WAVEHDR));
IncrementRef(&data->WaveBuffersCommitted);
}
if(althrd_create(&data->thread, CaptureThreadProc, Device) != althrd_success)
goto failure;
al_string_copy(&Device->DeviceName, VECTOR_ELEM(CaptureDevices, DeviceID));
return ALC_NO_ERROR;
failure:
if(BufferData)
{
for(i = 0;i < 4;i++)
waveInUnprepareHeader(data->WaveHandle.In, &data->WaveBuffer[i], sizeof(WAVEHDR));
free(BufferData);
}
if(data->Ring)
DestroyRingBuffer(data->Ring);
if(data->WaveHandle.In)
waveInClose(data->WaveHandle.In);
free(data);
Device->ExtraData = NULL;
return ALC_INVALID_VALUE;
}
static void WinMMCloseCapture(ALCdevice *Device)
{
WinMMData *data = (WinMMData*)Device->ExtraData;
void *buffer = NULL;
int i;
/* Tell the processing thread to quit and wait for it to do so. */
data->killNow = AL_TRUE;
PostThreadMessage(data->thread, WM_QUIT, 0, 0);
althrd_join(data->thread, &i);
/* Make sure capture is stopped and all pending buffers are flushed. */
waveInReset(data->WaveHandle.In);
// Release the wave buffers
for(i = 0;i < 4;i++)
{
waveInUnprepareHeader(data->WaveHandle.In, &data->WaveBuffer[i], sizeof(WAVEHDR));
if(i == 0) buffer = data->WaveBuffer[i].lpData;
data->WaveBuffer[i].lpData = NULL;
}
free(buffer);
DestroyRingBuffer(data->Ring);
data->Ring = NULL;
// Close the Wave device
waveInClose(data->WaveHandle.In);
data->WaveHandle.In = 0;
free(data);
Device->ExtraData = NULL;
}
static void WinMMStartCapture(ALCdevice *Device)
{
WinMMData *data = (WinMMData*)Device->ExtraData;
waveInStart(data->WaveHandle.In);
}
static void WinMMStopCapture(ALCdevice *Device)
{
WinMMData *data = (WinMMData*)Device->ExtraData;
waveInStop(data->WaveHandle.In);
}
static ALCenum WinMMCaptureSamples(ALCdevice *Device, ALCvoid *Buffer, ALCuint Samples)
{
WinMMData *data = (WinMMData*)Device->ExtraData;
ReadRingBuffer(data->Ring, Buffer, Samples);
return ALC_NO_ERROR;
}
static ALCuint WinMMAvailableSamples(ALCdevice *Device)
{
WinMMData *data = (WinMMData*)Device->ExtraData;
return RingBufferSize(data->Ring);
}
static inline void AppendAllDevicesList2(const al_string *name)
{
if(!al_string_empty(*name))
AppendAllDevicesList(al_string_get_cstr(*name));
}
static inline void AppendCaptureDeviceList2(const al_string *name)
{
if(!al_string_empty(*name))
AppendCaptureDeviceList(al_string_get_cstr(*name));
}
static const BackendFuncs WinMMFuncs = {
WinMMOpenPlayback,
WinMMClosePlayback,
WinMMResetPlayback,
WinMMStartPlayback,
WinMMStopPlayback,
WinMMOpenCapture,
WinMMCloseCapture,
WinMMStartCapture,
WinMMStopCapture,
WinMMCaptureSamples,
WinMMAvailableSamples,
ALCdevice_GetLatencyDefault
};
ALCboolean alcWinMMInit(BackendFuncs *FuncList)
{
VECTOR_INIT(PlaybackDevices);
VECTOR_INIT(CaptureDevices);
*FuncList = WinMMFuncs;
return ALC_TRUE;
}
void alcWinMMDeinit()
{
clear_devlist(&PlaybackDevices);
VECTOR_DEINIT(PlaybackDevices);
clear_devlist(&CaptureDevices);
VECTOR_DEINIT(CaptureDevices);
}
void alcWinMMProbe(enum DevProbe type)
{
switch(type)
{
case ALL_DEVICE_PROBE:
ProbePlaybackDevices();
VECTOR_FOR_EACH(const al_string, PlaybackDevices, AppendAllDevicesList2);
break;
case CAPTURE_DEVICE_PROBE:
ProbeCaptureDevices();
VECTOR_FOR_EACH(const al_string, CaptureDevices, AppendCaptureDeviceList2);
break;
}
}
+143 -201
View File
@@ -1,201 +1,143 @@
/*-
* Copyright (c) 2005 Boris Mikhaylov
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include <math.h>
#include "bs2b.h"
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
/* Single pole IIR filter.
* O[n] = a0*I[n] + a1*I[n-1] + b1*O[n-1]
*/
/* Lowpass filter */
#define lo_filter(in, out_1) (bs2b->a0_lo*(in) + bs2b->b1_lo*(out_1))
/* Highboost filter */
#define hi_filter(in, in_1, out_1) (bs2b->a0_hi*(in) + bs2b->a1_hi*(in_1) + bs2b->b1_hi*(out_1))
/* Set up all data. */
static void init(struct bs2b *bs2b)
{
double Fc_lo, Fc_hi;
double G_lo, G_hi;
double x;
if ((bs2b->srate > 192000) || (bs2b->srate < 2000))
bs2b->srate = BS2B_DEFAULT_SRATE;
switch(bs2b->level)
{
case BS2B_LOW_CLEVEL: /* Low crossfeed level */
Fc_lo = 360.0;
Fc_hi = 501.0;
G_lo = 0.398107170553497;
G_hi = 0.205671765275719;
break;
case BS2B_MIDDLE_CLEVEL: /* Middle crossfeed level */
Fc_lo = 500.0;
Fc_hi = 711.0;
G_lo = 0.459726988530872;
G_hi = 0.228208484414988;
break;
case BS2B_HIGH_CLEVEL: /* High crossfeed level (virtual speakers are closer to itself) */
Fc_lo = 700.0;
Fc_hi = 1021.0;
G_lo = 0.530884444230988;
G_hi = 0.250105790667544;
break;
case BS2B_LOW_ECLEVEL: /* Low easy crossfeed level */
Fc_lo = 360.0;
Fc_hi = 494.0;
G_lo = 0.316227766016838;
G_hi = 0.168236228897329;
break;
case BS2B_MIDDLE_ECLEVEL: /* Middle easy crossfeed level */
Fc_lo = 500.0;
Fc_hi = 689.0;
G_lo = 0.354813389233575;
G_hi = 0.187169483835901;
break;
default: /* High easy crossfeed level */
bs2b->level = BS2B_HIGH_ECLEVEL;
Fc_lo = 700.0;
Fc_hi = 975.0;
G_lo = 0.398107170553497;
G_hi = 0.205671765275719;
break;
} /* switch */
/* $fc = $Fc / $s;
* $d = 1 / 2 / pi / $fc;
* $x = exp(-1 / $d);
*/
x = exp(-2.0 * M_PI * Fc_lo / bs2b->srate);
bs2b->b1_lo = x;
bs2b->a0_lo = G_lo * (1.0 - x);
x = exp(-2.0 * M_PI * Fc_hi / bs2b->srate);
bs2b->b1_hi = x;
bs2b->a0_hi = 1.0 - G_hi * (1.0 - x);
bs2b->a1_hi = -x;
bs2b->gain = 1.0 / (1.0 - G_hi + G_lo);
} /* init */
/* Exported functions.
* See descriptions in "bs2b.h"
*/
void bs2b_set_level(struct bs2b *bs2b, int level)
{
if(level == bs2b->level)
return;
bs2b->level = level;
init(bs2b);
} /* bs2b_set_level */
int bs2b_get_level(struct bs2b *bs2b)
{
return bs2b->level;
} /* bs2b_get_level */
void bs2b_set_srate(struct bs2b *bs2b, int srate)
{
if (srate == bs2b->srate)
return;
bs2b->srate = srate;
init(bs2b);
} /* bs2b_set_srate */
int bs2b_get_srate(struct bs2b *bs2b)
{
return bs2b->srate;
} /* bs2b_get_srate */
void bs2b_clear(struct bs2b *bs2b)
{
int loopv = sizeof(bs2b->last_sample);
while (loopv)
{
((char *)&bs2b->last_sample)[--loopv] = 0;
}
} /* bs2b_clear */
int bs2b_is_clear(struct bs2b *bs2b)
{
int loopv = sizeof(bs2b->last_sample);
while (loopv)
{
if (((char *)&bs2b->last_sample)[--loopv] != 0)
return 0;
}
return 1;
} /* bs2b_is_clear */
void bs2b_cross_feed(struct bs2b *bs2b, float *sample)
{
/* Lowpass filter */
bs2b->last_sample.lo[0] = lo_filter(sample[0], bs2b->last_sample.lo[0]);
bs2b->last_sample.lo[1] = lo_filter(sample[1], bs2b->last_sample.lo[1]);
/* Highboost filter */
bs2b->last_sample.hi[0] = hi_filter(sample[0], bs2b->last_sample.asis[0], bs2b->last_sample.hi[0]);
bs2b->last_sample.hi[1] = hi_filter(sample[1], bs2b->last_sample.asis[1], bs2b->last_sample.hi[1]);
bs2b->last_sample.asis[0] = sample[0];
bs2b->last_sample.asis[1] = sample[1];
/* Crossfeed */
sample[0] = bs2b->last_sample.hi[0] + bs2b->last_sample.lo[1];
sample[1] = bs2b->last_sample.hi[1] + bs2b->last_sample.lo[0];
/* Bass boost cause allpass attenuation */
sample[0] *= bs2b->gain;
sample[1] *= bs2b->gain;
/* Clipping of overloaded samples */
#if 0
if (sample[0] > 1.0)
sample[0] = 1.0;
if (sample[0] < -1.0)
sample[0] = -1.0;
if (sample[1] > 1.0)
sample[1] = 1.0;
if (sample[1] < -1.0)
sample[1] = -1.0;
#endif
} /* bs2b_cross_feed */
/*-
* Copyright (c) 2005 Boris Mikhaylov
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include <math.h>
#include <string.h>
#include "bs2b.h"
#include "alu.h"
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
/* Set up all data. */
static void init(struct bs2b *bs2b)
{
float Fc_lo, Fc_hi;
float G_lo, G_hi;
float x, g;
bs2b->srate = clampi(bs2b->srate, 2000, 192000);
switch(bs2b->level)
{
case BS2B_LOW_CLEVEL: /* Low crossfeed level */
Fc_lo = 360.0f;
Fc_hi = 501.0f;
G_lo = 0.398107170553497f;
G_hi = 0.205671765275719f;
break;
case BS2B_MIDDLE_CLEVEL: /* Middle crossfeed level */
Fc_lo = 500.0f;
Fc_hi = 711.0f;
G_lo = 0.459726988530872f;
G_hi = 0.228208484414988f;
break;
case BS2B_HIGH_CLEVEL: /* High crossfeed level (virtual speakers are closer to itself) */
Fc_lo = 700.0f;
Fc_hi = 1021.0f;
G_lo = 0.530884444230988f;
G_hi = 0.250105790667544f;
break;
case BS2B_LOW_ECLEVEL: /* Low easy crossfeed level */
Fc_lo = 360.0f;
Fc_hi = 494.0f;
G_lo = 0.316227766016838f;
G_hi = 0.168236228897329f;
break;
case BS2B_MIDDLE_ECLEVEL: /* Middle easy crossfeed level */
Fc_lo = 500.0f;
Fc_hi = 689.0f;
G_lo = 0.354813389233575f;
G_hi = 0.187169483835901f;
break;
default: /* High easy crossfeed level */
bs2b->level = BS2B_HIGH_ECLEVEL;
Fc_lo = 700.0f;
Fc_hi = 975.0f;
G_lo = 0.398107170553497f;
G_hi = 0.205671765275719f;
break;
} /* switch */
g = 1.0f / (1.0f - G_hi + G_lo);
/* $fc = $Fc / $s;
* $d = 1 / 2 / pi / $fc;
* $x = exp(-1 / $d);
*/
x = expf(-2.0f * F_PI * Fc_lo / bs2b->srate);
bs2b->b1_lo = x;
bs2b->a0_lo = G_lo * (1.0f - x) * g;
x = expf(-2.0f * F_PI * Fc_hi / bs2b->srate);
bs2b->b1_hi = x;
bs2b->a0_hi = (1.0f - G_hi * (1.0f - x)) * g;
bs2b->a1_hi = -x * g;
} /* init */
/* Exported functions.
* See descriptions in "bs2b.h"
*/
void bs2b_set_level(struct bs2b *bs2b, int level)
{
if(level == bs2b->level)
return;
bs2b->level = level;
init(bs2b);
} /* bs2b_set_level */
int bs2b_get_level(struct bs2b *bs2b)
{
return bs2b->level;
} /* bs2b_get_level */
void bs2b_set_srate(struct bs2b *bs2b, int srate)
{
if (srate == bs2b->srate)
return;
bs2b->srate = srate;
init(bs2b);
} /* bs2b_set_srate */
int bs2b_get_srate(struct bs2b *bs2b)
{
return bs2b->srate;
} /* bs2b_get_srate */
void bs2b_clear(struct bs2b *bs2b)
{
memset(&bs2b->last_sample, 0, sizeof(bs2b->last_sample));
} /* bs2b_clear */
extern inline void bs2b_cross_feed(struct bs2b *bs2b, float *restrict samples);
+32
View File
@@ -0,0 +1,32 @@
#ifndef AL_COMPAT_H
#define AL_COMPAT_H
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
WCHAR *strdupW(const WCHAR *str);
/* Opens a file with standard I/O. The filename is expected to be UTF-8. */
FILE *al_fopen(const char *fname, const char *mode);
#define HAVE_DYNLOAD 1
#else
#define al_fopen fopen
#if defined(HAVE_DLFCN_H) && !defined(IN_IDE_PARSER)
#define HAVE_DYNLOAD 1
#endif
#endif
#ifdef HAVE_DYNLOAD
void *LoadLib(const char *name);
void CloseLib(void *handle);
void *GetSymbol(void *handle, const char *name);
#endif
#endif /* AL_COMPAT_H */
-572
View File
@@ -1,572 +0,0 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#define _WIN32_WINNT 0x0500
#define INITGUID
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <dsound.h>
#include <mmreg.h>
#ifndef _WAVEFORMATEXTENSIBLE_
#include <ks.h>
#include <ksmedia.h>
#endif
#include "alMain.h"
#include "AL/al.h"
#include "AL/alc.h"
#ifndef DSSPEAKER_5POINT1
#define DSSPEAKER_5POINT1 6
#endif
#ifndef DSSPEAKER_7POINT1
#define DSSPEAKER_7POINT1 7
#endif
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
static void *ds_handle;
static HRESULT (WINAPI *pDirectSoundCreate)(LPCGUID pcGuidDevice, LPDIRECTSOUND *ppDS, LPUNKNOWN pUnkOuter);
static HRESULT (WINAPI *pDirectSoundEnumerateA)(LPDSENUMCALLBACKA pDSEnumCallback, LPVOID pContext);
typedef struct {
// DirectSound Playback Device
LPDIRECTSOUND lpDS;
LPDIRECTSOUNDBUFFER DSpbuffer;
LPDIRECTSOUNDBUFFER DSsbuffer;
volatile int killNow;
ALvoid *thread;
} DSoundData;
typedef struct {
ALCchar *name;
GUID guid;
} DevMap;
static const ALCchar dsDevice[] = "DirectSound Software";
static DevMap *DeviceList;
static ALuint NumDevices;
static volatile ALuint load_count;
void DSoundLoad(void)
{
if(load_count == 0)
{
#ifdef _WIN32
ds_handle = LoadLibraryA("dsound.dll");
if(ds_handle == NULL)
{
AL_PRINT("Failed to load dsound.dll\n");
return;
}
#define LOAD_FUNC(f) do { \
p##f = (void*)GetProcAddress((HMODULE)ds_handle, #f); \
if(p##f == NULL) \
{ \
FreeLibrary(ds_handle); \
ds_handle = NULL; \
AL_PRINT("Could not load %s from dsound.dll\n", #f); \
return; \
} \
} while(0)
#else
ds_handle = (void*)0xDEADBEEF;
#define LOAD_FUNC(f) p##f = f
#endif
LOAD_FUNC(DirectSoundCreate);
LOAD_FUNC(DirectSoundEnumerateA);
#undef LOAD_FUNC
}
++load_count;
}
void DSoundUnload(void)
{
if(load_count == 0 || --load_count > 0)
return;
#ifdef _WIN32
FreeLibrary(ds_handle);
#endif
ds_handle = NULL;
}
static ALuint DSoundProc(ALvoid *ptr)
{
ALCdevice *pDevice = (ALCdevice*)ptr;
DSoundData *pData = (DSoundData*)pDevice->ExtraData;
DSBCAPS DSBCaps;
DWORD LastCursor = 0;
DWORD PlayCursor;
VOID *WritePtr1, *WritePtr2;
DWORD WriteCnt1, WriteCnt2;
DWORD FrameSize;
DWORD FragSize;
DWORD avail;
HRESULT err;
memset(&DSBCaps, 0, sizeof(DSBCaps));
DSBCaps.dwSize = sizeof(DSBCaps);
err = IDirectSoundBuffer_GetCaps(pData->DSsbuffer, &DSBCaps);
if(FAILED(err))
{
AL_PRINT("Failed to get buffer caps: 0x%lx\n", err);
aluHandleDisconnect(pDevice);
return 1;
}
FrameSize = aluChannelsFromFormat(pDevice->Format) *
aluBytesFromFormat(pDevice->Format);
FragSize = pDevice->UpdateSize * FrameSize;
IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &LastCursor, NULL);
while(!pData->killNow)
{
// Get current play and write cursors
IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &PlayCursor, NULL);
avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
if(avail < FragSize)
{
Sleep(1);
continue;
}
avail -= avail%FragSize;
// Lock output buffer
WriteCnt1 = 0;
WriteCnt2 = 0;
err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
// If the buffer is lost, restore it, play and lock
if(err == DSERR_BUFFERLOST)
{
err = IDirectSoundBuffer_Restore(pData->DSsbuffer);
if(SUCCEEDED(err))
err = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
if(SUCCEEDED(err))
err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
}
// Successfully locked the output buffer
if(SUCCEEDED(err))
{
// If we have an active context, mix data directly into output buffer otherwise fill with silence
aluMixData(pDevice, WritePtr1, WriteCnt1/FrameSize);
aluMixData(pDevice, WritePtr2, WriteCnt2/FrameSize);
// Unlock output buffer only when successfully locked
IDirectSoundBuffer_Unlock(pData->DSsbuffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
}
else
AL_PRINT("Buffer lock error: %#lx\n", err);
// Update old write cursor location
LastCursor += WriteCnt1+WriteCnt2;
LastCursor %= DSBCaps.dwBufferBytes;
}
return 0;
}
static ALCboolean DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
{
DSoundData *pData = NULL;
LPGUID guid = NULL;
HRESULT hr;
if(!deviceName)
deviceName = dsDevice;
else if(strcmp(deviceName, dsDevice) != 0)
{
ALuint i;
for(i = 0;i < NumDevices;i++)
{
if(strcmp(deviceName, DeviceList[i].name) == 0)
{
guid = &DeviceList[i].guid;
break;
}
}
if(i == NumDevices)
return ALC_FALSE;
}
DSoundLoad();
if(ds_handle == NULL)
return ALC_FALSE;
//Initialise requested device
pData = calloc(1, sizeof(DSoundData));
if(!pData)
{
alcSetError(ALC_OUT_OF_MEMORY);
DSoundUnload();
return ALC_FALSE;
}
//DirectSound Init code
hr = pDirectSoundCreate(guid, &pData->lpDS, NULL);
if(SUCCEEDED(hr))
hr = IDirectSound_SetCooperativeLevel(pData->lpDS, GetForegroundWindow(), DSSCL_PRIORITY);
if(FAILED(hr))
{
if(pData->lpDS)
IDirectSound_Release(pData->lpDS);
free(pData);
DSoundUnload();
return ALC_FALSE;
}
device->szDeviceName = strdup(deviceName);
device->ExtraData = pData;
return ALC_TRUE;
}
static void DSoundClosePlayback(ALCdevice *device)
{
DSoundData *pData = device->ExtraData;
IDirectSound_Release(pData->lpDS);
free(pData);
device->ExtraData = NULL;
DSoundUnload();
}
static ALCboolean DSoundResetPlayback(ALCdevice *device)
{
DSoundData *pData = (DSoundData*)device->ExtraData;
DSBUFFERDESC DSBDescription;
WAVEFORMATEXTENSIBLE OutputType;
DWORD frameSize = 0;
ALenum format = 0;
DWORD speakers;
HRESULT hr;
memset(&OutputType, 0, sizeof(OutputType));
hr = IDirectSound_GetSpeakerConfig(pData->lpDS, &speakers);
if(SUCCEEDED(hr) && *(GetConfigValue(NULL, "format", "")) != 0)
{
if(aluChannelsFromFormat(device->Format) == 1)
speakers = DSSPEAKER_COMBINED(DSSPEAKER_MONO, 0);
else if(aluChannelsFromFormat(device->Format) == 2)
speakers = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, 0);
else if(aluChannelsFromFormat(device->Format) == 4)
speakers = DSSPEAKER_COMBINED(DSSPEAKER_QUAD, 0);
else if(aluChannelsFromFormat(device->Format) == 6)
speakers = DSSPEAKER_COMBINED(DSSPEAKER_5POINT1, 0);
else if(aluChannelsFromFormat(device->Format) == 8)
speakers = DSSPEAKER_COMBINED(DSSPEAKER_7POINT1, 0);
else
{
AL_PRINT("Unknown format: 0x%x\n", device->Format);
return ALC_FALSE;
}
}
if(SUCCEEDED(hr))
{
speakers = DSSPEAKER_CONFIG(speakers);
if(speakers == DSSPEAKER_MONO)
{
if(aluBytesFromFormat(device->Format) == 1)
format = AL_FORMAT_MONO8;
else if(aluBytesFromFormat(device->Format) == 2)
format = AL_FORMAT_MONO16;
else if(aluBytesFromFormat(device->Format) == 4)
format = AL_FORMAT_MONO_FLOAT32;
}
else if(speakers == DSSPEAKER_STEREO)
{
if(aluBytesFromFormat(device->Format) == 1)
format = AL_FORMAT_STEREO8;
else if(aluBytesFromFormat(device->Format) == 2)
format = AL_FORMAT_STEREO16;
else if(aluBytesFromFormat(device->Format) == 4)
format = AL_FORMAT_STEREO_FLOAT32;
}
else if(speakers == DSSPEAKER_QUAD)
{
if(aluBytesFromFormat(device->Format) == 1)
format = AL_FORMAT_QUAD8;
else if(aluBytesFromFormat(device->Format) == 2)
format = AL_FORMAT_QUAD16;
else if(aluBytesFromFormat(device->Format) == 4)
format = AL_FORMAT_QUAD32;
OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
SPEAKER_FRONT_RIGHT |
SPEAKER_BACK_LEFT |
SPEAKER_BACK_RIGHT;
}
else if(speakers == DSSPEAKER_5POINT1)
{
if(aluBytesFromFormat(device->Format) == 1)
format = AL_FORMAT_51CHN8;
else if(aluBytesFromFormat(device->Format) == 2)
format = AL_FORMAT_51CHN16;
else if(aluBytesFromFormat(device->Format) == 4)
format = AL_FORMAT_51CHN32;
OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
SPEAKER_FRONT_RIGHT |
SPEAKER_FRONT_CENTER |
SPEAKER_LOW_FREQUENCY |
SPEAKER_BACK_LEFT |
SPEAKER_BACK_RIGHT;
}
else if(speakers == DSSPEAKER_7POINT1)
{
if(aluBytesFromFormat(device->Format) == 1)
format = AL_FORMAT_71CHN8;
else if(aluBytesFromFormat(device->Format) == 2)
format = AL_FORMAT_71CHN16;
else if(aluBytesFromFormat(device->Format) == 4)
format = AL_FORMAT_71CHN32;
OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
SPEAKER_FRONT_RIGHT |
SPEAKER_FRONT_CENTER |
SPEAKER_LOW_FREQUENCY |
SPEAKER_BACK_LEFT |
SPEAKER_BACK_RIGHT |
SPEAKER_SIDE_LEFT |
SPEAKER_SIDE_RIGHT;
}
else
format = device->Format;
frameSize = aluBytesFromFormat(format) * aluChannelsFromFormat(format);
OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
OutputType.Format.nChannels = aluChannelsFromFormat(format);
OutputType.Format.wBitsPerSample = aluBytesFromFormat(format) * 8;
OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
OutputType.Format.nSamplesPerSec = device->Frequency;
OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
OutputType.Format.cbSize = 0;
}
if(OutputType.Format.nChannels > 2 || OutputType.Format.wBitsPerSample > 16)
{
OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
OutputType.Format.cbSize = 22;
if(OutputType.Format.wBitsPerSample == 32)
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
else
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
}
else
{
if(SUCCEEDED(hr))
{
memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
DSBDescription.dwSize=sizeof(DSBUFFERDESC);
DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER;
hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSpbuffer, NULL);
}
if(SUCCEEDED(hr))
hr = IDirectSoundBuffer_SetFormat(pData->DSpbuffer,&OutputType.Format);
}
if(SUCCEEDED(hr))
{
memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
DSBDescription.dwSize=sizeof(DSBUFFERDESC);
DSBDescription.dwFlags=DSBCAPS_GLOBALFOCUS|DSBCAPS_GETCURRENTPOSITION2;
DSBDescription.dwBufferBytes=device->UpdateSize * device->NumUpdates * frameSize;
DSBDescription.lpwfxFormat=&OutputType.Format;
hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSsbuffer, NULL);
}
if(SUCCEEDED(hr))
hr = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
if(SUCCEEDED(hr))
{
device->ExtraData = pData;
pData->thread = StartThread(DSoundProc, device);
if(!pData->thread)
hr = E_FAIL;
}
if(FAILED(hr))
{
if (pData->DSsbuffer)
IDirectSoundBuffer_Release(pData->DSsbuffer);
pData->DSsbuffer = NULL;
if (pData->DSpbuffer)
IDirectSoundBuffer_Release(pData->DSpbuffer);
pData->DSpbuffer = NULL;
return ALC_FALSE;
}
device->Format = format;
return ALC_TRUE;
}
static void DSoundStopPlayback(ALCdevice *device)
{
DSoundData *pData = device->ExtraData;
if(!pData->thread)
return;
pData->killNow = 1;
StopThread(pData->thread);
pData->thread = NULL;
IDirectSoundBuffer_Release(pData->DSsbuffer);
pData->DSsbuffer = NULL;
if (pData->DSpbuffer)
IDirectSoundBuffer_Release(pData->DSpbuffer);
pData->DSpbuffer = NULL;
}
static ALCboolean DSoundOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName)
{
(void)pDevice;
(void)deviceName;
return ALC_FALSE;
}
static void DSoundCloseCapture(ALCdevice *pDevice)
{
(void)pDevice;
}
static void DSoundStartCapture(ALCdevice *pDevice)
{
(void)pDevice;
}
static void DSoundStopCapture(ALCdevice *pDevice)
{
(void)pDevice;
}
static void DSoundCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
{
(void)pDevice;
(void)pBuffer;
(void)lSamples;
}
static ALCuint DSoundAvailableSamples(ALCdevice *pDevice)
{
(void)pDevice;
return 0;
}
BackendFuncs DSoundFuncs = {
DSoundOpenPlayback,
DSoundClosePlayback,
DSoundResetPlayback,
DSoundStopPlayback,
DSoundOpenCapture,
DSoundCloseCapture,
DSoundStartCapture,
DSoundStopCapture,
DSoundCaptureSamples,
DSoundAvailableSamples
};
static BOOL CALLBACK DSoundEnumDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data)
{
(void)data;
(void)drvname;
if(guid)
{
char str[128];
void *temp;
temp = realloc(DeviceList, sizeof(DevMap) * (NumDevices+1));
if(temp)
{
DeviceList = temp;
snprintf(str, sizeof(str), "DirectSound Software on %s", desc);
AppendAllDeviceList(str);
DeviceList[NumDevices].name = strdup(str);
DeviceList[NumDevices].guid = *guid;
NumDevices++;
}
}
return TRUE;
}
void alcDSoundInit(BackendFuncs *FuncList)
{
*FuncList = DSoundFuncs;
}
void alcDSoundDeinit(void)
{
ALuint i;
for(i = 0;i < NumDevices;++i)
free(DeviceList[i].name);
free(DeviceList);
DeviceList = NULL;
NumDevices = 0;
}
void alcDSoundProbe(int type)
{
DSoundLoad();
if(!ds_handle) return;
if(type == DEVICE_PROBE)
AppendDeviceList(dsDevice);
else if(type == ALL_DEVICE_PROBE)
{
HRESULT hr;
ALuint i;
for(i = 0;i < NumDevices;++i)
free(DeviceList[i].name);
free(DeviceList);
DeviceList = NULL;
NumDevices = 0;
hr = pDirectSoundEnumerateA(DSoundEnumDevices, NULL);
if(FAILED(hr))
AL_PRINT("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);
}
DSoundUnload();
}
+272
View File
@@ -0,0 +1,272 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2013 by Anis A. Hireche, Nasca Octavian Paul
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include <stdlib.h>
#include "config.h"
#include "alu.h"
#include "alFilter.h"
#include "alError.h"
#include "alMain.h"
#include "alAuxEffectSlot.h"
/* Auto-wah is simply a low-pass filter with a cutoff frequency that shifts up
* or down depending on the input signal, and a resonant peak at the cutoff.
*
* Currently, we assume a cutoff frequency range of 500hz (no amplitude) to
* 3khz (peak gain). Peak gain is assumed to be in normalized scale.
*/
typedef struct ALautowahState {
DERIVE_FROM_TYPE(ALeffectState);
/* Effect gains for each channel */
ALfloat Gain[MaxChannels];
/* Effect parameters */
ALfloat AttackRate;
ALfloat ReleaseRate;
ALfloat Resonance;
ALfloat PeakGain;
ALfloat GainCtrl;
ALfloat Frequency;
/* Samples processing */
ALfilterState LowPass;
} ALautowahState;
static ALvoid ALautowahState_Destruct(ALautowahState *UNUSED(state))
{
}
static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *device)
{
state->Frequency = (ALfloat)device->Frequency;
return AL_TRUE;
}
static ALvoid ALautowahState_update(ALautowahState *state, ALCdevice *device, const ALeffectslot *slot)
{
ALfloat attackTime, releaseTime;
ALfloat gain;
attackTime = slot->EffectProps.Autowah.AttackTime * state->Frequency;
releaseTime = slot->EffectProps.Autowah.ReleaseTime * state->Frequency;
state->AttackRate = powf(1.0f/GAIN_SILENCE_THRESHOLD, 1.0f/attackTime);
state->ReleaseRate = powf(GAIN_SILENCE_THRESHOLD/1.0f, 1.0f/releaseTime);
state->PeakGain = slot->EffectProps.Autowah.PeakGain;
state->Resonance = slot->EffectProps.Autowah.Resonance;
gain = sqrtf(1.0f / device->NumChan) * slot->Gain;
SetGains(device, gain, state->Gain);
}
static ALvoid ALautowahState_process(ALautowahState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE])
{
ALuint it, kt;
ALuint base;
for(base = 0;base < SamplesToDo;)
{
ALfloat temps[64];
ALuint td = minu(SamplesToDo-base, 64);
ALfloat gain = state->GainCtrl;
for(it = 0;it < td;it++)
{
ALfloat smp = SamplesIn[it+base];
ALfloat alpha, w0;
ALfloat amplitude;
ALfloat cutoff;
/* Similar to compressor, we get the current amplitude of the
* incoming signal, and attack or release to reach it. */
amplitude = fabsf(smp);
if(amplitude > gain)
gain = minf(gain*state->AttackRate, amplitude);
else if(amplitude < gain)
gain = maxf(gain*state->ReleaseRate, amplitude);
gain = maxf(gain, GAIN_SILENCE_THRESHOLD);
/* FIXME: What range does the filter cover? */
cutoff = lerp(20.0f, 20000.0f, minf(gain/state->PeakGain, 1.0f));
/* The code below is like calling ALfilterState_setParams with
* ALfilterType_LowPass. However, instead of passing a bandwidth,
* we use the resonance property for Q. This also inlines the call.
*/
w0 = F_2PI * cutoff / state->Frequency;
/* FIXME: Resonance controls the resonant peak, or Q. How? Not sure
* that Q = resonance*0.1. */
alpha = sinf(w0) / (2.0f * state->Resonance*0.1f);
state->LowPass.b[0] = (1.0f - cosf(w0)) / 2.0f;
state->LowPass.b[1] = 1.0f - cosf(w0);
state->LowPass.b[2] = (1.0f - cosf(w0)) / 2.0f;
state->LowPass.a[0] = 1.0f + alpha;
state->LowPass.a[1] = -2.0f * cosf(w0);
state->LowPass.a[2] = 1.0f - alpha;
state->LowPass.b[2] /= state->LowPass.a[0];
state->LowPass.b[1] /= state->LowPass.a[0];
state->LowPass.b[0] /= state->LowPass.a[0];
state->LowPass.a[2] /= state->LowPass.a[0];
state->LowPass.a[1] /= state->LowPass.a[0];
state->LowPass.a[0] /= state->LowPass.a[0];
temps[it] = ALfilterState_processSingle(&state->LowPass, smp);
}
state->GainCtrl = gain;
for(kt = 0;kt < MaxChannels;kt++)
{
ALfloat gain = state->Gain[kt];
if(!(gain > GAIN_SILENCE_THRESHOLD))
continue;
for(it = 0;it < td;it++)
SamplesOut[kt][base+it] += gain * temps[it];
}
base += td;
}
}
DECLARE_DEFAULT_ALLOCATORS(ALautowahState)
DEFINE_ALEFFECTSTATE_VTABLE(ALautowahState);
typedef struct ALautowahStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALautowahStateFactory;
static ALeffectState *ALautowahStateFactory_create(ALautowahStateFactory *UNUSED(factory))
{
ALautowahState *state;
state = ALautowahState_New(sizeof(*state));
if(!state) return NULL;
SET_VTABLE2(ALautowahState, ALeffectState, state);
state->AttackRate = 1.0f;
state->ReleaseRate = 1.0f;
state->Resonance = 2.0f;
state->PeakGain = 1.0f;
state->GainCtrl = 1.0f;
ALfilterState_clear(&state->LowPass);
return STATIC_CAST(ALeffectState, state);
}
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALautowahStateFactory);
ALeffectStateFactory *ALautowahStateFactory_getFactory(void)
{
static ALautowahStateFactory AutowahFactory = { { GET_VTABLE2(ALautowahStateFactory, ALeffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &AutowahFactory);
}
void ALautowah_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALautowah_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALautowah_setParami(effect, context, param, vals[0]);
}
void ALautowah_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_AUTOWAH_ATTACK_TIME:
if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Autowah.AttackTime = val;
break;
case AL_AUTOWAH_RELEASE_TIME:
if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Autowah.ReleaseTime = val;
break;
case AL_AUTOWAH_RESONANCE:
if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Autowah.Resonance = val;
break;
case AL_AUTOWAH_PEAK_GAIN:
if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Autowah.PeakGain = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALautowah_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALautowah_setParamf(effect, context, param, vals[0]);
}
void ALautowah_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALautowah_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALautowah_getParami(effect, context, param, vals);
}
void ALautowah_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_AUTOWAH_ATTACK_TIME:
*val = props->Autowah.AttackTime;
break;
case AL_AUTOWAH_RELEASE_TIME:
*val = props->Autowah.ReleaseTime;
break;
case AL_AUTOWAH_RESONANCE:
*val = props->Autowah.Resonance;
break;
case AL_AUTOWAH_PEAK_GAIN:
*val = props->Autowah.PeakGain;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALautowah_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALautowah_getParamf(effect, context, param, vals);
}
DEFINE_ALEFFECT_VTABLE(ALautowah);
+397
View File
@@ -0,0 +1,397 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2013 by Mike Gorchak
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <math.h>
#include <stdlib.h>
#include "alMain.h"
#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
enum ChorusWaveForm {
CWF_Triangle = AL_CHORUS_WAVEFORM_TRIANGLE,
CWF_Sinusoid = AL_CHORUS_WAVEFORM_SINUSOID
};
typedef struct ALchorusState {
DERIVE_FROM_TYPE(ALeffectState);
ALfloat *SampleBuffer[2];
ALuint BufferLength;
ALuint offset;
ALuint lfo_range;
ALfloat lfo_scale;
ALint lfo_disp;
/* Gains for left and right sides */
ALfloat Gain[2][MaxChannels];
/* effect parameters */
enum ChorusWaveForm waveform;
ALint delay;
ALfloat depth;
ALfloat feedback;
} ALchorusState;
static ALvoid ALchorusState_Destruct(ALchorusState *state)
{
free(state->SampleBuffer[0]);
state->SampleBuffer[0] = NULL;
state->SampleBuffer[1] = NULL;
}
static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device)
{
ALuint maxlen;
ALuint it;
maxlen = fastf2u(AL_CHORUS_MAX_DELAY * 3.0f * Device->Frequency) + 1;
maxlen = NextPowerOf2(maxlen);
if(maxlen != state->BufferLength)
{
void *temp;
temp = realloc(state->SampleBuffer[0], maxlen * sizeof(ALfloat) * 2);
if(!temp) return AL_FALSE;
state->SampleBuffer[0] = temp;
state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen;
state->BufferLength = maxlen;
}
for(it = 0;it < state->BufferLength;it++)
{
state->SampleBuffer[0][it] = 0.0f;
state->SampleBuffer[1][it] = 0.0f;
}
return AL_TRUE;
}
static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, const ALeffectslot *Slot)
{
ALfloat frequency = (ALfloat)Device->Frequency;
ALfloat rate;
ALint phase;
switch(Slot->EffectProps.Chorus.Waveform)
{
case AL_CHORUS_WAVEFORM_TRIANGLE:
state->waveform = CWF_Triangle;
break;
case AL_CHORUS_WAVEFORM_SINUSOID:
state->waveform = CWF_Sinusoid;
break;
}
state->depth = Slot->EffectProps.Chorus.Depth;
state->feedback = Slot->EffectProps.Chorus.Feedback;
state->delay = fastf2i(Slot->EffectProps.Chorus.Delay * frequency);
/* Gains for left and right sides */
ComputeAngleGains(Device, atan2f(-1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[0]);
ComputeAngleGains(Device, atan2f(+1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[1]);
phase = Slot->EffectProps.Chorus.Phase;
rate = Slot->EffectProps.Chorus.Rate;
if(!(rate > 0.0f))
{
state->lfo_scale = 0.0f;
state->lfo_range = 1;
state->lfo_disp = 0;
}
else
{
/* Calculate LFO coefficient */
state->lfo_range = fastf2u(frequency/rate + 0.5f);
switch(state->waveform)
{
case CWF_Triangle:
state->lfo_scale = 4.0f / state->lfo_range;
break;
case CWF_Sinusoid:
state->lfo_scale = F_2PI / state->lfo_range;
break;
}
/* Calculate lfo phase displacement */
state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f));
}
}
static inline void Triangle(ALint *delay_left, ALint *delay_right, ALuint offset, const ALchorusState *state)
{
ALfloat lfo_value;
lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
lfo_value *= state->depth * state->delay;
*delay_left = fastf2i(lfo_value) + state->delay;
offset += state->lfo_disp;
lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
lfo_value *= state->depth * state->delay;
*delay_right = fastf2i(lfo_value) + state->delay;
}
static inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALuint offset, const ALchorusState *state)
{
ALfloat lfo_value;
lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
lfo_value *= state->depth * state->delay;
*delay_left = fastf2i(lfo_value) + state->delay;
offset += state->lfo_disp;
lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
lfo_value *= state->depth * state->delay;
*delay_right = fastf2i(lfo_value) + state->delay;
}
#define DECL_TEMPLATE(Func) \
static void Process##Func(ALchorusState *state, const ALuint SamplesToDo, \
const ALfloat *restrict SamplesIn, ALfloat (*restrict out)[2]) \
{ \
const ALuint bufmask = state->BufferLength-1; \
ALfloat *restrict leftbuf = state->SampleBuffer[0]; \
ALfloat *restrict rightbuf = state->SampleBuffer[1]; \
ALuint offset = state->offset; \
const ALfloat feedback = state->feedback; \
ALuint it; \
\
for(it = 0;it < SamplesToDo;it++) \
{ \
ALint delay_left, delay_right; \
Func(&delay_left, &delay_right, offset, state); \
\
out[it][0] = leftbuf[(offset-delay_left)&bufmask]; \
leftbuf[offset&bufmask] = (out[it][0]+SamplesIn[it]) * feedback; \
\
out[it][1] = rightbuf[(offset-delay_right)&bufmask]; \
rightbuf[offset&bufmask] = (out[it][1]+SamplesIn[it]) * feedback; \
\
offset++; \
} \
state->offset = offset; \
}
DECL_TEMPLATE(Triangle)
DECL_TEMPLATE(Sinusoid)
#undef DECL_TEMPLATE
static ALvoid ALchorusState_process(ALchorusState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
{
ALuint it, kt;
ALuint base;
for(base = 0;base < SamplesToDo;)
{
ALfloat temps[64][2];
ALuint td = minu(SamplesToDo-base, 64);
switch(state->waveform)
{
case CWF_Triangle:
ProcessTriangle(state, td, SamplesIn+base, temps);
break;
case CWF_Sinusoid:
ProcessSinusoid(state, td, SamplesIn+base, temps);
break;
}
for(kt = 0;kt < MaxChannels;kt++)
{
ALfloat gain = state->Gain[0][kt];
if(gain > GAIN_SILENCE_THRESHOLD)
{
for(it = 0;it < td;it++)
SamplesOut[kt][it+base] += temps[it][0] * gain;
}
gain = state->Gain[1][kt];
if(gain > GAIN_SILENCE_THRESHOLD)
{
for(it = 0;it < td;it++)
SamplesOut[kt][it+base] += temps[it][1] * gain;
}
}
base += td;
}
}
DECLARE_DEFAULT_ALLOCATORS(ALchorusState)
DEFINE_ALEFFECTSTATE_VTABLE(ALchorusState);
typedef struct ALchorusStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALchorusStateFactory;
static ALeffectState *ALchorusStateFactory_create(ALchorusStateFactory *UNUSED(factory))
{
ALchorusState *state;
state = ALchorusState_New(sizeof(*state));
if(!state) return NULL;
SET_VTABLE2(ALchorusState, ALeffectState, state);
state->BufferLength = 0;
state->SampleBuffer[0] = NULL;
state->SampleBuffer[1] = NULL;
state->offset = 0;
state->lfo_range = 1;
state->waveform = CWF_Triangle;
return STATIC_CAST(ALeffectState, state);
}
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALchorusStateFactory);
ALeffectStateFactory *ALchorusStateFactory_getFactory(void)
{
static ALchorusStateFactory ChorusFactory = { { GET_VTABLE2(ALchorusStateFactory, ALeffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &ChorusFactory);
}
void ALchorus_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_CHORUS_WAVEFORM:
if(!(val >= AL_CHORUS_MIN_WAVEFORM && val <= AL_CHORUS_MAX_WAVEFORM))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Chorus.Waveform = val;
break;
case AL_CHORUS_PHASE:
if(!(val >= AL_CHORUS_MIN_PHASE && val <= AL_CHORUS_MAX_PHASE))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Chorus.Phase = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALchorus_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALchorus_setParami(effect, context, param, vals[0]);
}
void ALchorus_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_CHORUS_RATE:
if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Chorus.Rate = val;
break;
case AL_CHORUS_DEPTH:
if(!(val >= AL_CHORUS_MIN_DEPTH && val <= AL_CHORUS_MAX_DEPTH))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Chorus.Depth = val;
break;
case AL_CHORUS_FEEDBACK:
if(!(val >= AL_CHORUS_MIN_FEEDBACK && val <= AL_CHORUS_MAX_FEEDBACK))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Chorus.Feedback = val;
break;
case AL_CHORUS_DELAY:
if(!(val >= AL_CHORUS_MIN_DELAY && val <= AL_CHORUS_MAX_DELAY))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Chorus.Delay = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALchorus_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALchorus_setParamf(effect, context, param, vals[0]);
}
void ALchorus_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_CHORUS_WAVEFORM:
*val = props->Chorus.Waveform;
break;
case AL_CHORUS_PHASE:
*val = props->Chorus.Phase;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALchorus_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALchorus_getParami(effect, context, param, vals);
}
void ALchorus_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_CHORUS_RATE:
*val = props->Chorus.Rate;
break;
case AL_CHORUS_DEPTH:
*val = props->Chorus.Depth;
break;
case AL_CHORUS_FEEDBACK:
*val = props->Chorus.Feedback;
break;
case AL_CHORUS_DELAY:
*val = props->Chorus.Delay;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALchorus_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALchorus_getParamf(effect, context, param, vals);
}
DEFINE_ALEFFECT_VTABLE(ALchorus);
+220
View File
@@ -0,0 +1,220 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2013 by Anis A. Hireche
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include <stdlib.h>
#include "config.h"
#include "alError.h"
#include "alMain.h"
#include "alAuxEffectSlot.h"
#include "alu.h"
typedef struct ALcompressorState {
DERIVE_FROM_TYPE(ALeffectState);
/* Effect gains for each channel */
ALfloat Gain[MaxChannels];
/* Effect parameters */
ALboolean Enabled;
ALfloat AttackRate;
ALfloat ReleaseRate;
ALfloat GainCtrl;
} ALcompressorState;
static ALvoid ALcompressorState_Destruct(ALcompressorState *UNUSED(state))
{
}
static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device)
{
const ALfloat attackTime = device->Frequency * 0.2f; /* 200ms Attack */
const ALfloat releaseTime = device->Frequency * 0.4f; /* 400ms Release */
state->AttackRate = 1.0f / attackTime;
state->ReleaseRate = 1.0f / releaseTime;
return AL_TRUE;
}
static ALvoid ALcompressorState_update(ALcompressorState *state, ALCdevice *Device, const ALeffectslot *Slot)
{
ALfloat gain;
state->Enabled = Slot->EffectProps.Compressor.OnOff;
gain = sqrtf(1.0f / Device->NumChan) * Slot->Gain;
SetGains(Device, gain, state->Gain);
}
static ALvoid ALcompressorState_process(ALcompressorState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE])
{
ALuint it, kt;
ALuint base;
for(base = 0;base < SamplesToDo;)
{
ALfloat temps[64];
ALuint td = minu(SamplesToDo-base, 64);
if(state->Enabled)
{
ALfloat output, smp, amplitude;
ALfloat gain = state->GainCtrl;
for(it = 0;it < td;it++)
{
smp = SamplesIn[it+base];
amplitude = fabsf(smp);
if(amplitude > gain)
gain = minf(gain+state->AttackRate, amplitude);
else if(amplitude < gain)
gain = maxf(gain-state->ReleaseRate, amplitude);
output = 1.0f / clampf(gain, 0.5f, 2.0f);
temps[it] = smp * output;
}
state->GainCtrl = gain;
}
else
{
ALfloat output, smp, amplitude;
ALfloat gain = state->GainCtrl;
for(it = 0;it < td;it++)
{
smp = SamplesIn[it+base];
amplitude = 1.0f;
if(amplitude > gain)
gain = minf(gain+state->AttackRate, amplitude);
else if(amplitude < gain)
gain = maxf(gain-state->ReleaseRate, amplitude);
output = 1.0f / clampf(gain, 0.5f, 2.0f);
temps[it] = smp * output;
}
state->GainCtrl = gain;
}
for(kt = 0;kt < MaxChannels;kt++)
{
ALfloat gain = state->Gain[kt];
if(!(gain > GAIN_SILENCE_THRESHOLD))
continue;
for(it = 0;it < td;it++)
SamplesOut[kt][base+it] += gain * temps[it];
}
base += td;
}
}
DECLARE_DEFAULT_ALLOCATORS(ALcompressorState)
DEFINE_ALEFFECTSTATE_VTABLE(ALcompressorState);
typedef struct ALcompressorStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALcompressorStateFactory;
static ALeffectState *ALcompressorStateFactory_create(ALcompressorStateFactory *UNUSED(factory))
{
ALcompressorState *state;
state = ALcompressorState_New(sizeof(*state));
if(!state) return NULL;
SET_VTABLE2(ALcompressorState, ALeffectState, state);
state->Enabled = AL_TRUE;
state->AttackRate = 0.0f;
state->ReleaseRate = 0.0f;
state->GainCtrl = 1.0f;
return STATIC_CAST(ALeffectState, state);
}
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALcompressorStateFactory);
ALeffectStateFactory *ALcompressorStateFactory_getFactory(void)
{
static ALcompressorStateFactory CompressorFactory = { { GET_VTABLE2(ALcompressorStateFactory, ALeffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &CompressorFactory);
}
void ALcompressor_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_COMPRESSOR_ONOFF:
if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Compressor.OnOff = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALcompressor_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALcompressor_setParami(effect, context, param, vals[0]);
}
void ALcompressor_setParamf(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALfloat UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALcompressor_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALcompressor_setParamf(effect, context, param, vals[0]);
}
void ALcompressor_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_COMPRESSOR_ONOFF:
*val = props->Compressor.OnOff;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALcompressor_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALcompressor_getParami(effect, context, param, vals);
}
void ALcompressor_getParamf(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALfloat *UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALcompressor_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALcompressor_getParamf(effect, context, param, vals);
}
DEFINE_ALEFFECT_VTABLE(ALcompressor);
+164
View File
@@ -0,0 +1,164 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2011 by Chris Robinson.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdlib.h>
#include "alMain.h"
#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
typedef struct ALdedicatedState {
DERIVE_FROM_TYPE(ALeffectState);
ALfloat gains[MaxChannels];
} ALdedicatedState;
static ALvoid ALdedicatedState_Destruct(ALdedicatedState *UNUSED(state))
{
}
static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *UNUSED(state), ALCdevice *UNUSED(device))
{
return AL_TRUE;
}
static ALvoid ALdedicatedState_update(ALdedicatedState *state, ALCdevice *device, const ALeffectslot *Slot)
{
ALfloat Gain;
ALsizei s;
Gain = Slot->Gain * Slot->EffectProps.Dedicated.Gain;
if(Slot->EffectType == AL_EFFECT_DEDICATED_DIALOGUE)
ComputeAngleGains(device, atan2f(0.0f, 1.0f), 0.0f, Gain, state->gains);
else if(Slot->EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
{
for(s = 0;s < MaxChannels;s++)
state->gains[s] = 0.0f;
state->gains[LFE] = Gain;
}
}
static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
{
const ALfloat *gains = state->gains;
ALuint i, c;
for(c = 0;c < MaxChannels;c++)
{
if(!(gains[c] > GAIN_SILENCE_THRESHOLD))
continue;
for(i = 0;i < SamplesToDo;i++)
SamplesOut[c][i] = SamplesIn[i] * gains[c];
}
}
DECLARE_DEFAULT_ALLOCATORS(ALdedicatedState)
DEFINE_ALEFFECTSTATE_VTABLE(ALdedicatedState);
typedef struct ALdedicatedStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALdedicatedStateFactory;
ALeffectState *ALdedicatedStateFactory_create(ALdedicatedStateFactory *UNUSED(factory))
{
ALdedicatedState *state;
ALsizei s;
state = ALdedicatedState_New(sizeof(*state));
if(!state) return NULL;
SET_VTABLE2(ALdedicatedState, ALeffectState, state);
for(s = 0;s < MaxChannels;s++)
state->gains[s] = 0.0f;
return STATIC_CAST(ALeffectState, state);
}
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALdedicatedStateFactory);
ALeffectStateFactory *ALdedicatedStateFactory_getFactory(void)
{
static ALdedicatedStateFactory DedicatedFactory = { { GET_VTABLE2(ALdedicatedStateFactory, ALeffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &DedicatedFactory);
}
void ALdedicated_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALdedicated_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALdedicated_setParami(effect, context, param, vals[0]);
}
void ALdedicated_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_DEDICATED_GAIN:
if(!(val >= 0.0f && isfinite(val)))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Dedicated.Gain = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALdedicated_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALdedicated_setParamf(effect, context, param, vals[0]);
}
void ALdedicated_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALdedicated_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALdedicated_getParami(effect, context, param, vals);
}
void ALdedicated_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_DEDICATED_GAIN:
*val = props->Dedicated.Gain;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALdedicated_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALdedicated_getParamf(effect, context, param, vals);
}
DEFINE_ALEFFECT_VTABLE(ALdedicated);
+297
View File
@@ -0,0 +1,297 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2013 by Mike Gorchak
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <math.h>
#include <stdlib.h>
#include "alMain.h"
#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
typedef struct ALdistortionState {
DERIVE_FROM_TYPE(ALeffectState);
/* Effect gains for each channel */
ALfloat Gain[MaxChannels];
/* Effect parameters */
ALfilterState lowpass;
ALfilterState bandpass;
ALfloat attenuation;
ALfloat edge_coeff;
} ALdistortionState;
static ALvoid ALdistortionState_Destruct(ALdistortionState *UNUSED(state))
{
}
static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *UNUSED(state), ALCdevice *UNUSED(device))
{
return AL_TRUE;
}
static ALvoid ALdistortionState_update(ALdistortionState *state, ALCdevice *Device, const ALeffectslot *Slot)
{
ALfloat frequency = (ALfloat)Device->Frequency;
ALfloat bandwidth;
ALfloat cutoff;
ALfloat edge;
ALfloat gain;
/* Store distorted signal attenuation settings */
state->attenuation = Slot->EffectProps.Distortion.Gain;
/* Store waveshaper edge settings */
edge = sinf(Slot->EffectProps.Distortion.Edge * (F_PI_2));
edge = minf(edge, 0.99f);
state->edge_coeff = 2.0f * edge / (1.0f-edge);
/* Lowpass filter */
cutoff = Slot->EffectProps.Distortion.LowpassCutoff;
/* Bandwidth value is constant in octaves */
bandwidth = (cutoff / 2.0f) / (cutoff * 0.67f);
ALfilterState_setParams(&state->lowpass, ALfilterType_LowPass, 1.0f,
cutoff / (frequency*4.0f), bandwidth);
/* Bandpass filter */
cutoff = Slot->EffectProps.Distortion.EQCenter;
/* Convert bandwidth in Hz to octaves */
bandwidth = Slot->EffectProps.Distortion.EQBandwidth / (cutoff * 0.67f);
ALfilterState_setParams(&state->bandpass, ALfilterType_BandPass, 1.0f,
cutoff / (frequency*4.0f), bandwidth);
gain = sqrtf(1.0f / Device->NumChan) * Slot->Gain;
SetGains(Device, gain, state->Gain);
}
static ALvoid ALdistortionState_process(ALdistortionState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
{
const ALfloat fc = state->edge_coeff;
float oversample_buffer[64][4];
ALuint base;
ALuint it;
ALuint ot;
ALuint kt;
for(base = 0;base < SamplesToDo;)
{
ALfloat temps[64];
ALuint td = minu(SamplesToDo-base, 64);
/* Perform 4x oversampling to avoid aliasing. */
/* Oversampling greatly improves distortion */
/* quality and allows to implement lowpass and */
/* bandpass filters using high frequencies, at */
/* which classic IIR filters became unstable. */
/* Fill oversample buffer using zero stuffing */
for(it = 0;it < td;it++)
{
oversample_buffer[it][0] = SamplesIn[it+base];
oversample_buffer[it][1] = 0.0f;
oversample_buffer[it][2] = 0.0f;
oversample_buffer[it][3] = 0.0f;
}
/* First step, do lowpass filtering of original signal, */
/* additionally perform buffer interpolation and lowpass */
/* cutoff for oversampling (which is fortunately first */
/* step of distortion). So combine three operations into */
/* the one. */
for(it = 0;it < td;it++)
{
for(ot = 0;ot < 4;ot++)
{
ALfloat smp;
smp = ALfilterState_processSingle(&state->lowpass, oversample_buffer[it][ot]);
/* Restore signal power by multiplying sample by amount of oversampling */
oversample_buffer[it][ot] = smp * 4.0f;
}
}
for(it = 0;it < td;it++)
{
/* Second step, do distortion using waveshaper function */
/* to emulate signal processing during tube overdriving. */
/* Three steps of waveshaping are intended to modify */
/* waveform without boost/clipping/attenuation process. */
for(ot = 0;ot < 4;ot++)
{
ALfloat smp = oversample_buffer[it][ot];
smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp)) * -1.0f;
smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
/* Third step, do bandpass filtering of distorted signal */
smp = ALfilterState_processSingle(&state->bandpass, smp);
oversample_buffer[it][ot] = smp;
}
/* Fourth step, final, do attenuation and perform decimation, */
/* store only one sample out of 4. */
temps[it] = oversample_buffer[it][0] * state->attenuation;
}
for(kt = 0;kt < MaxChannels;kt++)
{
ALfloat gain = state->Gain[kt];
if(!(gain > GAIN_SILENCE_THRESHOLD))
continue;
for(it = 0;it < td;it++)
SamplesOut[kt][base+it] += gain * temps[it];
}
base += td;
}
}
DECLARE_DEFAULT_ALLOCATORS(ALdistortionState)
DEFINE_ALEFFECTSTATE_VTABLE(ALdistortionState);
typedef struct ALdistortionStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALdistortionStateFactory;
static ALeffectState *ALdistortionStateFactory_create(ALdistortionStateFactory *UNUSED(factory))
{
ALdistortionState *state;
state = ALdistortionState_New(sizeof(*state));
if(!state) return NULL;
SET_VTABLE2(ALdistortionState, ALeffectState, state);
ALfilterState_clear(&state->lowpass);
ALfilterState_clear(&state->bandpass);
return STATIC_CAST(ALeffectState, state);
}
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALdistortionStateFactory);
ALeffectStateFactory *ALdistortionStateFactory_getFactory(void)
{
static ALdistortionStateFactory DistortionFactory = { { GET_VTABLE2(ALdistortionStateFactory, ALeffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &DistortionFactory);
}
void ALdistortion_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALdistortion_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALdistortion_setParami(effect, context, param, vals[0]);
}
void ALdistortion_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_DISTORTION_EDGE:
if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Distortion.Edge = val;
break;
case AL_DISTORTION_GAIN:
if(!(val >= AL_DISTORTION_MIN_GAIN && val <= AL_DISTORTION_MAX_GAIN))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Distortion.Gain = val;
break;
case AL_DISTORTION_LOWPASS_CUTOFF:
if(!(val >= AL_DISTORTION_MIN_LOWPASS_CUTOFF && val <= AL_DISTORTION_MAX_LOWPASS_CUTOFF))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Distortion.LowpassCutoff = val;
break;
case AL_DISTORTION_EQCENTER:
if(!(val >= AL_DISTORTION_MIN_EQCENTER && val <= AL_DISTORTION_MAX_EQCENTER))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Distortion.EQCenter = val;
break;
case AL_DISTORTION_EQBANDWIDTH:
if(!(val >= AL_DISTORTION_MIN_EQBANDWIDTH && val <= AL_DISTORTION_MAX_EQBANDWIDTH))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Distortion.EQBandwidth = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALdistortion_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALdistortion_setParamf(effect, context, param, vals[0]);
}
void ALdistortion_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALdistortion_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALdistortion_getParami(effect, context, param, vals);
}
void ALdistortion_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_DISTORTION_EDGE:
*val = props->Distortion.Edge;
break;
case AL_DISTORTION_GAIN:
*val = props->Distortion.Gain;
break;
case AL_DISTORTION_LOWPASS_CUTOFF:
*val = props->Distortion.LowpassCutoff;
break;
case AL_DISTORTION_EQCENTER:
*val = props->Distortion.EQCenter;
break;
case AL_DISTORTION_EQBANDWIDTH:
*val = props->Distortion.EQBandwidth;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALdistortion_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALdistortion_getParamf(effect, context, param, vals);
}
DEFINE_ALEFFECT_VTABLE(ALdistortion);
+293
View File
@@ -0,0 +1,293 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2009 by Chris Robinson.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <math.h>
#include <stdlib.h>
#include "alMain.h"
#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
typedef struct ALechoState {
DERIVE_FROM_TYPE(ALeffectState);
ALfloat *SampleBuffer;
ALuint BufferLength;
// The echo is two tap. The delay is the number of samples from before the
// current offset
struct {
ALuint delay;
} Tap[2];
ALuint Offset;
/* The panning gains for the two taps */
ALfloat Gain[2][MaxChannels];
ALfloat FeedGain;
ALfilterState Filter;
} ALechoState;
static ALvoid ALechoState_Destruct(ALechoState *state)
{
free(state->SampleBuffer);
state->SampleBuffer = NULL;
}
static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device)
{
ALuint maxlen, i;
// Use the next power of 2 for the buffer length, so the tap offsets can be
// wrapped using a mask instead of a modulo
maxlen = fastf2u(AL_ECHO_MAX_DELAY * Device->Frequency) + 1;
maxlen += fastf2u(AL_ECHO_MAX_LRDELAY * Device->Frequency) + 1;
maxlen = NextPowerOf2(maxlen);
if(maxlen != state->BufferLength)
{
void *temp;
temp = realloc(state->SampleBuffer, maxlen * sizeof(ALfloat));
if(!temp) return AL_FALSE;
state->SampleBuffer = temp;
state->BufferLength = maxlen;
}
for(i = 0;i < state->BufferLength;i++)
state->SampleBuffer[i] = 0.0f;
return AL_TRUE;
}
static ALvoid ALechoState_update(ALechoState *state, ALCdevice *Device, const ALeffectslot *Slot)
{
ALuint frequency = Device->Frequency;
ALfloat lrpan, gain;
ALfloat dirGain;
state->Tap[0].delay = fastf2u(Slot->EffectProps.Echo.Delay * frequency) + 1;
state->Tap[1].delay = fastf2u(Slot->EffectProps.Echo.LRDelay * frequency);
state->Tap[1].delay += state->Tap[0].delay;
lrpan = Slot->EffectProps.Echo.Spread;
state->FeedGain = Slot->EffectProps.Echo.Feedback;
ALfilterState_setParams(&state->Filter, ALfilterType_HighShelf,
1.0f - Slot->EffectProps.Echo.Damping,
LOWPASSFREQREF/frequency, 0.0f);
gain = Slot->Gain;
dirGain = fabsf(lrpan);
/* First tap panning */
ComputeAngleGains(Device, atan2f(-lrpan, 0.0f), (1.0f-dirGain)*F_PI, gain, state->Gain[0]);
/* Second tap panning */
ComputeAngleGains(Device, atan2f(+lrpan, 0.0f), (1.0f-dirGain)*F_PI, gain, state->Gain[1]);
}
static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
{
const ALuint mask = state->BufferLength-1;
const ALuint tap1 = state->Tap[0].delay;
const ALuint tap2 = state->Tap[1].delay;
ALuint offset = state->Offset;
ALfloat smp;
ALuint base;
ALuint i, k;
for(base = 0;base < SamplesToDo;)
{
ALfloat temps[64][2];
ALuint td = minu(SamplesToDo-base, 64);
for(i = 0;i < td;i++)
{
/* First tap */
temps[i][0] = state->SampleBuffer[(offset-tap1) & mask];
/* Second tap */
temps[i][1] = state->SampleBuffer[(offset-tap2) & mask];
// Apply damping and feedback gain to the second tap, and mix in the
// new sample
smp = ALfilterState_processSingle(&state->Filter, temps[i][1]+SamplesIn[i+base]);
state->SampleBuffer[offset&mask] = smp * state->FeedGain;
offset++;
}
for(k = 0;k < MaxChannels;k++)
{
ALfloat gain = state->Gain[0][k];
if(gain > GAIN_SILENCE_THRESHOLD)
{
for(i = 0;i < td;i++)
SamplesOut[k][i+base] += temps[i][0] * gain;
}
gain = state->Gain[1][k];
if(gain > GAIN_SILENCE_THRESHOLD)
{
for(i = 0;i < td;i++)
SamplesOut[k][i+base] += temps[i][1] * gain;
}
}
base += td;
}
state->Offset = offset;
}
DECLARE_DEFAULT_ALLOCATORS(ALechoState)
DEFINE_ALEFFECTSTATE_VTABLE(ALechoState);
typedef struct ALechoStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALechoStateFactory;
ALeffectState *ALechoStateFactory_create(ALechoStateFactory *UNUSED(factory))
{
ALechoState *state;
state = ALechoState_New(sizeof(*state));
if(!state) return NULL;
SET_VTABLE2(ALechoState, ALeffectState, state);
state->BufferLength = 0;
state->SampleBuffer = NULL;
state->Tap[0].delay = 0;
state->Tap[1].delay = 0;
state->Offset = 0;
ALfilterState_clear(&state->Filter);
return STATIC_CAST(ALeffectState, state);
}
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALechoStateFactory);
ALeffectStateFactory *ALechoStateFactory_getFactory(void)
{
static ALechoStateFactory EchoFactory = { { GET_VTABLE2(ALechoStateFactory, ALeffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &EchoFactory);
}
void ALecho_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALecho_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALecho_setParami(effect, context, param, vals[0]);
}
void ALecho_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_ECHO_DELAY:
if(!(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Echo.Delay = val;
break;
case AL_ECHO_LRDELAY:
if(!(val >= AL_ECHO_MIN_LRDELAY && val <= AL_ECHO_MAX_LRDELAY))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Echo.LRDelay = val;
break;
case AL_ECHO_DAMPING:
if(!(val >= AL_ECHO_MIN_DAMPING && val <= AL_ECHO_MAX_DAMPING))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Echo.Damping = val;
break;
case AL_ECHO_FEEDBACK:
if(!(val >= AL_ECHO_MIN_FEEDBACK && val <= AL_ECHO_MAX_FEEDBACK))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Echo.Feedback = val;
break;
case AL_ECHO_SPREAD:
if(!(val >= AL_ECHO_MIN_SPREAD && val <= AL_ECHO_MAX_SPREAD))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Echo.Spread = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALecho_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALecho_setParamf(effect, context, param, vals[0]);
}
void ALecho_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALecho_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALecho_getParami(effect, context, param, vals);
}
void ALecho_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_ECHO_DELAY:
*val = props->Echo.Delay;
break;
case AL_ECHO_LRDELAY:
*val = props->Echo.LRDelay;
break;
case AL_ECHO_DAMPING:
*val = props->Echo.Damping;
break;
case AL_ECHO_FEEDBACK:
*val = props->Echo.Feedback;
break;
case AL_ECHO_SPREAD:
*val = props->Echo.Spread;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALecho_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALecho_getParamf(effect, context, param, vals);
}
DEFINE_ALEFFECT_VTABLE(ALecho);
+334
View File
@@ -0,0 +1,334 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2013 by Mike Gorchak
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <math.h>
#include <stdlib.h>
#include "alMain.h"
#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
/* The document "Effects Extension Guide.pdf" says that low and high *
* frequencies are cutoff frequencies. This is not fully correct, they *
* are corner frequencies for low and high shelf filters. If they were *
* just cutoff frequencies, there would be no need in cutoff frequency *
* gains, which are present. Documentation for "Creative Proteus X2" *
* software describes 4-band equalizer functionality in a much better *
* way. This equalizer seems to be a predecessor of OpenAL 4-band *
* equalizer. With low and high shelf filters we are able to cutoff *
* frequencies below and/or above corner frequencies using attenuation *
* gains (below 1.0) and amplify all low and/or high frequencies using *
* gains above 1.0. *
* *
* Low-shelf Low Mid Band High Mid Band High-shelf *
* corner center center corner *
* frequency frequency frequency frequency *
* 50Hz..800Hz 200Hz..3000Hz 1000Hz..8000Hz 4000Hz..16000Hz *
* *
* | | | | *
* | | | | *
* B -----+ /--+--\ /--+--\ +----- *
* O |\ | | | | | | /| *
* O | \ - | - - | - / | *
* S + | \ | | | | | | / | *
* T | | | | | | | | | | *
* ---------+---------------+------------------+---------------+-------- *
* C | | | | | | | | | | *
* U - | / | | | | | | \ | *
* T | / - | - - | - \ | *
* O |/ | | | | | | \| *
* F -----+ \--+--/ \--+--/ +----- *
* F | | | | *
* | | | | *
* *
* Gains vary from 0.126 up to 7.943, which means from -18dB attenuation *
* up to +18dB amplification. Band width varies from 0.01 up to 1.0 in *
* octaves for two mid bands. *
* *
* Implementation is based on the "Cookbook formulae for audio EQ biquad *
* filter coefficients" by Robert Bristow-Johnson *
* http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt */
typedef struct ALequalizerState {
DERIVE_FROM_TYPE(ALeffectState);
/* Effect gains for each channel */
ALfloat Gain[MaxChannels];
/* Effect parameters */
ALfilterState filter[4];
} ALequalizerState;
static ALvoid ALequalizerState_Destruct(ALequalizerState *UNUSED(state))
{
}
static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *UNUSED(state), ALCdevice *UNUSED(device))
{
return AL_TRUE;
}
static ALvoid ALequalizerState_update(ALequalizerState *state, ALCdevice *device, const ALeffectslot *slot)
{
ALfloat frequency = (ALfloat)device->Frequency;
ALfloat gain = sqrtf(1.0f / device->NumChan) * slot->Gain;
SetGains(device, gain, state->Gain);
/* Calculate coefficients for the each type of filter */
ALfilterState_setParams(&state->filter[0], ALfilterType_LowShelf,
sqrtf(slot->EffectProps.Equalizer.LowGain),
slot->EffectProps.Equalizer.LowCutoff/frequency,
0.0f);
ALfilterState_setParams(&state->filter[1], ALfilterType_Peaking,
sqrtf(slot->EffectProps.Equalizer.Mid1Gain),
slot->EffectProps.Equalizer.Mid1Center/frequency,
slot->EffectProps.Equalizer.Mid1Width);
ALfilterState_setParams(&state->filter[2], ALfilterType_Peaking,
sqrtf(slot->EffectProps.Equalizer.Mid2Gain),
slot->EffectProps.Equalizer.Mid2Center/frequency,
slot->EffectProps.Equalizer.Mid2Width);
ALfilterState_setParams(&state->filter[3], ALfilterType_HighShelf,
sqrtf(slot->EffectProps.Equalizer.HighGain),
slot->EffectProps.Equalizer.HighCutoff/frequency,
0.0f);
}
static ALvoid ALequalizerState_process(ALequalizerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
{
ALuint base;
ALuint it;
ALuint kt;
ALuint ft;
for(base = 0;base < SamplesToDo;)
{
ALfloat temps[64];
ALuint td = minu(SamplesToDo-base, 64);
for(it = 0;it < td;it++)
{
ALfloat smp = SamplesIn[base+it];
for(ft = 0;ft < 4;ft++)
smp = ALfilterState_processSingle(&state->filter[ft], smp);
temps[it] = smp;
}
for(kt = 0;kt < MaxChannels;kt++)
{
ALfloat gain = state->Gain[kt];
if(!(gain > GAIN_SILENCE_THRESHOLD))
continue;
for(it = 0;it < td;it++)
SamplesOut[kt][base+it] += gain * temps[it];
}
base += td;
}
}
DECLARE_DEFAULT_ALLOCATORS(ALequalizerState)
DEFINE_ALEFFECTSTATE_VTABLE(ALequalizerState);
typedef struct ALequalizerStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALequalizerStateFactory;
ALeffectState *ALequalizerStateFactory_create(ALequalizerStateFactory *UNUSED(factory))
{
ALequalizerState *state;
int it;
state = ALequalizerState_New(sizeof(*state));
if(!state) return NULL;
SET_VTABLE2(ALequalizerState, ALeffectState, state);
/* Initialize sample history only on filter creation to avoid */
/* sound clicks if filter settings were changed in runtime. */
for(it = 0; it < 4; it++)
ALfilterState_clear(&state->filter[it]);
return STATIC_CAST(ALeffectState, state);
}
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALequalizerStateFactory);
ALeffectStateFactory *ALequalizerStateFactory_getFactory(void)
{
static ALequalizerStateFactory EqualizerFactory = { { GET_VTABLE2(ALequalizerStateFactory, ALeffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &EqualizerFactory);
}
void ALequalizer_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALequalizer_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALequalizer_setParami(effect, context, param, vals[0]);
}
void ALequalizer_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_EQUALIZER_LOW_GAIN:
if(!(val >= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_LOW_GAIN))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Equalizer.LowGain = val;
break;
case AL_EQUALIZER_LOW_CUTOFF:
if(!(val >= AL_EQUALIZER_MIN_LOW_CUTOFF && val <= AL_EQUALIZER_MAX_LOW_CUTOFF))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Equalizer.LowCutoff = val;
break;
case AL_EQUALIZER_MID1_GAIN:
if(!(val >= AL_EQUALIZER_MIN_MID1_GAIN && val <= AL_EQUALIZER_MAX_MID1_GAIN))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Equalizer.Mid1Gain = val;
break;
case AL_EQUALIZER_MID1_CENTER:
if(!(val >= AL_EQUALIZER_MIN_MID1_CENTER && val <= AL_EQUALIZER_MAX_MID1_CENTER))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Equalizer.Mid1Center = val;
break;
case AL_EQUALIZER_MID1_WIDTH:
if(!(val >= AL_EQUALIZER_MIN_MID1_WIDTH && val <= AL_EQUALIZER_MAX_MID1_WIDTH))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Equalizer.Mid1Width = val;
break;
case AL_EQUALIZER_MID2_GAIN:
if(!(val >= AL_EQUALIZER_MIN_MID2_GAIN && val <= AL_EQUALIZER_MAX_MID2_GAIN))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Equalizer.Mid2Gain = val;
break;
case AL_EQUALIZER_MID2_CENTER:
if(!(val >= AL_EQUALIZER_MIN_MID2_CENTER && val <= AL_EQUALIZER_MAX_MID2_CENTER))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Equalizer.Mid2Center = val;
break;
case AL_EQUALIZER_MID2_WIDTH:
if(!(val >= AL_EQUALIZER_MIN_MID2_WIDTH && val <= AL_EQUALIZER_MAX_MID2_WIDTH))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Equalizer.Mid2Width = val;
break;
case AL_EQUALIZER_HIGH_GAIN:
if(!(val >= AL_EQUALIZER_MIN_HIGH_GAIN && val <= AL_EQUALIZER_MAX_HIGH_GAIN))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Equalizer.HighGain = val;
break;
case AL_EQUALIZER_HIGH_CUTOFF:
if(!(val >= AL_EQUALIZER_MIN_HIGH_CUTOFF && val <= AL_EQUALIZER_MAX_HIGH_CUTOFF))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Equalizer.HighCutoff = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALequalizer_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALequalizer_setParamf(effect, context, param, vals[0]);
}
void ALequalizer_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALequalizer_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALequalizer_getParami(effect, context, param, vals);
}
void ALequalizer_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_EQUALIZER_LOW_GAIN:
*val = props->Equalizer.LowGain;
break;
case AL_EQUALIZER_LOW_CUTOFF:
*val = props->Equalizer.LowCutoff;
break;
case AL_EQUALIZER_MID1_GAIN:
*val = props->Equalizer.Mid1Gain;
break;
case AL_EQUALIZER_MID1_CENTER:
*val = props->Equalizer.Mid1Center;
break;
case AL_EQUALIZER_MID1_WIDTH:
*val = props->Equalizer.Mid1Width;
break;
case AL_EQUALIZER_MID2_GAIN:
*val = props->Equalizer.Mid2Gain;
break;
case AL_EQUALIZER_MID2_CENTER:
*val = props->Equalizer.Mid2Center;
break;
case AL_EQUALIZER_MID2_WIDTH:
*val = props->Equalizer.Mid2Width;
break;
case AL_EQUALIZER_HIGH_GAIN:
*val = props->Equalizer.HighGain;
break;
case AL_EQUALIZER_HIGH_CUTOFF:
*val = props->Equalizer.HighCutoff;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALequalizer_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALequalizer_getParamf(effect, context, param, vals);
}
DEFINE_ALEFFECT_VTABLE(ALequalizer);
+396
View File
@@ -0,0 +1,396 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2013 by Mike Gorchak
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <math.h>
#include <stdlib.h>
#include "alMain.h"
#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
enum FlangerWaveForm {
FWF_Triangle = AL_FLANGER_WAVEFORM_TRIANGLE,
FWF_Sinusoid = AL_FLANGER_WAVEFORM_SINUSOID
};
typedef struct ALflangerState {
DERIVE_FROM_TYPE(ALeffectState);
ALfloat *SampleBuffer[2];
ALuint BufferLength;
ALuint offset;
ALuint lfo_range;
ALfloat lfo_scale;
ALint lfo_disp;
/* Gains for left and right sides */
ALfloat Gain[2][MaxChannels];
/* effect parameters */
enum FlangerWaveForm waveform;
ALint delay;
ALfloat depth;
ALfloat feedback;
} ALflangerState;
static ALvoid ALflangerState_Destruct(ALflangerState *state)
{
free(state->SampleBuffer[0]);
state->SampleBuffer[0] = NULL;
state->SampleBuffer[1] = NULL;
}
static ALboolean ALflangerState_deviceUpdate(ALflangerState *state, ALCdevice *Device)
{
ALuint maxlen;
ALuint it;
maxlen = fastf2u(AL_FLANGER_MAX_DELAY * 3.0f * Device->Frequency) + 1;
maxlen = NextPowerOf2(maxlen);
if(maxlen != state->BufferLength)
{
void *temp;
temp = realloc(state->SampleBuffer[0], maxlen * sizeof(ALfloat) * 2);
if(!temp) return AL_FALSE;
state->SampleBuffer[0] = temp;
state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen;
state->BufferLength = maxlen;
}
for(it = 0;it < state->BufferLength;it++)
{
state->SampleBuffer[0][it] = 0.0f;
state->SampleBuffer[1][it] = 0.0f;
}
return AL_TRUE;
}
static ALvoid ALflangerState_update(ALflangerState *state, ALCdevice *Device, const ALeffectslot *Slot)
{
ALfloat frequency = (ALfloat)Device->Frequency;
ALfloat rate;
ALint phase;
switch(Slot->EffectProps.Flanger.Waveform)
{
case AL_FLANGER_WAVEFORM_TRIANGLE:
state->waveform = FWF_Triangle;
break;
case AL_FLANGER_WAVEFORM_SINUSOID:
state->waveform = FWF_Sinusoid;
break;
}
state->depth = Slot->EffectProps.Flanger.Depth;
state->feedback = Slot->EffectProps.Flanger.Feedback;
state->delay = fastf2i(Slot->EffectProps.Flanger.Delay * frequency);
/* Gains for left and right sides */
ComputeAngleGains(Device, atan2f(-1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[0]);
ComputeAngleGains(Device, atan2f(+1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[1]);
phase = Slot->EffectProps.Flanger.Phase;
rate = Slot->EffectProps.Flanger.Rate;
if(!(rate > 0.0f))
{
state->lfo_scale = 0.0f;
state->lfo_range = 1;
state->lfo_disp = 0;
}
else
{
/* Calculate LFO coefficient */
state->lfo_range = fastf2u(frequency/rate + 0.5f);
switch(state->waveform)
{
case FWF_Triangle:
state->lfo_scale = 4.0f / state->lfo_range;
break;
case FWF_Sinusoid:
state->lfo_scale = F_2PI / state->lfo_range;
break;
}
/* Calculate lfo phase displacement */
state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f));
}
}
static inline void Triangle(ALint *delay_left, ALint *delay_right, ALuint offset, const ALflangerState *state)
{
ALfloat lfo_value;
lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
lfo_value *= state->depth * state->delay;
*delay_left = fastf2i(lfo_value) + state->delay;
offset += state->lfo_disp;
lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
lfo_value *= state->depth * state->delay;
*delay_right = fastf2i(lfo_value) + state->delay;
}
static inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALuint offset, const ALflangerState *state)
{
ALfloat lfo_value;
lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
lfo_value *= state->depth * state->delay;
*delay_left = fastf2i(lfo_value) + state->delay;
offset += state->lfo_disp;
lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
lfo_value *= state->depth * state->delay;
*delay_right = fastf2i(lfo_value) + state->delay;
}
#define DECL_TEMPLATE(Func) \
static void Process##Func(ALflangerState *state, const ALuint SamplesToDo, \
const ALfloat *restrict SamplesIn, ALfloat (*restrict out)[2]) \
{ \
const ALuint bufmask = state->BufferLength-1; \
ALfloat *restrict leftbuf = state->SampleBuffer[0]; \
ALfloat *restrict rightbuf = state->SampleBuffer[1]; \
ALuint offset = state->offset; \
const ALfloat feedback = state->feedback; \
ALuint it; \
\
for(it = 0;it < SamplesToDo;it++) \
{ \
ALint delay_left, delay_right; \
Func(&delay_left, &delay_right, offset, state); \
\
out[it][0] = leftbuf[(offset-delay_left)&bufmask]; \
leftbuf[offset&bufmask] = (out[it][0]+SamplesIn[it]) * feedback; \
\
out[it][1] = rightbuf[(offset-delay_right)&bufmask]; \
rightbuf[offset&bufmask] = (out[it][1]+SamplesIn[it]) * feedback; \
\
offset++; \
} \
state->offset = offset; \
}
DECL_TEMPLATE(Triangle)
DECL_TEMPLATE(Sinusoid)
#undef DECL_TEMPLATE
static ALvoid ALflangerState_process(ALflangerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
{
ALuint it, kt;
ALuint base;
for(base = 0;base < SamplesToDo;)
{
ALfloat temps[64][2];
ALuint td = minu(SamplesToDo-base, 64);
switch(state->waveform)
{
case FWF_Triangle:
ProcessTriangle(state, td, SamplesIn+base, temps);
break;
case FWF_Sinusoid:
ProcessSinusoid(state, td, SamplesIn+base, temps);
break;
}
for(kt = 0;kt < MaxChannels;kt++)
{
ALfloat gain = state->Gain[0][kt];
if(gain > GAIN_SILENCE_THRESHOLD)
{
for(it = 0;it < td;it++)
SamplesOut[kt][it+base] += temps[it][0] * gain;
}
gain = state->Gain[1][kt];
if(gain > GAIN_SILENCE_THRESHOLD)
{
for(it = 0;it < td;it++)
SamplesOut[kt][it+base] += temps[it][1] * gain;
}
}
base += td;
}
}
DECLARE_DEFAULT_ALLOCATORS(ALflangerState)
DEFINE_ALEFFECTSTATE_VTABLE(ALflangerState);
typedef struct ALflangerStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALflangerStateFactory;
ALeffectState *ALflangerStateFactory_create(ALflangerStateFactory *UNUSED(factory))
{
ALflangerState *state;
state = ALflangerState_New(sizeof(*state));
if(!state) return NULL;
SET_VTABLE2(ALflangerState, ALeffectState, state);
state->BufferLength = 0;
state->SampleBuffer[0] = NULL;
state->SampleBuffer[1] = NULL;
state->offset = 0;
state->lfo_range = 1;
state->waveform = FWF_Triangle;
return STATIC_CAST(ALeffectState, state);
}
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALflangerStateFactory);
ALeffectStateFactory *ALflangerStateFactory_getFactory(void)
{
static ALflangerStateFactory FlangerFactory = { { GET_VTABLE2(ALflangerStateFactory, ALeffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &FlangerFactory);
}
void ALflanger_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_FLANGER_WAVEFORM:
if(!(val >= AL_FLANGER_MIN_WAVEFORM && val <= AL_FLANGER_MAX_WAVEFORM))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Flanger.Waveform = val;
break;
case AL_FLANGER_PHASE:
if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Flanger.Phase = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALflanger_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALflanger_setParami(effect, context, param, vals[0]);
}
void ALflanger_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_FLANGER_RATE:
if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Flanger.Rate = val;
break;
case AL_FLANGER_DEPTH:
if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Flanger.Depth = val;
break;
case AL_FLANGER_FEEDBACK:
if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Flanger.Feedback = val;
break;
case AL_FLANGER_DELAY:
if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Flanger.Delay = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALflanger_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALflanger_setParamf(effect, context, param, vals[0]);
}
void ALflanger_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_FLANGER_WAVEFORM:
*val = props->Flanger.Waveform;
break;
case AL_FLANGER_PHASE:
*val = props->Flanger.Phase;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALflanger_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALflanger_getParami(effect, context, param, vals);
}
void ALflanger_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_FLANGER_RATE:
*val = props->Flanger.Rate;
break;
case AL_FLANGER_DEPTH:
*val = props->Flanger.Depth;
break;
case AL_FLANGER_FEEDBACK:
*val = props->Flanger.Feedback;
break;
case AL_FLANGER_DELAY:
*val = props->Flanger.Delay;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALflanger_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALflanger_getParamf(effect, context, param, vals);
}
DEFINE_ALEFFECT_VTABLE(ALflanger);
+303
View File
@@ -0,0 +1,303 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2009 by Chris Robinson.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <math.h>
#include <stdlib.h>
#include "alMain.h"
#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
typedef struct ALmodulatorState {
DERIVE_FROM_TYPE(ALeffectState);
enum {
SINUSOID,
SAWTOOTH,
SQUARE
} Waveform;
ALuint index;
ALuint step;
ALfloat Gain[MaxChannels];
ALfilterState Filter;
} ALmodulatorState;
#define WAVEFORM_FRACBITS 24
#define WAVEFORM_FRACONE (1<<WAVEFORM_FRACBITS)
#define WAVEFORM_FRACMASK (WAVEFORM_FRACONE-1)
static inline ALfloat Sin(ALuint index)
{
return sinf(index*(F_2PI/WAVEFORM_FRACONE) - F_PI)*0.5f + 0.5f;
}
static inline ALfloat Saw(ALuint index)
{
return (ALfloat)index / WAVEFORM_FRACONE;
}
static inline ALfloat Square(ALuint index)
{
return (ALfloat)((index >> (WAVEFORM_FRACBITS - 1)) & 1);
}
#define DECL_TEMPLATE(func) \
static void Process##func(ALmodulatorState *state, ALuint SamplesToDo, \
const ALfloat *restrict SamplesIn, \
ALfloat (*restrict SamplesOut)[BUFFERSIZE]) \
{ \
const ALuint step = state->step; \
ALuint index = state->index; \
ALuint base; \
\
for(base = 0;base < SamplesToDo;) \
{ \
ALfloat temps[64]; \
ALuint td = minu(SamplesToDo-base, 64); \
ALuint i, k; \
\
for(i = 0;i < td;i++) \
{ \
ALfloat samp; \
samp = SamplesIn[base+i]; \
samp = ALfilterState_processSingle(&state->Filter, samp); \
\
index += step; \
index &= WAVEFORM_FRACMASK; \
temps[i] = samp * func(index); \
} \
\
for(k = 0;k < MaxChannels;k++) \
{ \
ALfloat gain = state->Gain[k]; \
if(!(gain > GAIN_SILENCE_THRESHOLD)) \
continue; \
\
for(i = 0;i < td;i++) \
SamplesOut[k][base+i] += gain * temps[i]; \
} \
\
base += td; \
} \
state->index = index; \
}
DECL_TEMPLATE(Sin)
DECL_TEMPLATE(Saw)
DECL_TEMPLATE(Square)
#undef DECL_TEMPLATE
static ALvoid ALmodulatorState_Destruct(ALmodulatorState *UNUSED(state))
{
}
static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *UNUSED(state), ALCdevice *UNUSED(device))
{
return AL_TRUE;
}
static ALvoid ALmodulatorState_update(ALmodulatorState *state, ALCdevice *Device, const ALeffectslot *Slot)
{
ALfloat gain, cw, a;
if(Slot->EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SINUSOID)
state->Waveform = SINUSOID;
else if(Slot->EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SAWTOOTH)
state->Waveform = SAWTOOTH;
else if(Slot->EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SQUARE)
state->Waveform = SQUARE;
state->step = fastf2u(Slot->EffectProps.Modulator.Frequency*WAVEFORM_FRACONE /
Device->Frequency);
if(state->step == 0) state->step = 1;
/* Custom filter coeffs, which match the old version instead of a low-shelf. */
cw = cosf(F_2PI * Slot->EffectProps.Modulator.HighPassCutoff / Device->Frequency);
a = (2.0f-cw) - sqrtf(powf(2.0f-cw, 2.0f) - 1.0f);
state->Filter.b[0] = a;
state->Filter.b[1] = -a;
state->Filter.b[2] = 0.0f;
state->Filter.a[0] = 1.0f;
state->Filter.a[1] = -a;
state->Filter.a[2] = 0.0f;
gain = sqrtf(1.0f/Device->NumChan) * Slot->Gain;
SetGains(Device, gain, state->Gain);
}
static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
{
switch(state->Waveform)
{
case SINUSOID:
ProcessSin(state, SamplesToDo, SamplesIn, SamplesOut);
break;
case SAWTOOTH:
ProcessSaw(state, SamplesToDo, SamplesIn, SamplesOut);
break;
case SQUARE:
ProcessSquare(state, SamplesToDo, SamplesIn, SamplesOut);
break;
}
}
DECLARE_DEFAULT_ALLOCATORS(ALmodulatorState)
DEFINE_ALEFFECTSTATE_VTABLE(ALmodulatorState);
typedef struct ALmodulatorStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALmodulatorStateFactory;
static ALeffectState *ALmodulatorStateFactory_create(ALmodulatorStateFactory *UNUSED(factory))
{
ALmodulatorState *state;
state = ALmodulatorState_New(sizeof(*state));
if(!state) return NULL;
SET_VTABLE2(ALmodulatorState, ALeffectState, state);
state->index = 0;
state->step = 1;
ALfilterState_clear(&state->Filter);
return STATIC_CAST(ALeffectState, state);
}
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALmodulatorStateFactory);
ALeffectStateFactory *ALmodulatorStateFactory_getFactory(void)
{
static ALmodulatorStateFactory ModulatorFactory = { { GET_VTABLE2(ALmodulatorStateFactory, ALeffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &ModulatorFactory);
}
void ALmodulator_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_RING_MODULATOR_FREQUENCY:
if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Modulator.Frequency = val;
break;
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Modulator.HighPassCutoff = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALmodulator_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALmodulator_setParamf(effect, context, param, vals[0]);
}
void ALmodulator_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_RING_MODULATOR_FREQUENCY:
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
ALmodulator_setParamf(effect, context, param, (ALfloat)val);
break;
case AL_RING_MODULATOR_WAVEFORM:
if(!(val >= AL_RING_MODULATOR_MIN_WAVEFORM && val <= AL_RING_MODULATOR_MAX_WAVEFORM))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Modulator.Waveform = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALmodulator_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALmodulator_setParami(effect, context, param, vals[0]);
}
void ALmodulator_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_RING_MODULATOR_FREQUENCY:
*val = (ALint)props->Modulator.Frequency;
break;
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
*val = (ALint)props->Modulator.HighPassCutoff;
break;
case AL_RING_MODULATOR_WAVEFORM:
*val = props->Modulator.Waveform;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALmodulator_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALmodulator_getParami(effect, context, param, vals);
}
void ALmodulator_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_RING_MODULATOR_FREQUENCY:
*val = props->Modulator.Frequency;
break;
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
*val = props->Modulator.HighPassCutoff;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALmodulator_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALmodulator_getParamf(effect, context, param, vals);
}
DEFINE_ALEFFECT_VTABLE(ALmodulator);
+165
View File
@@ -0,0 +1,165 @@
#include "config.h"
#include <stdlib.h>
#include "AL/al.h"
#include "AL/alc.h"
#include "alMain.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
typedef struct ALnullState {
DERIVE_FROM_TYPE(ALeffectState);
} ALnullState;
/* This destructs (not free!) the effect state. It's called only when the
* effect slot is no longer used.
*/
static ALvoid ALnullState_Destruct(ALnullState* UNUSED(state))
{
}
/* This updates the device-dependant effect state. This is called on
* initialization and any time the device parameters (eg. playback frequency,
* format) have been changed.
*/
static ALboolean ALnullState_deviceUpdate(ALnullState* UNUSED(state), ALCdevice* UNUSED(device))
{
return AL_TRUE;
}
/* This updates the effect state. This is called any time the effect is
* (re)loaded into a slot.
*/
static ALvoid ALnullState_update(ALnullState* UNUSED(state), ALCdevice* UNUSED(device), const ALeffectslot* UNUSED(slot))
{
}
/* This processes the effect state, for the given number of samples from the
* input to the output buffer. The result should be added to the output buffer,
* not replace it.
*/
static ALvoid ALnullState_process(ALnullState* UNUSED(state), ALuint UNUSED(samplesToDo), const ALfloat *restrict UNUSED(samplesIn), ALfloat (*restrict samplesOut)[BUFFERSIZE])
{
/* NOTE: Couldn't use the UNUSED macro on samplesOut due to the way GCC's
* __attribute__ declaration interacts with the parenthesis. */
(void)samplesOut;
}
/* This allocates memory to store the object, before it gets constructed.
* DECLARE_DEFAULT_ALLOCATORS can be used to declate a default method.
*/
static void *ALnullState_New(size_t size)
{
return malloc(size);
}
/* This frees the memory used by the object, after it has been destructed.
* DECLARE_DEFAULT_ALLOCATORS can be used to declate a default method.
*/
static void ALnullState_Delete(void *ptr)
{
free(ptr);
}
/* Define the forwards and the ALeffectState vtable for this type. */
DEFINE_ALEFFECTSTATE_VTABLE(ALnullState);
typedef struct ALnullStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALnullStateFactory;
/* Creates ALeffectState objects of the appropriate type. */
ALeffectState *ALnullStateFactory_create(ALnullStateFactory *UNUSED(factory))
{
ALnullState *state;
state = ALnullState_New(sizeof(*state));
if(!state) return NULL;
/* Set vtables for inherited types. */
SET_VTABLE2(ALnullState, ALeffectState, state);
return STATIC_CAST(ALeffectState, state);
}
/* Define the ALeffectStateFactory vtable for this type. */
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALnullStateFactory);
ALeffectStateFactory *ALnullStateFactory_getFactory(void)
{
static ALnullStateFactory NullFactory = { { GET_VTABLE2(ALnullStateFactory, ALeffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &NullFactory);
}
void ALnull_setParami(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
{
switch(param)
{
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALnull_setParamiv(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, const ALint* UNUSED(vals))
{
switch(param)
{
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALnull_setParamf(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat UNUSED(val))
{
switch(param)
{
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALnull_setParamfv(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, const ALfloat* UNUSED(vals))
{
switch(param)
{
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALnull_getParami(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(val))
{
switch(param)
{
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALnull_getParamiv(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(vals))
{
switch(param)
{
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALnull_getParamf(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(val))
{
switch(param)
{
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALnull_getParamfv(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(vals))
{
switch(param)
{
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
DEFINE_ALEFFECT_VTABLE(ALnull);
+1779
View File
File diff suppressed because it is too large Load Diff
+31
View File
@@ -0,0 +1,31 @@
#ifndef AL_EVTQUEUE_H
#define AL_EVTQUEUE_H
#include "AL/al.h"
#include "alMain.h"
typedef struct MidiEvent {
ALuint64 time;
ALuint event;
union {
ALuint val[2];
struct {
ALvoid *data;
ALsizei size;
} sysex;
} param;
} MidiEvent;
typedef struct EvtQueue {
MidiEvent *events;
ALsizei pos;
ALsizei size;
ALsizei maxsize;
} EvtQueue;
void InitEvtQueue(EvtQueue *queue);
void ResetEvtQueue(EvtQueue *queue);
ALenum InsertEvtQueue(EvtQueue *queue, const MidiEvent *evt);
#endif /* AL_EVTQUEUE_H */
+814
View File
@@ -0,0 +1,814 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2011 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#ifdef _WIN32
#ifdef __MINGW32__
#define _WIN32_IE 0x501
#else
#define _WIN32_IE 0x400
#endif
#endif
#include "config.h"
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <stdarg.h>
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
#ifndef AL_NO_UID_DEFS
#if defined(HAVE_GUIDDEF_H) || defined(HAVE_INITGUID_H)
#define INITGUID
#include <windows.h>
#ifdef HAVE_GUIDDEF_H
#include <guiddef.h>
#else
#include <initguid.h>
#endif
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71);
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71);
DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf,0x08, 0x00,0xa0,0xc9,0x25,0xcd,0x16);
DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xbcde0395, 0xe52f, 0x467c, 0x8e,0x3d, 0xc4,0x57,0x92,0x91,0x69,0x2e);
DEFINE_GUID(IID_IMMDeviceEnumerator, 0xa95664d2, 0x9614, 0x4f35, 0xa7,0x46, 0xde,0x8d,0xb6,0x36,0x17,0xe6);
DEFINE_GUID(IID_IAudioClient, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1,0x78, 0xc2,0xf5,0x68,0xa7,0x03,0xb2);
DEFINE_GUID(IID_IAudioRenderClient, 0xf294acfc, 0x3146, 0x4483, 0xa7,0xbf, 0xad,0xdc,0xa7,0xc2,0x60,0xe2);
#ifdef HAVE_MMDEVAPI
#include <devpropdef.h>
DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
#endif
#endif
#endif /* AL_NO_UID_DEFS */
#ifdef HAVE_DLFCN_H
#include <dlfcn.h>
#endif
#ifdef HAVE_INTRIN_H
#include <intrin.h>
#endif
#ifdef HAVE_CPUID_H
#include <cpuid.h>
#endif
#ifdef HAVE_SYS_SYSCONF_H
#include <sys/sysconf.h>
#endif
#ifdef HAVE_FLOAT_H
#include <float.h>
#endif
#ifdef HAVE_IEEEFP_H
#include <ieeefp.h>
#endif
#ifdef _WIN32_IE
#include <shlobj.h>
#endif
#include "alMain.h"
#include "alu.h"
#include "atomic.h"
#include "uintmap.h"
#include "vector.h"
#include "alstring.h"
#include "compat.h"
#include "threads.h"
extern inline ALuint NextPowerOf2(ALuint value);
extern inline ALint fastf2i(ALfloat f);
extern inline ALuint fastf2u(ALfloat f);
ALuint CPUCapFlags = 0;
void FillCPUCaps(ALuint capfilter)
{
ALuint caps = 0;
/* FIXME: We really should get this for all available CPUs in case different
* CPUs have different caps (is that possible on one machine?). */
#if defined(HAVE_GCC_GET_CPUID) && (defined(__i386__) || defined(__x86_64__) || \
defined(_M_IX86) || defined(_M_X64))
union {
unsigned int regs[4];
char str[sizeof(unsigned int[4])];
} cpuinf[3];
if(!__get_cpuid(0, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]))
ERR("Failed to get CPUID\n");
else
{
unsigned int maxfunc = cpuinf[0].regs[0];
unsigned int maxextfunc = 0;
if(__get_cpuid(0x80000000, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]))
maxextfunc = cpuinf[0].regs[0];
TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc, maxextfunc);
TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf[0].str+4, cpuinf[0].str+12, cpuinf[0].str+8);
if(maxextfunc >= 0x80000004 &&
__get_cpuid(0x80000002, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]) &&
__get_cpuid(0x80000003, &cpuinf[1].regs[0], &cpuinf[1].regs[1], &cpuinf[1].regs[2], &cpuinf[1].regs[3]) &&
__get_cpuid(0x80000004, &cpuinf[2].regs[0], &cpuinf[2].regs[1], &cpuinf[2].regs[2], &cpuinf[2].regs[3]))
TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf[0].str, cpuinf[1].str, cpuinf[2].str);
if(maxfunc >= 1 &&
__get_cpuid(1, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]))
{
if((cpuinf[0].regs[3]&(1<<25)))
{
caps |= CPU_CAP_SSE;
if((cpuinf[0].regs[3]&(1<<26)))
{
caps |= CPU_CAP_SSE2;
if((cpuinf[0].regs[2]&(1<<19)))
caps |= CPU_CAP_SSE4_1;
}
}
}
}
#elif defined(HAVE_CPUID_INTRINSIC) && (defined(__i386__) || defined(__x86_64__) || \
defined(_M_IX86) || defined(_M_X64))
union {
int regs[4];
char str[sizeof(int[4])];
} cpuinf[3];
(__cpuid)(cpuinf[0].regs, 0);
if(cpuinf[0].regs[0] == 0)
ERR("Failed to get CPUID\n");
else
{
unsigned int maxfunc = cpuinf[0].regs[0];
unsigned int maxextfunc;
(__cpuid)(cpuinf[0].regs, 0x80000000);
maxextfunc = cpuinf[0].regs[0];
TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc, maxextfunc);
TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf[0].str+4, cpuinf[0].str+12, cpuinf[0].str+8);
if(maxextfunc >= 0x80000004)
{
(__cpuid)(cpuinf[0].regs, 0x80000002);
(__cpuid)(cpuinf[1].regs, 0x80000003);
(__cpuid)(cpuinf[2].regs, 0x80000004);
TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf[0].str, cpuinf[1].str, cpuinf[2].str);
}
if(maxfunc >= 1)
{
(__cpuid)(cpuinf[0].regs, 1);
if((cpuinf[0].regs[3]&(1<<25)))
{
caps |= CPU_CAP_SSE;
if((cpuinf[0].regs[3]&(1<<26)))
{
caps |= CPU_CAP_SSE2;
if((cpuinf[0].regs[2]&(1<<19)))
caps |= CPU_CAP_SSE4_1;
}
}
}
}
#else
/* Assume support for whatever's supported if we can't check for it */
#if defined(HAVE_SSE4_1)
#warning "Assuming SSE 4.1 run-time support!"
capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE4_1;
#elif defined(HAVE_SSE2)
#warning "Assuming SSE 2 run-time support!"
capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2;
#elif defined(HAVE_SSE)
#warning "Assuming SSE run-time support!"
capfilter |= CPU_CAP_SSE;
#endif
#endif
#ifdef HAVE_NEON
/* Assume Neon support if compiled with it */
caps |= CPU_CAP_NEON;
#endif
TRACE("Extensions:%s%s%s%s%s\n",
((capfilter&CPU_CAP_SSE) ? ((caps&CPU_CAP_SSE) ? " +SSE" : " -SSE") : ""),
((capfilter&CPU_CAP_SSE2) ? ((caps&CPU_CAP_SSE2) ? " +SSE2" : " -SSE2") : ""),
((capfilter&CPU_CAP_SSE4_1) ? ((caps&CPU_CAP_SSE4_1) ? " +SSE4.1" : " -SSE4.1") : ""),
((capfilter&CPU_CAP_NEON) ? ((caps&CPU_CAP_NEON) ? " +Neon" : " -Neon") : ""),
((!capfilter) ? " -none-" : "")
);
CPUCapFlags = caps & capfilter;
}
void *al_malloc(size_t alignment, size_t size)
{
#if defined(HAVE_ALIGNED_ALLOC)
size = (size+(alignment-1))&~(alignment-1);
return aligned_alloc(alignment, size);
#elif defined(HAVE_POSIX_MEMALIGN)
void *ret;
if(posix_memalign(&ret, alignment, size) == 0)
return ret;
return NULL;
#elif defined(HAVE__ALIGNED_MALLOC)
return _aligned_malloc(size, alignment);
#else
char *ret = malloc(size+alignment);
if(ret != NULL)
{
*(ret++) = 0x00;
while(((ALintptrEXT)ret&(alignment-1)) != 0)
*(ret++) = 0x55;
}
return ret;
#endif
}
void *al_calloc(size_t alignment, size_t size)
{
void *ret = al_malloc(alignment, size);
if(ret) memset(ret, 0, size);
return ret;
}
void al_free(void *ptr)
{
#if defined(HAVE_ALIGNED_ALLOC) || defined(HAVE_POSIX_MEMALIGN)
free(ptr);
#elif defined(HAVE__ALIGNED_MALLOC)
_aligned_free(ptr);
#else
if(ptr != NULL)
{
char *finder = ptr;
do {
--finder;
} while(*finder == 0x55);
free(finder);
}
#endif
}
void SetMixerFPUMode(FPUCtl *ctl)
{
#ifdef HAVE_FENV_H
fegetenv(STATIC_CAST(fenv_t, ctl));
#if defined(__GNUC__) && defined(HAVE_SSE)
if((CPUCapFlags&CPU_CAP_SSE))
__asm__ __volatile__("stmxcsr %0" : "=m" (*&ctl->sse_state));
#endif
#ifdef FE_TOWARDZERO
fesetround(FE_TOWARDZERO);
#endif
#if defined(__GNUC__) && defined(HAVE_SSE)
if((CPUCapFlags&CPU_CAP_SSE))
{
int sseState = ctl->sse_state;
sseState |= 0x6000; /* set round-to-zero */
sseState |= 0x8000; /* set flush-to-zero */
if((CPUCapFlags&CPU_CAP_SSE2))
sseState |= 0x0040; /* set denormals-are-zero */
__asm__ __volatile__("ldmxcsr %0" : : "m" (*&sseState));
}
#endif
#elif defined(HAVE___CONTROL87_2)
int mode;
__control87_2(0, 0, &ctl->state, NULL);
__control87_2(_RC_CHOP, _MCW_RC, &mode, NULL);
#ifdef HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
{
__control87_2(0, 0, NULL, &ctl->sse_state);
__control87_2(_RC_CHOP|_DN_FLUSH, _MCW_RC|_MCW_DN, NULL, &mode);
}
#endif
#elif defined(HAVE__CONTROLFP)
ctl->state = _controlfp(0, 0);
(void)_controlfp(_RC_CHOP, _MCW_RC);
#endif
}
void RestoreFPUMode(const FPUCtl *ctl)
{
#ifdef HAVE_FENV_H
fesetenv(STATIC_CAST(fenv_t, ctl));
#if defined(__GNUC__) && defined(HAVE_SSE)
if((CPUCapFlags&CPU_CAP_SSE))
__asm__ __volatile__("ldmxcsr %0" : : "m" (*&ctl->sse_state));
#endif
#elif defined(HAVE___CONTROL87_2)
int mode;
__control87_2(ctl->state, _MCW_RC, &mode, NULL);
#ifdef HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
__control87_2(ctl->sse_state, _MCW_RC|_MCW_DN, NULL, &mode);
#endif
#elif defined(HAVE__CONTROLFP)
_controlfp(ctl->state, _MCW_RC);
#endif
}
#ifdef _WIN32
static WCHAR *FromUTF8(const char *str)
{
WCHAR *out = NULL;
int len;
if((len=MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0)) > 0)
{
out = calloc(sizeof(WCHAR), len);
MultiByteToWideChar(CP_UTF8, 0, str, -1, out, len);
}
return out;
}
void *LoadLib(const char *name)
{
HANDLE hdl = NULL;
WCHAR *wname;
wname = FromUTF8(name);
if(!wname)
ERR("Failed to convert UTF-8 filename: \"%s\"\n", name);
else
{
hdl = LoadLibraryW(wname);
free(wname);
}
return hdl;
}
void CloseLib(void *handle)
{ FreeLibrary((HANDLE)handle); }
void *GetSymbol(void *handle, const char *name)
{
void *ret;
ret = (void*)GetProcAddress((HANDLE)handle, name);
if(ret == NULL)
ERR("Failed to load %s\n", name);
return ret;
}
WCHAR *strdupW(const WCHAR *str)
{
const WCHAR *n;
WCHAR *ret;
size_t len;
n = str;
while(*n) n++;
len = n - str;
ret = calloc(sizeof(WCHAR), len+1);
if(ret != NULL)
memcpy(ret, str, sizeof(WCHAR)*len);
return ret;
}
FILE *al_fopen(const char *fname, const char *mode)
{
WCHAR *wname=NULL, *wmode=NULL;
FILE *file = NULL;
wname = FromUTF8(fname);
wmode = FromUTF8(mode);
if(!wname)
ERR("Failed to convert UTF-8 filename: \"%s\"\n", fname);
else if(!wmode)
ERR("Failed to convert UTF-8 mode: \"%s\"\n", mode);
else
file = _wfopen(wname, wmode);
free(wname);
free(wmode);
return file;
}
#else
#ifdef HAVE_DLFCN_H
void *LoadLib(const char *name)
{
const char *err;
void *handle;
dlerror();
handle = dlopen(name, RTLD_NOW);
if((err=dlerror()) != NULL)
handle = NULL;
return handle;
}
void CloseLib(void *handle)
{ dlclose(handle); }
void *GetSymbol(void *handle, const char *name)
{
const char *err;
void *sym;
dlerror();
sym = dlsym(handle, name);
if((err=dlerror()) != NULL)
{
WARN("Failed to load %s: %s\n", name, err);
sym = NULL;
}
return sym;
}
#endif
#endif
void al_print(const char *type, const char *func, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
fprintf(LogFile, "AL lib: %s %s: ", type, func);
vfprintf(LogFile, fmt, ap);
va_end(ap);
fflush(LogFile);
}
#ifdef _WIN32
static inline int is_slash(int c)
{ return (c == '\\' || c == '/'); }
FILE *OpenDataFile(const char *fname, const char *subdir)
{
static const int ids[2] = { CSIDL_APPDATA, CSIDL_COMMON_APPDATA };
WCHAR *wname=NULL, *wsubdir=NULL;
FILE *f;
int i;
/* If the path is absolute, open it directly. */
if(fname[0] != '\0' && fname[1] == ':' && is_slash(fname[2]))
{
if((f=al_fopen(fname, "rb")) != NULL)
{
TRACE("Opened %s\n", fname);
return f;
}
WARN("Could not open %s\n", fname);
return NULL;
}
/* If it's relative, try the current directory first before the data directories. */
if((f=al_fopen(fname, "rb")) != NULL)
{
TRACE("Opened %s\n", fname);
return f;
}
WARN("Could not open %s\n", fname);
wname = FromUTF8(fname);
wsubdir = FromUTF8(subdir);
if(!wname)
ERR("Failed to convert UTF-8 filename: \"%s\"\n", fname);
else if(!wsubdir)
ERR("Failed to convert UTF-8 subdir: \"%s\"\n", subdir);
else for(i = 0;i < 2;i++)
{
WCHAR buffer[PATH_MAX];
size_t len;
if(SHGetSpecialFolderPathW(NULL, buffer, ids[i], FALSE) == FALSE)
continue;
len = lstrlenW(buffer);
if(len > 0 && is_slash(buffer[len-1]))
buffer[--len] = '\0';
_snwprintf(buffer+len, PATH_MAX-len, L"/%ls/%ls", wsubdir, wname);
len = lstrlenW(buffer);
while(len > 0)
{
--len;
if(buffer[len] == '/')
buffer[len] = '\\';
}
if((f=_wfopen(buffer, L"rb")) != NULL)
{
TRACE("Opened %ls\n", buffer);
return f;
}
WARN("Could not open %ls\n", buffer);
}
free(wname);
free(wsubdir);
return NULL;
}
#else
FILE *OpenDataFile(const char *fname, const char *subdir)
{
char buffer[PATH_MAX] = "";
const char *str, *next;
FILE *f;
if(fname[0] == '/')
{
if((f=al_fopen(fname, "rb")) != NULL)
{
TRACE("Opened %s\n", fname);
return f;
}
WARN("Could not open %s\n", fname);
return NULL;
}
if((f=al_fopen(fname, "rb")) != NULL)
{
TRACE("Opened %s\n", fname);
return f;
}
WARN("Could not open %s\n", fname);
if((str=getenv("XDG_DATA_HOME")) != NULL && str[0] != '\0')
snprintf(buffer, sizeof(buffer), "%s/%s/%s", str, subdir, fname);
else if((str=getenv("HOME")) != NULL && str[0] != '\0')
snprintf(buffer, sizeof(buffer), "%s/.local/share/%s/%s", str, subdir, fname);
if(buffer[0])
{
if((f=al_fopen(buffer, "rb")) != NULL)
{
TRACE("Opened %s\n", buffer);
return f;
}
WARN("Could not open %s\n", buffer);
}
if((str=getenv("XDG_DATA_DIRS")) == NULL || str[0] == '\0')
str = "/usr/local/share/:/usr/share/";
next = str;
while((str=next) != NULL && str[0] != '\0')
{
size_t len;
next = strchr(str, ':');
if(!next)
len = strlen(str);
else
{
len = next - str;
next++;
}
if(len > sizeof(buffer)-1)
len = sizeof(buffer)-1;
strncpy(buffer, str, len);
buffer[len] = '\0';
snprintf(buffer+len, sizeof(buffer)-len, "/%s/%s", subdir, fname);
if((f=al_fopen(buffer, "rb")) != NULL)
{
TRACE("Opened %s\n", buffer);
return f;
}
WARN("Could not open %s\n", buffer);
}
return NULL;
}
#endif
void SetRTPriority(void)
{
ALboolean failed = AL_FALSE;
#ifdef _WIN32
if(RTPrioLevel > 0)
failed = !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
#elif defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__)
if(RTPrioLevel > 0)
{
struct sched_param param;
/* Use the minimum real-time priority possible for now (on Linux this
* should be 1 for SCHED_RR) */
param.sched_priority = sched_get_priority_min(SCHED_RR);
failed = !!pthread_setschedparam(pthread_self(), SCHED_RR, &param);
}
#else
/* Real-time priority not available */
failed = (RTPrioLevel>0);
#endif
if(failed)
ERR("Failed to set priority level for thread\n");
}
ALboolean vector_reserve(char *ptr, size_t base_size, size_t obj_size, ALsizei obj_count, ALboolean exact)
{
vector_ *vecptr = (vector_*)ptr;
if(obj_count < 0)
return AL_FALSE;
if((*vecptr ? (*vecptr)->Capacity : 0) < obj_count)
{
ALsizei old_size = (*vecptr ? (*vecptr)->Size : 0);
void *temp;
/* Use the next power-of-2 size if we don't need to allocate the exact
* amount. This is preferred when regularly increasing the vector since
* it means fewer reallocations. Though it means it also wastes some
* memory. */
if(exact == AL_FALSE)
{
obj_count = NextPowerOf2((ALuint)obj_count);
if(obj_count < 0) return AL_FALSE;
}
/* Need to be explicit with the caller type's base size, because it
* could have extra padding before the start of the array (that is,
* sizeof(*vector_) may not equal base_size). */
temp = realloc(*vecptr, base_size + obj_size*obj_count);
if(temp == NULL) return AL_FALSE;
*vecptr = temp;
(*vecptr)->Capacity = obj_count;
(*vecptr)->Size = old_size;
}
return AL_TRUE;
}
ALboolean vector_resize(char *ptr, size_t base_size, size_t obj_size, ALsizei obj_count)
{
vector_ *vecptr = (vector_*)ptr;
if(obj_count < 0)
return AL_FALSE;
if(*vecptr || obj_count > 0)
{
if(!vector_reserve((char*)vecptr, base_size, obj_size, obj_count, AL_TRUE))
return AL_FALSE;
(*vecptr)->Size = obj_count;
}
return AL_TRUE;
}
ALboolean vector_insert(char *ptr, size_t base_size, size_t obj_size, void *ins_pos, const void *datstart, const void *datend)
{
vector_ *vecptr = (vector_*)ptr;
if(datstart != datend)
{
ptrdiff_t ins_elem = (*vecptr ? ((char*)ins_pos - ((char*)(*vecptr) + base_size)) :
((char*)ins_pos - (char*)NULL)) /
obj_size;
ptrdiff_t numins = ((const char*)datend - (const char*)datstart) / obj_size;
assert(numins > 0);
if(INT_MAX-VECTOR_SIZE(*vecptr) <= numins ||
!vector_reserve((char*)vecptr, base_size, obj_size, VECTOR_SIZE(*vecptr)+numins, AL_TRUE))
return AL_FALSE;
/* NOTE: ins_pos may have been invalidated if *vecptr moved. Use ins_elem instead. */
if(ins_elem < (*vecptr)->Size)
{
memmove((char*)(*vecptr) + base_size + ((ins_elem+numins)*obj_size),
(char*)(*vecptr) + base_size + ((ins_elem )*obj_size),
((*vecptr)->Size-ins_elem)*obj_size);
}
memcpy((char*)(*vecptr) + base_size + (ins_elem*obj_size),
datstart, numins*obj_size);
(*vecptr)->Size += (ALsizei)numins;
}
return AL_TRUE;
}
extern inline void al_string_deinit(al_string *str);
extern inline ALsizei al_string_length(const_al_string str);
extern inline ALboolean al_string_empty(const_al_string str);
extern inline const al_string_char_type *al_string_get_cstr(const_al_string str);
void al_string_clear(al_string *str)
{
/* Reserve one more character than the total size of the string. This is to
* ensure we have space to add a null terminator in the string data so it
* can be used as a C-style string. */
VECTOR_RESERVE(*str, 1);
VECTOR_RESIZE(*str, 0);
*VECTOR_ITER_END(*str) = 0;
}
static inline int al_string_compare(const al_string_char_type *str1, ALsizei str1len,
const al_string_char_type *str2, ALsizei str2len)
{
ALsizei complen = mini(str1len, str2len);
int ret = memcmp(str1, str2, complen);
if(ret == 0)
{
if(str1len > str2len) return 1;
if(str1len < str2len) return -1;
}
return ret;
}
int al_string_cmp(const_al_string str1, const_al_string str2)
{
return al_string_compare(&VECTOR_FRONT(str1), al_string_length(str1),
&VECTOR_FRONT(str2), al_string_length(str2));
}
int al_string_cmp_cstr(const_al_string str1, const al_string_char_type *str2)
{
return al_string_compare(&VECTOR_FRONT(str1), al_string_length(str1),
str2, (ALsizei)strlen(str2));
}
void al_string_copy(al_string *str, const_al_string from)
{
ALsizei len = VECTOR_SIZE(from);
VECTOR_RESERVE(*str, len+1);
VECTOR_RESIZE(*str, 0);
VECTOR_INSERT(*str, VECTOR_ITER_END(*str), VECTOR_ITER_BEGIN(from), VECTOR_ITER_BEGIN(from)+len);
*VECTOR_ITER_END(*str) = 0;
}
void al_string_copy_cstr(al_string *str, const al_string_char_type *from)
{
size_t len = strlen(from);
VECTOR_RESERVE(*str, len+1);
VECTOR_RESIZE(*str, 0);
VECTOR_INSERT(*str, VECTOR_ITER_END(*str), from, from+len);
*VECTOR_ITER_END(*str) = 0;
}
void al_string_append_char(al_string *str, const al_string_char_type c)
{
VECTOR_RESERVE(*str, al_string_length(*str)+2);
VECTOR_PUSH_BACK(*str, c);
*VECTOR_ITER_END(*str) = 0;
}
void al_string_append_cstr(al_string *str, const al_string_char_type *from)
{
size_t len = strlen(from);
if(len != 0)
{
VECTOR_RESERVE(*str, al_string_length(*str)+len+1);
VECTOR_INSERT(*str, VECTOR_ITER_END(*str), from, from+len);
*VECTOR_ITER_END(*str) = 0;
}
}
void al_string_append_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to)
{
if(to != from)
{
VECTOR_RESERVE(*str, al_string_length(*str)+(to-from)+1);
VECTOR_INSERT(*str, VECTOR_ITER_END(*str), from, to);
*VECTOR_ITER_END(*str) = 0;
}
}
#ifdef _WIN32
void al_string_copy_wcstr(al_string *str, const wchar_t *from)
{
int len;
if((len=WideCharToMultiByte(CP_UTF8, 0, from, -1, NULL, 0, NULL, NULL)) > 0)
{
VECTOR_RESERVE(*str, len);
VECTOR_RESIZE(*str, len-1);
WideCharToMultiByte(CP_UTF8, 0, from, -1, &VECTOR_FRONT(*str), len, NULL, NULL);
*VECTOR_ITER_END(*str) = 0;
}
}
#endif
+820
View File
@@ -0,0 +1,820 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2011 by Chris Robinson
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdlib.h>
#include <ctype.h>
#include "AL/al.h"
#include "AL/alc.h"
#include "alMain.h"
#include "alSource.h"
#include "alu.h"
#include "hrtf.h"
/* Current data set limits defined by the makehrtf utility. */
#define MIN_IR_SIZE (8)
#define MAX_IR_SIZE (128)
#define MOD_IR_SIZE (8)
#define MIN_EV_COUNT (5)
#define MAX_EV_COUNT (128)
#define MIN_AZ_COUNT (1)
#define MAX_AZ_COUNT (128)
struct Hrtf {
ALuint sampleRate;
ALuint irSize;
ALubyte evCount;
const ALubyte *azCount;
const ALushort *evOffset;
const ALshort *coeffs;
const ALubyte *delays;
struct Hrtf *next;
};
static const ALchar magicMarker00[8] = "MinPHR00";
static const ALchar magicMarker01[8] = "MinPHR01";
/* First value for pass-through coefficients (remaining are 0), used for omni-
* directional sounds. */
static const ALfloat PassthruCoeff = 32767.0f * 0.707106781187f/*sqrt(0.5)*/;
static struct Hrtf *LoadedHrtfs = NULL;
/* Calculate the elevation indices given the polar elevation in radians.
* This will return two indices between 0 and (evcount - 1) and an
* interpolation factor between 0.0 and 1.0.
*/
static void CalcEvIndices(ALuint evcount, ALfloat ev, ALuint *evidx, ALfloat *evmu)
{
ev = (F_PI_2 + ev) * (evcount-1) / F_PI;
evidx[0] = fastf2u(ev);
evidx[1] = minu(evidx[0] + 1, evcount-1);
*evmu = ev - evidx[0];
}
/* Calculate the azimuth indices given the polar azimuth in radians. This
* will return two indices between 0 and (azcount - 1) and an interpolation
* factor between 0.0 and 1.0.
*/
static void CalcAzIndices(ALuint azcount, ALfloat az, ALuint *azidx, ALfloat *azmu)
{
az = (F_2PI + az) * azcount / (F_2PI);
azidx[0] = fastf2u(az) % azcount;
azidx[1] = (azidx[0] + 1) % azcount;
*azmu = az - floorf(az);
}
/* Calculates the normalized HRTF transition factor (delta) from the changes
* in gain and listener to source angle between updates. The result is a
* normalized delta factor that can be used to calculate moving HRIR stepping
* values.
*/
ALfloat CalcHrtfDelta(ALfloat oldGain, ALfloat newGain, const ALfloat olddir[3], const ALfloat newdir[3])
{
ALfloat gainChange, angleChange, change;
// Calculate the normalized dB gain change.
newGain = maxf(newGain, 0.0001f);
oldGain = maxf(oldGain, 0.0001f);
gainChange = fabsf(log10f(newGain / oldGain) / log10f(0.0001f));
// Calculate the angle change only when there is enough gain to notice it.
angleChange = 0.0f;
if(gainChange > 0.0001f || newGain > 0.0001f)
{
// No angle change when the directions are equal or degenerate (when
// both have zero length).
if(newdir[0] != olddir[0] || newdir[1] != olddir[1] || newdir[2] != olddir[2])
{
ALfloat dotp = olddir[0]*newdir[0] + olddir[1]*newdir[1] + olddir[2]*newdir[2];
angleChange = acosf(clampf(dotp, -1.0f, 1.0f)) / F_PI;
}
}
// Use the largest of the two changes for the delta factor, and apply a
// significance shaping function to it.
change = maxf(angleChange * 25.0f, gainChange) * 2.0f;
return minf(change, 1.0f);
}
/* Calculates static HRIR coefficients and delays for the given polar
* elevation and azimuth in radians. Linear interpolation is used to
* increase the apparent resolution of the HRIR data set. The coefficients
* are also normalized and attenuated by the specified gain.
*/
void GetLerpedHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat (*coeffs)[2], ALuint *delays)
{
ALuint evidx[2], lidx[4], ridx[4];
ALfloat mu[3], blend[4];
ALuint i;
/* Claculate elevation indices and interpolation factor. */
CalcEvIndices(Hrtf->evCount, elevation, evidx, &mu[2]);
for(i = 0;i < 2;i++)
{
ALuint azcount = Hrtf->azCount[evidx[i]];
ALuint evoffset = Hrtf->evOffset[evidx[i]];
ALuint azidx[2];
/* Calculate azimuth indices and interpolation factor for this elevation. */
CalcAzIndices(azcount, azimuth, azidx, &mu[i]);
/* Calculate a set of linear HRIR indices for left and right channels. */
lidx[i*2 + 0] = evoffset + azidx[0];
lidx[i*2 + 1] = evoffset + azidx[1];
ridx[i*2 + 0] = evoffset + ((azcount-azidx[0]) % azcount);
ridx[i*2 + 1] = evoffset + ((azcount-azidx[1]) % azcount);
}
/* Calculate 4 blending weights for 2D bilinear interpolation. */
blend[0] = (1.0f-mu[0]) * (1.0f-mu[2]);
blend[1] = ( mu[0]) * (1.0f-mu[2]);
blend[2] = (1.0f-mu[1]) * ( mu[2]);
blend[3] = ( mu[1]) * ( mu[2]);
/* Calculate the HRIR delays using linear interpolation. */
delays[0] = fastf2u((Hrtf->delays[lidx[0]]*blend[0] + Hrtf->delays[lidx[1]]*blend[1] +
Hrtf->delays[lidx[2]]*blend[2] + Hrtf->delays[lidx[3]]*blend[3]) *
dirfact + 0.5f) << HRTFDELAY_BITS;
delays[1] = fastf2u((Hrtf->delays[ridx[0]]*blend[0] + Hrtf->delays[ridx[1]]*blend[1] +
Hrtf->delays[ridx[2]]*blend[2] + Hrtf->delays[ridx[3]]*blend[3]) *
dirfact + 0.5f) << HRTFDELAY_BITS;
/* Calculate the sample offsets for the HRIR indices. */
lidx[0] *= Hrtf->irSize;
lidx[1] *= Hrtf->irSize;
lidx[2] *= Hrtf->irSize;
lidx[3] *= Hrtf->irSize;
ridx[0] *= Hrtf->irSize;
ridx[1] *= Hrtf->irSize;
ridx[2] *= Hrtf->irSize;
ridx[3] *= Hrtf->irSize;
/* Calculate the normalized and attenuated HRIR coefficients using linear
* interpolation when there is enough gain to warrant it. Zero the
* coefficients if gain is too low.
*/
if(gain > 0.0001f)
{
ALfloat c;
gain *= 1.0f/32767.0f;
i = 0;
c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
coeffs[i][0] = lerp(PassthruCoeff, c, dirfact) * gain;
c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
coeffs[i][1] = lerp(PassthruCoeff, c, dirfact) * gain;
for(i = 1;i < Hrtf->irSize;i++)
{
c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
coeffs[i][0] = lerp(0.0f, c, dirfact) * gain;
c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
coeffs[i][1] = lerp(0.0f, c, dirfact) * gain;
}
}
else
{
for(i = 0;i < Hrtf->irSize;i++)
{
coeffs[i][0] = 0.0f;
coeffs[i][1] = 0.0f;
}
}
}
/* Calculates the moving HRIR target coefficients, target delays, and
* stepping values for the given polar elevation and azimuth in radians.
* Linear interpolation is used to increase the apparent resolution of the
* HRIR data set. The coefficients are also normalized and attenuated by the
* specified gain. Stepping resolution and count is determined using the
* given delta factor between 0.0 and 1.0.
*/
ALuint GetMovingHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat delta, ALint counter, ALfloat (*coeffs)[2], ALuint *delays, ALfloat (*coeffStep)[2], ALint *delayStep)
{
ALuint evidx[2], lidx[4], ridx[4];
ALfloat mu[3], blend[4];
ALfloat left, right;
ALfloat step;
ALuint i;
/* Claculate elevation indices and interpolation factor. */
CalcEvIndices(Hrtf->evCount, elevation, evidx, &mu[2]);
for(i = 0;i < 2;i++)
{
ALuint azcount = Hrtf->azCount[evidx[i]];
ALuint evoffset = Hrtf->evOffset[evidx[i]];
ALuint azidx[2];
/* Calculate azimuth indices and interpolation factor for this elevation. */
CalcAzIndices(azcount, azimuth, azidx, &mu[i]);
/* Calculate a set of linear HRIR indices for left and right channels. */
lidx[i*2 + 0] = evoffset + azidx[0];
lidx[i*2 + 1] = evoffset + azidx[1];
ridx[i*2 + 0] = evoffset + ((azcount-azidx[0]) % azcount);
ridx[i*2 + 1] = evoffset + ((azcount-azidx[1]) % azcount);
}
// Calculate the stepping parameters.
delta = maxf(floorf(delta*(Hrtf->sampleRate*0.015f) + 0.5f), 1.0f);
step = 1.0f / delta;
/* Calculate 4 blending weights for 2D bilinear interpolation. */
blend[0] = (1.0f-mu[0]) * (1.0f-mu[2]);
blend[1] = ( mu[0]) * (1.0f-mu[2]);
blend[2] = (1.0f-mu[1]) * ( mu[2]);
blend[3] = ( mu[1]) * ( mu[2]);
/* Calculate the HRIR delays using linear interpolation. Then calculate
* the delay stepping values using the target and previous running
* delays.
*/
left = (ALfloat)(delays[0] - (delayStep[0] * counter));
right = (ALfloat)(delays[1] - (delayStep[1] * counter));
delays[0] = fastf2u((Hrtf->delays[lidx[0]]*blend[0] + Hrtf->delays[lidx[1]]*blend[1] +
Hrtf->delays[lidx[2]]*blend[2] + Hrtf->delays[lidx[3]]*blend[3]) *
dirfact + 0.5f) << HRTFDELAY_BITS;
delays[1] = fastf2u((Hrtf->delays[ridx[0]]*blend[0] + Hrtf->delays[ridx[1]]*blend[1] +
Hrtf->delays[ridx[2]]*blend[2] + Hrtf->delays[ridx[3]]*blend[3]) *
dirfact + 0.5f) << HRTFDELAY_BITS;
delayStep[0] = fastf2i(step * (delays[0] - left));
delayStep[1] = fastf2i(step * (delays[1] - right));
/* Calculate the sample offsets for the HRIR indices. */
lidx[0] *= Hrtf->irSize;
lidx[1] *= Hrtf->irSize;
lidx[2] *= Hrtf->irSize;
lidx[3] *= Hrtf->irSize;
ridx[0] *= Hrtf->irSize;
ridx[1] *= Hrtf->irSize;
ridx[2] *= Hrtf->irSize;
ridx[3] *= Hrtf->irSize;
/* Calculate the normalized and attenuated target HRIR coefficients using
* linear interpolation when there is enough gain to warrant it. Zero
* the target coefficients if gain is too low. Then calculate the
* coefficient stepping values using the target and previous running
* coefficients.
*/
if(gain > 0.0001f)
{
ALfloat c;
gain *= 1.0f/32767.0f;
i = 0;
left = coeffs[i][0] - (coeffStep[i][0] * counter);
right = coeffs[i][1] - (coeffStep[i][1] * counter);
c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
coeffs[i][0] = lerp(PassthruCoeff, c, dirfact) * gain;
c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
coeffs[i][1] = lerp(PassthruCoeff, c, dirfact) * gain;
coeffStep[i][0] = step * (coeffs[i][0] - left);
coeffStep[i][1] = step * (coeffs[i][1] - right);
for(i = 1;i < Hrtf->irSize;i++)
{
left = coeffs[i][0] - (coeffStep[i][0] * counter);
right = coeffs[i][1] - (coeffStep[i][1] * counter);
c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
coeffs[i][0] = lerp(0.0f, c, dirfact) * gain;
c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
coeffs[i][1] = lerp(0.0f, c, dirfact) * gain;
coeffStep[i][0] = step * (coeffs[i][0] - left);
coeffStep[i][1] = step * (coeffs[i][1] - right);
}
}
else
{
for(i = 0;i < Hrtf->irSize;i++)
{
left = coeffs[i][0] - (coeffStep[i][0] * counter);
right = coeffs[i][1] - (coeffStep[i][1] * counter);
coeffs[i][0] = 0.0f;
coeffs[i][1] = 0.0f;
coeffStep[i][0] = step * -left;
coeffStep[i][1] = step * -right;
}
}
/* The stepping count is the number of samples necessary for the HRIR to
* complete its transition. The mixer will only apply stepping for this
* many samples.
*/
return fastf2u(delta);
}
static struct Hrtf *LoadHrtf00(FILE *f, ALuint deviceRate)
{
const ALubyte maxDelay = SRC_HISTORY_LENGTH-1;
struct Hrtf *Hrtf = NULL;
ALboolean failed = AL_FALSE;
ALuint rate = 0, irCount = 0;
ALushort irSize = 0;
ALubyte evCount = 0;
ALubyte *azCount = NULL;
ALushort *evOffset = NULL;
ALshort *coeffs = NULL;
ALubyte *delays = NULL;
ALuint i, j;
rate = fgetc(f);
rate |= fgetc(f)<<8;
rate |= fgetc(f)<<16;
rate |= fgetc(f)<<24;
irCount = fgetc(f);
irCount |= fgetc(f)<<8;
irSize = fgetc(f);
irSize |= fgetc(f)<<8;
evCount = fgetc(f);
if(rate != deviceRate)
{
ERR("HRIR rate does not match device rate: rate=%d (%d)\n",
rate, deviceRate);
failed = AL_TRUE;
}
if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE))
{
ERR("Unsupported HRIR size: irSize=%d (%d to %d by %d)\n",
irSize, MIN_IR_SIZE, MAX_IR_SIZE, MOD_IR_SIZE);
failed = AL_TRUE;
}
if(evCount < MIN_EV_COUNT || evCount > MAX_EV_COUNT)
{
ERR("Unsupported elevation count: evCount=%d (%d to %d)\n",
evCount, MIN_EV_COUNT, MAX_EV_COUNT);
failed = AL_TRUE;
}
if(failed)
return NULL;
azCount = malloc(sizeof(azCount[0])*evCount);
evOffset = malloc(sizeof(evOffset[0])*evCount);
if(azCount == NULL || evOffset == NULL)
{
ERR("Out of memory.\n");
failed = AL_TRUE;
}
if(!failed)
{
evOffset[0] = fgetc(f);
evOffset[0] |= fgetc(f)<<8;
for(i = 1;i < evCount;i++)
{
evOffset[i] = fgetc(f);
evOffset[i] |= fgetc(f)<<8;
if(evOffset[i] <= evOffset[i-1])
{
ERR("Invalid evOffset: evOffset[%d]=%d (last=%d)\n",
i, evOffset[i], evOffset[i-1]);
failed = AL_TRUE;
}
azCount[i-1] = evOffset[i] - evOffset[i-1];
if(azCount[i-1] < MIN_AZ_COUNT || azCount[i-1] > MAX_AZ_COUNT)
{
ERR("Unsupported azimuth count: azCount[%d]=%d (%d to %d)\n",
i-1, azCount[i-1], MIN_AZ_COUNT, MAX_AZ_COUNT);
failed = AL_TRUE;
}
}
if(irCount <= evOffset[i-1])
{
ERR("Invalid evOffset: evOffset[%d]=%d (irCount=%d)\n",
i-1, evOffset[i-1], irCount);
failed = AL_TRUE;
}
azCount[i-1] = irCount - evOffset[i-1];
if(azCount[i-1] < MIN_AZ_COUNT || azCount[i-1] > MAX_AZ_COUNT)
{
ERR("Unsupported azimuth count: azCount[%d]=%d (%d to %d)\n",
i-1, azCount[i-1], MIN_AZ_COUNT, MAX_AZ_COUNT);
failed = AL_TRUE;
}
}
if(!failed)
{
coeffs = malloc(sizeof(coeffs[0])*irSize*irCount);
delays = malloc(sizeof(delays[0])*irCount);
if(coeffs == NULL || delays == NULL)
{
ERR("Out of memory.\n");
failed = AL_TRUE;
}
}
if(!failed)
{
for(i = 0;i < irCount*irSize;i+=irSize)
{
for(j = 0;j < irSize;j++)
{
ALshort coeff;
coeff = fgetc(f);
coeff |= fgetc(f)<<8;
coeffs[i+j] = coeff;
}
}
for(i = 0;i < irCount;i++)
{
delays[i] = fgetc(f);
if(delays[i] > maxDelay)
{
ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i], maxDelay);
failed = AL_TRUE;
}
}
if(feof(f))
{
ERR("Premature end of data\n");
failed = AL_TRUE;
}
}
if(!failed)
{
Hrtf = malloc(sizeof(struct Hrtf));
if(Hrtf == NULL)
{
ERR("Out of memory.\n");
failed = AL_TRUE;
}
}
if(!failed)
{
Hrtf->sampleRate = rate;
Hrtf->irSize = irSize;
Hrtf->evCount = evCount;
Hrtf->azCount = azCount;
Hrtf->evOffset = evOffset;
Hrtf->coeffs = coeffs;
Hrtf->delays = delays;
Hrtf->next = NULL;
return Hrtf;
}
free(azCount);
free(evOffset);
free(coeffs);
free(delays);
return NULL;
}
static struct Hrtf *LoadHrtf01(FILE *f, ALuint deviceRate)
{
const ALubyte maxDelay = SRC_HISTORY_LENGTH-1;
struct Hrtf *Hrtf = NULL;
ALboolean failed = AL_FALSE;
ALuint rate = 0, irCount = 0;
ALubyte irSize = 0, evCount = 0;
ALubyte *azCount = NULL;
ALushort *evOffset = NULL;
ALshort *coeffs = NULL;
ALubyte *delays = NULL;
ALuint i, j;
rate = fgetc(f);
rate |= fgetc(f)<<8;
rate |= fgetc(f)<<16;
rate |= fgetc(f)<<24;
irSize = fgetc(f);
evCount = fgetc(f);
if(rate != deviceRate)
{
ERR("HRIR rate does not match device rate: rate=%d (%d)\n",
rate, deviceRate);
failed = AL_TRUE;
}
if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE))
{
ERR("Unsupported HRIR size: irSize=%d (%d to %d by %d)\n",
irSize, MIN_IR_SIZE, MAX_IR_SIZE, MOD_IR_SIZE);
failed = AL_TRUE;
}
if(evCount < MIN_EV_COUNT || evCount > MAX_EV_COUNT)
{
ERR("Unsupported elevation count: evCount=%d (%d to %d)\n",
evCount, MIN_EV_COUNT, MAX_EV_COUNT);
failed = AL_TRUE;
}
if(failed)
return NULL;
azCount = malloc(sizeof(azCount[0])*evCount);
evOffset = malloc(sizeof(evOffset[0])*evCount);
if(azCount == NULL || evOffset == NULL)
{
ERR("Out of memory.\n");
failed = AL_TRUE;
}
if(!failed)
{
for(i = 0;i < evCount;i++)
{
azCount[i] = fgetc(f);
if(azCount[i] < MIN_AZ_COUNT || azCount[i] > MAX_AZ_COUNT)
{
ERR("Unsupported azimuth count: azCount[%d]=%d (%d to %d)\n",
i, azCount[i], MIN_AZ_COUNT, MAX_AZ_COUNT);
failed = AL_TRUE;
}
}
}
if(!failed)
{
evOffset[0] = 0;
irCount = azCount[0];
for(i = 1;i < evCount;i++)
{
evOffset[i] = evOffset[i-1] + azCount[i-1];
irCount += azCount[i];
}
coeffs = malloc(sizeof(coeffs[0])*irSize*irCount);
delays = malloc(sizeof(delays[0])*irCount);
if(coeffs == NULL || delays == NULL)
{
ERR("Out of memory.\n");
failed = AL_TRUE;
}
}
if(!failed)
{
for(i = 0;i < irCount*irSize;i+=irSize)
{
for(j = 0;j < irSize;j++)
{
ALshort coeff;
coeff = fgetc(f);
coeff |= fgetc(f)<<8;
coeffs[i+j] = coeff;
}
}
for(i = 0;i < irCount;i++)
{
delays[i] = fgetc(f);
if(delays[i] > maxDelay)
{
ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i], maxDelay);
failed = AL_TRUE;
}
}
if(feof(f))
{
ERR("Premature end of data\n");
failed = AL_TRUE;
}
}
if(!failed)
{
Hrtf = malloc(sizeof(struct Hrtf));
if(Hrtf == NULL)
{
ERR("Out of memory.\n");
failed = AL_TRUE;
}
}
if(!failed)
{
Hrtf->sampleRate = rate;
Hrtf->irSize = irSize;
Hrtf->evCount = evCount;
Hrtf->azCount = azCount;
Hrtf->evOffset = evOffset;
Hrtf->coeffs = coeffs;
Hrtf->delays = delays;
Hrtf->next = NULL;
return Hrtf;
}
free(azCount);
free(evOffset);
free(coeffs);
free(delays);
return NULL;
}
static struct Hrtf *LoadHrtf(ALuint deviceRate)
{
const char *fnamelist = "default-%r.mhr";
ConfigValueStr(NULL, "hrtf_tables", &fnamelist);
while(*fnamelist != '\0')
{
struct Hrtf *Hrtf = NULL;
char fname[PATH_MAX];
const char *next;
ALchar magic[8];
ALuint i;
FILE *f;
i = 0;
while(isspace(*fnamelist) || *fnamelist == ',')
fnamelist++;
next = fnamelist;
while(*(fnamelist=next) != '\0' && *fnamelist != ',')
{
next = strpbrk(fnamelist, "%,");
while(fnamelist != next && *fnamelist && i < sizeof(fname))
fname[i++] = *(fnamelist++);
if(!next || *next == ',')
break;
/* *next == '%' */
next++;
if(*next == 'r')
{
int wrote = snprintf(&fname[i], sizeof(fname)-i, "%u", deviceRate);
i += minu(wrote, sizeof(fname)-i);
next++;
}
else if(*next == '%')
{
if(i < sizeof(fname))
fname[i++] = '%';
next++;
}
else
ERR("Invalid marker '%%%c'\n", *next);
}
i = minu(i, sizeof(fname)-1);
fname[i] = '\0';
while(i > 0 && isspace(fname[i-1]))
i--;
fname[i] = '\0';
if(fname[0] == '\0')
continue;
TRACE("Loading %s...\n", fname);
f = OpenDataFile(fname, "openal/hrtf");
if(f == NULL)
{
ERR("Could not open %s\n", fname);
continue;
}
if(fread(magic, 1, sizeof(magic), f) != sizeof(magic))
ERR("Failed to read header from %s\n", fname);
else
{
if(memcmp(magic, magicMarker00, sizeof(magicMarker00)) == 0)
{
TRACE("Detected data set format v0\n");
Hrtf = LoadHrtf00(f, deviceRate);
}
else if(memcmp(magic, magicMarker01, sizeof(magicMarker01)) == 0)
{
TRACE("Detected data set format v1\n");
Hrtf = LoadHrtf01(f, deviceRate);
}
else
ERR("Invalid header in %s: \"%.8s\"\n", fname, magic);
}
fclose(f);
f = NULL;
if(Hrtf)
{
Hrtf->next = LoadedHrtfs;
LoadedHrtfs = Hrtf;
TRACE("Loaded HRTF support for format: %s %uhz\n",
DevFmtChannelsString(DevFmtStereo), Hrtf->sampleRate);
return Hrtf;
}
ERR("Failed to load %s\n", fname);
}
return NULL;
}
const struct Hrtf *GetHrtf(enum DevFmtChannels chans, ALCuint srate)
{
if(chans == DevFmtStereo)
{
struct Hrtf *Hrtf = LoadedHrtfs;
while(Hrtf != NULL)
{
if(srate == Hrtf->sampleRate)
return Hrtf;
Hrtf = Hrtf->next;
}
Hrtf = LoadHrtf(srate);
if(Hrtf != NULL)
return Hrtf;
}
ERR("Incompatible format: %s %uhz\n", DevFmtChannelsString(chans), srate);
return NULL;
}
ALCboolean FindHrtfFormat(enum DevFmtChannels *chans, ALCuint *srate)
{
const struct Hrtf *hrtf = LoadedHrtfs;
while(hrtf != NULL)
{
if(*srate == hrtf->sampleRate)
break;
hrtf = hrtf->next;
}
if(hrtf == NULL)
{
hrtf = LoadHrtf(*srate);
if(hrtf == NULL) return ALC_FALSE;
}
*chans = DevFmtStereo;
*srate = hrtf->sampleRate;
return ALC_TRUE;
}
void FreeHrtfs(void)
{
struct Hrtf *Hrtf = NULL;
while((Hrtf=LoadedHrtfs) != NULL)
{
LoadedHrtfs = Hrtf->next;
free((void*)Hrtf->azCount);
free((void*)Hrtf->evOffset);
free((void*)Hrtf->coeffs);
free((void*)Hrtf->delays);
free(Hrtf);
}
}
ALuint GetHrtfIrSize (const struct Hrtf *Hrtf)
{
return Hrtf->irSize;
}
+28
View File
@@ -0,0 +1,28 @@
#ifndef ALC_HRTF_H
#define ALC_HRTF_H
#include "AL/al.h"
#include "AL/alc.h"
enum DevFmtChannels;
struct Hrtf;
#define HRIR_BITS (7)
#define HRIR_LENGTH (1<<HRIR_BITS)
#define HRIR_MASK (HRIR_LENGTH-1)
#define HRTFDELAY_BITS (20)
#define HRTFDELAY_FRACONE (1<<HRTFDELAY_BITS)
#define HRTFDELAY_MASK (HRTFDELAY_FRACONE-1)
const struct Hrtf *GetHrtf(enum DevFmtChannels chans, ALCuint srate);
ALCboolean FindHrtfFormat(enum DevFmtChannels *chans, ALCuint *srate);
void FreeHrtfs(void);
ALuint GetHrtfIrSize(const struct Hrtf *Hrtf);
ALfloat CalcHrtfDelta(ALfloat oldGain, ALfloat newGain, const ALfloat olddir[3], const ALfloat newdir[3]);
void GetLerpedHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat (*coeffs)[2], ALuint *delays);
ALuint GetMovingHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat delta, ALint counter, ALfloat (*coeffs)[2], ALuint *delays, ALfloat (*coeffStep)[2], ALint *delayStep);
#endif /* ALC_HRTF_H */
+244
View File
@@ -0,0 +1,244 @@
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "midi/base.h"
#include "alMidi.h"
#include "alMain.h"
#include "alError.h"
#include "alThunk.h"
#include "evtqueue.h"
#include "rwlock.h"
#include "alu.h"
extern inline ALboolean IsValidCtrlInput(int cc);
extern inline size_t Reader_read(Reader *self, void *buf, size_t len);
/* MIDI events */
#define SYSEX_EVENT (0xF0)
void InitEvtQueue(EvtQueue *queue)
{
queue->events = NULL;
queue->maxsize = 0;
queue->size = 0;
queue->pos = 0;
}
void ResetEvtQueue(EvtQueue *queue)
{
ALsizei i;
for(i = 0;i < queue->size;i++)
{
if(queue->events[i].event == SYSEX_EVENT)
{
free(queue->events[i].param.sysex.data);
queue->events[i].param.sysex.data = NULL;
}
}
free(queue->events);
queue->events = NULL;
queue->maxsize = 0;
queue->size = 0;
queue->pos = 0;
}
ALenum InsertEvtQueue(EvtQueue *queue, const MidiEvent *evt)
{
ALsizei pos;
if(queue->maxsize == queue->size)
{
if(queue->pos > 0)
{
/* Queue has some stale entries, remove them to make space for more
* events. */
for(pos = 0;pos < queue->pos;pos++)
{
if(queue->events[pos].event == SYSEX_EVENT)
{
free(queue->events[pos].param.sysex.data);
queue->events[pos].param.sysex.data = NULL;
}
}
memmove(&queue->events[0], &queue->events[queue->pos],
(queue->size-queue->pos)*sizeof(queue->events[0]));
queue->size -= queue->pos;
queue->pos = 0;
}
else
{
/* Queue is full, double the allocated space. */
void *temp = NULL;
ALsizei newsize;
newsize = (queue->maxsize ? (queue->maxsize<<1) : 16);
if(newsize > queue->maxsize)
temp = realloc(queue->events, newsize * sizeof(queue->events[0]));
if(!temp)
return AL_OUT_OF_MEMORY;
queue->events = temp;
queue->maxsize = newsize;
}
}
pos = queue->pos;
if(queue->size > 0)
{
ALsizei high = queue->size - 1;
while(pos < high)
{
ALsizei mid = pos + (high-pos)/2;
if(queue->events[mid].time < evt->time)
pos = mid + 1;
else
high = mid;
}
while(pos < queue->size && queue->events[pos].time <= evt->time)
pos++;
if(pos < queue->size)
memmove(&queue->events[pos+1], &queue->events[pos],
(queue->size-pos)*sizeof(queue->events[0]));
}
queue->events[pos] = *evt;
queue->size++;
return AL_NO_ERROR;
}
void MidiSynth_Construct(MidiSynth *self, ALCdevice *device)
{
InitEvtQueue(&self->EventQueue);
RWLockInit(&self->Lock);
self->Soundfonts = NULL;
self->NumSoundfonts = 0;
self->Gain = 1.0f;
self->State = AL_INITIAL;
self->ClockBase = 0;
self->SamplesDone = 0;
self->SampleRate = device->Frequency;
}
void MidiSynth_Destruct(MidiSynth *self)
{
ALsizei i;
for(i = 0;i < self->NumSoundfonts;i++)
DecrementRef(&self->Soundfonts[i]->ref);
free(self->Soundfonts);
self->Soundfonts = NULL;
self->NumSoundfonts = 0;
ResetEvtQueue(&self->EventQueue);
}
ALenum MidiSynth_selectSoundfonts(MidiSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids)
{
ALCdevice *device = context->Device;
ALsoundfont **sfonts;
ALsizei i;
if(self->State != AL_INITIAL && self->State != AL_STOPPED)
return AL_INVALID_OPERATION;
sfonts = calloc(1, count * sizeof(sfonts[0]));
if(!sfonts) return AL_OUT_OF_MEMORY;
for(i = 0;i < count;i++)
{
if(ids[i] == 0)
sfonts[i] = ALsoundfont_getDefSoundfont(context);
else if(!(sfonts[i]=LookupSfont(device, ids[i])))
{
free(sfonts);
return AL_INVALID_VALUE;
}
}
for(i = 0;i < count;i++)
IncrementRef(&sfonts[i]->ref);
sfonts = ExchangePtr((XchgPtr*)&self->Soundfonts, sfonts);
count = ExchangeInt(&self->NumSoundfonts, count);
for(i = 0;i < count;i++)
DecrementRef(&sfonts[i]->ref);
free(sfonts);
return AL_NO_ERROR;
}
extern inline void MidiSynth_setGain(MidiSynth *self, ALfloat gain);
extern inline ALfloat MidiSynth_getGain(const MidiSynth *self);
extern inline void MidiSynth_setState(MidiSynth *self, ALenum state);
extern inline ALenum MidiSynth_getState(const MidiSynth *self);
void MidiSynth_stop(MidiSynth *self)
{
ResetEvtQueue(&self->EventQueue);
self->ClockBase = 0;
self->SamplesDone = 0;
}
extern inline void MidiSynth_reset(MidiSynth *self);
extern inline ALuint64 MidiSynth_getTime(const MidiSynth *self);
extern inline ALuint64 MidiSynth_getNextEvtTime(const MidiSynth *self);
void MidiSynth_setSampleRate(MidiSynth *self, ALuint srate)
{
if(self->SampleRate != srate)
{
self->ClockBase += self->SamplesDone * MIDI_CLOCK_RES / self->SampleRate;
self->SamplesDone = 0;
self->SampleRate = srate;
}
}
extern inline void MidiSynth_update(MidiSynth *self, ALCdevice *device);
ALenum MidiSynth_insertEvent(MidiSynth *self, ALuint64 time, ALuint event, ALsizei param1, ALsizei param2)
{
MidiEvent entry;
entry.time = time;
entry.event = event;
entry.param.val[0] = param1;
entry.param.val[1] = param2;
return InsertEvtQueue(&self->EventQueue, &entry);
}
ALenum MidiSynth_insertSysExEvent(MidiSynth *self, ALuint64 time, const ALbyte *data, ALsizei size)
{
MidiEvent entry;
ALenum err;
entry.time = time;
entry.event = SYSEX_EVENT;
entry.param.sysex.size = size;
entry.param.sysex.data = malloc(size);
if(!entry.param.sysex.data)
return AL_OUT_OF_MEMORY;
memcpy(entry.param.sysex.data, data, size);
err = InsertEvtQueue(&self->EventQueue, &entry);
if(err != AL_NO_ERROR)
free(entry.param.sysex.data);
return err;
}
+133
View File
@@ -0,0 +1,133 @@
#ifndef AL_MIDI_BASE_H
#define AL_MIDI_BASE_H
#include "alMain.h"
#include "atomic.h"
#include "evtqueue.h"
#ifdef __cplusplus
extern "C" {
#endif
struct ALsoundfont;
typedef size_t (*ReaderCb)(void *ptr, size_t size, void *stream);
typedef struct Reader {
ReaderCb cb;
void *ptr;
int error;
} Reader;
inline size_t Reader_read(Reader *self, void *buf, size_t len)
{
size_t got = (!self->error) ? self->cb(buf, len, self->ptr) : 0;
if(got < len) self->error = 1;
return got;
}
#define READERR(x_) ((x_)->error)
ALboolean loadSf2(Reader *stream, struct ALsoundfont *sfont, ALCcontext *context);
#define MIDI_CLOCK_RES U64(1000000000)
struct MidiSynthVtable;
typedef struct MidiSynth {
EvtQueue EventQueue;
ALuint64 ClockBase;
ALuint SamplesDone;
ALuint SampleRate;
/* NOTE: This rwlock is for the state and soundfont. The EventQueue and
* related must instead use the device lock as they're used in the mixer
* thread.
*/
RWLock Lock;
struct ALsoundfont **Soundfonts;
ALsizei NumSoundfonts;
volatile ALfloat Gain;
volatile ALenum State;
const struct MidiSynthVtable *vtbl;
} MidiSynth;
void MidiSynth_Construct(MidiSynth *self, ALCdevice *device);
void MidiSynth_Destruct(MidiSynth *self);
ALenum MidiSynth_selectSoundfonts(MidiSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids);
inline void MidiSynth_setGain(MidiSynth *self, ALfloat gain) { self->Gain = gain; }
inline ALfloat MidiSynth_getGain(const MidiSynth *self) { return self->Gain; }
inline void MidiSynth_setState(MidiSynth *self, ALenum state) { ExchangeInt(&self->State, state); }
inline ALenum MidiSynth_getState(const MidiSynth *self) { return self->State; }
void MidiSynth_stop(MidiSynth *self);
inline void MidiSynth_reset(MidiSynth *self) { MidiSynth_stop(self); }
inline ALuint64 MidiSynth_getTime(const MidiSynth *self)
{ return self->ClockBase + (self->SamplesDone*MIDI_CLOCK_RES/self->SampleRate); }
inline ALuint64 MidiSynth_getNextEvtTime(const MidiSynth *self)
{
if(self->EventQueue.pos == self->EventQueue.size)
return UINT64_MAX;
return self->EventQueue.events[self->EventQueue.pos].time;
}
void MidiSynth_setSampleRate(MidiSynth *self, ALuint srate);
inline void MidiSynth_update(MidiSynth *self, ALCdevice *device)
{ MidiSynth_setSampleRate(self, device->Frequency); }
ALenum MidiSynth_insertEvent(MidiSynth *self, ALuint64 time, ALuint event, ALsizei param1, ALsizei param2);
ALenum MidiSynth_insertSysExEvent(MidiSynth *self, ALuint64 time, const ALbyte *data, ALsizei size);
struct MidiSynthVtable {
void (*const Destruct)(MidiSynth *self);
ALenum (*const selectSoundfonts)(MidiSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids);
void (*const setGain)(MidiSynth *self, ALfloat gain);
void (*const stop)(MidiSynth *self);
void (*const reset)(MidiSynth *self);
void (*const update)(MidiSynth *self, ALCdevice *device);
void (*const process)(MidiSynth *self, ALuint samples, ALfloat (*restrict DryBuffer)[BUFFERSIZE]);
void (*const Delete)(void *ptr);
};
#define DEFINE_MIDISYNTH_VTABLE(T) \
DECLARE_THUNK(T, MidiSynth, void, Destruct) \
DECLARE_THUNK3(T, MidiSynth, ALenum, selectSoundfonts, ALCcontext*, ALsizei, const ALuint*) \
DECLARE_THUNK1(T, MidiSynth, void, setGain, ALfloat) \
DECLARE_THUNK(T, MidiSynth, void, stop) \
DECLARE_THUNK(T, MidiSynth, void, reset) \
DECLARE_THUNK1(T, MidiSynth, void, update, ALCdevice*) \
DECLARE_THUNK2(T, MidiSynth, void, process, ALuint, ALfloatBUFFERSIZE*restrict) \
static void T##_MidiSynth_Delete(void *ptr) \
{ T##_Delete(STATIC_UPCAST(T, MidiSynth, (MidiSynth*)ptr)); } \
\
static const struct MidiSynthVtable T##_MidiSynth_vtable = { \
T##_MidiSynth_Destruct, \
\
T##_MidiSynth_selectSoundfonts, \
T##_MidiSynth_setGain, \
T##_MidiSynth_stop, \
T##_MidiSynth_reset, \
T##_MidiSynth_update, \
T##_MidiSynth_process, \
\
T##_MidiSynth_Delete, \
}
MidiSynth *SSynth_create(ALCdevice *device);
MidiSynth *FSynth_create(ALCdevice *device);
MidiSynth *DSynth_create(ALCdevice *device);
MidiSynth *SynthCreate(ALCdevice *device);
#ifdef __cplusplus
}
#endif
#endif /* AL_MIDI_BASE_H */
+76
View File
@@ -0,0 +1,76 @@
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "alMain.h"
#include "alError.h"
#include "evtqueue.h"
#include "rwlock.h"
#include "alu.h"
#include "midi/base.h"
typedef struct DSynth {
DERIVE_FROM_TYPE(MidiSynth);
} DSynth;
static void DSynth_Construct(DSynth *self, ALCdevice *device);
static DECLARE_FORWARD(DSynth, MidiSynth, void, Destruct)
static DECLARE_FORWARD3(DSynth, MidiSynth, ALenum, selectSoundfonts, ALCcontext*, ALsizei, const ALuint*)
static DECLARE_FORWARD1(DSynth, MidiSynth, void, setGain, ALfloat)
static DECLARE_FORWARD(DSynth, MidiSynth, void, stop)
static DECLARE_FORWARD(DSynth, MidiSynth, void, reset)
static DECLARE_FORWARD1(DSynth, MidiSynth, void, update, ALCdevice*)
static void DSynth_process(DSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]);
DECLARE_DEFAULT_ALLOCATORS(DSynth)
DEFINE_MIDISYNTH_VTABLE(DSynth);
static void DSynth_Construct(DSynth *self, ALCdevice *device)
{
MidiSynth_Construct(STATIC_CAST(MidiSynth, self), device);
SET_VTABLE2(DSynth, MidiSynth, self);
}
static void DSynth_processQueue(DSynth *self, ALuint64 time)
{
EvtQueue *queue = &STATIC_CAST(MidiSynth, self)->EventQueue;
while(queue->pos < queue->size && queue->events[queue->pos].time <= time)
queue->pos++;
}
static void DSynth_process(DSynth *self, ALuint SamplesToDo, ALfloatBUFFERSIZE*restrict UNUSED(DryBuffer))
{
MidiSynth *synth = STATIC_CAST(MidiSynth, self);
ALuint64 curtime;
if(synth->State != AL_PLAYING)
return;
synth->SamplesDone += SamplesToDo;
synth->ClockBase += (synth->SamplesDone/synth->SampleRate) * MIDI_CLOCK_RES;
synth->SamplesDone %= synth->SampleRate;
curtime = MidiSynth_getTime(synth);
DSynth_processQueue(self, maxi64(curtime-1, 0));
}
MidiSynth *DSynth_create(ALCdevice *device)
{
DSynth *synth = DSynth_New(sizeof(*synth));
if(!synth)
{
ERR("Failed to allocate DSynth\n");
return NULL;
}
memset(synth, 0, sizeof(*synth));
DSynth_Construct(synth, device);
return STATIC_CAST(MidiSynth, synth);
}
+930
View File
@@ -0,0 +1,930 @@
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "midi/base.h"
#include "alMain.h"
#include "alError.h"
#include "alMidi.h"
#include "alu.h"
#include "compat.h"
#include "evtqueue.h"
#include "rwlock.h"
#ifdef HAVE_FLUIDSYNTH
#include <fluidsynth.h>
#ifdef HAVE_DYNLOAD
#define FLUID_FUNCS(MAGIC) \
MAGIC(new_fluid_synth); \
MAGIC(delete_fluid_synth); \
MAGIC(new_fluid_settings); \
MAGIC(delete_fluid_settings); \
MAGIC(fluid_settings_setint); \
MAGIC(fluid_settings_setnum); \
MAGIC(fluid_synth_noteon); \
MAGIC(fluid_synth_noteoff); \
MAGIC(fluid_synth_program_change); \
MAGIC(fluid_synth_pitch_bend); \
MAGIC(fluid_synth_channel_pressure); \
MAGIC(fluid_synth_cc); \
MAGIC(fluid_synth_sysex); \
MAGIC(fluid_synth_bank_select); \
MAGIC(fluid_synth_set_channel_type); \
MAGIC(fluid_synth_all_sounds_off); \
MAGIC(fluid_synth_system_reset); \
MAGIC(fluid_synth_set_gain); \
MAGIC(fluid_synth_set_sample_rate); \
MAGIC(fluid_synth_write_float); \
MAGIC(fluid_synth_add_sfloader); \
MAGIC(fluid_synth_sfload); \
MAGIC(fluid_synth_sfunload); \
MAGIC(fluid_synth_alloc_voice); \
MAGIC(fluid_synth_start_voice); \
MAGIC(fluid_voice_gen_set); \
MAGIC(fluid_voice_add_mod); \
MAGIC(fluid_mod_set_source1); \
MAGIC(fluid_mod_set_source2); \
MAGIC(fluid_mod_set_amount); \
MAGIC(fluid_mod_set_dest);
void *fsynth_handle = NULL;
#define DECL_FUNC(x) __typeof(x) *p##x
FLUID_FUNCS(DECL_FUNC)
#undef DECL_FUNC
#define new_fluid_synth pnew_fluid_synth
#define delete_fluid_synth pdelete_fluid_synth
#define new_fluid_settings pnew_fluid_settings
#define delete_fluid_settings pdelete_fluid_settings
#define fluid_settings_setint pfluid_settings_setint
#define fluid_settings_setnum pfluid_settings_setnum
#define fluid_synth_noteon pfluid_synth_noteon
#define fluid_synth_noteoff pfluid_synth_noteoff
#define fluid_synth_program_change pfluid_synth_program_change
#define fluid_synth_pitch_bend pfluid_synth_pitch_bend
#define fluid_synth_channel_pressure pfluid_synth_channel_pressure
#define fluid_synth_cc pfluid_synth_cc
#define fluid_synth_sysex pfluid_synth_sysex
#define fluid_synth_bank_select pfluid_synth_bank_select
#define fluid_synth_set_channel_type pfluid_synth_set_channel_type
#define fluid_synth_all_sounds_off pfluid_synth_all_sounds_off
#define fluid_synth_system_reset pfluid_synth_system_reset
#define fluid_synth_set_gain pfluid_synth_set_gain
#define fluid_synth_set_sample_rate pfluid_synth_set_sample_rate
#define fluid_synth_write_float pfluid_synth_write_float
#define fluid_synth_add_sfloader pfluid_synth_add_sfloader
#define fluid_synth_sfload pfluid_synth_sfload
#define fluid_synth_sfunload pfluid_synth_sfunload
#define fluid_synth_alloc_voice pfluid_synth_alloc_voice
#define fluid_synth_start_voice pfluid_synth_start_voice
#define fluid_voice_gen_set pfluid_voice_gen_set
#define fluid_voice_add_mod pfluid_voice_add_mod
#define fluid_mod_set_source1 pfluid_mod_set_source1
#define fluid_mod_set_source2 pfluid_mod_set_source2
#define fluid_mod_set_amount pfluid_mod_set_amount
#define fluid_mod_set_dest pfluid_mod_set_dest
static ALboolean LoadFSynth(void)
{
ALboolean ret = AL_TRUE;
if(!fsynth_handle)
{
fsynth_handle = LoadLib("libfluidsynth.so.1");
if(!fsynth_handle) return AL_FALSE;
#define LOAD_FUNC(x) do { \
p##x = GetSymbol(fsynth_handle, #x); \
if(!p##x) ret = AL_FALSE; \
} while(0)
FLUID_FUNCS(LOAD_FUNC)
#undef LOAD_FUNC
if(ret == AL_FALSE)
{
CloseLib(fsynth_handle);
fsynth_handle = NULL;
}
}
return ret;
}
#else
static inline ALboolean LoadFSynth(void) { return AL_TRUE; }
#endif
/* MIDI events */
#define SYSEX_EVENT (0xF0)
/* MIDI controllers */
#define CTRL_BANKSELECT_MSB (0)
#define CTRL_BANKSELECT_LSB (32)
#define CTRL_ALLNOTESOFF (123)
static int getModInput(ALenum input)
{
switch(input)
{
case AL_ONE_SOFT: return FLUID_MOD_NONE;
case AL_NOTEON_VELOCITY_SOFT: return FLUID_MOD_VELOCITY;
case AL_NOTEON_KEY_SOFT: return FLUID_MOD_KEY;
case AL_KEYPRESSURE_SOFT: return FLUID_MOD_KEYPRESSURE;
case AL_CHANNELPRESSURE_SOFT: return FLUID_MOD_CHANNELPRESSURE;
case AL_PITCHBEND_SOFT: return FLUID_MOD_PITCHWHEEL;
case AL_PITCHBEND_SENSITIVITY_SOFT: return FLUID_MOD_PITCHWHEELSENS;
}
return input&0x7F;
}
static int getModFlags(ALenum input, ALenum type, ALenum form)
{
int ret = 0;
switch(type)
{
case AL_UNORM_SOFT: ret |= FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE; break;
case AL_UNORM_REV_SOFT: ret |= FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE; break;
case AL_SNORM_SOFT: ret |= FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE; break;
case AL_SNORM_REV_SOFT: ret |= FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE; break;
}
switch(form)
{
case AL_LINEAR_SOFT: ret |= FLUID_MOD_LINEAR; break;
case AL_CONCAVE_SOFT: ret |= FLUID_MOD_CONCAVE; break;
case AL_CONVEX_SOFT: ret |= FLUID_MOD_CONVEX; break;
case AL_SWITCH_SOFT: ret |= FLUID_MOD_SWITCH; break;
}
/* Source input values less than 128 correspond to a MIDI continuous
* controller. Otherwise, it's a general controller. */
if(input < 128) ret |= FLUID_MOD_CC;
else ret |= FLUID_MOD_GC;
return ret;
}
static enum fluid_gen_type getModDest(ALenum gen)
{
switch(gen)
{
case AL_MOD_LFO_TO_PITCH_SOFT: return GEN_MODLFOTOPITCH;
case AL_VIBRATO_LFO_TO_PITCH_SOFT: return GEN_VIBLFOTOPITCH;
case AL_MOD_ENV_TO_PITCH_SOFT: return GEN_MODENVTOPITCH;
case AL_FILTER_CUTOFF_SOFT: return GEN_FILTERFC;
case AL_FILTER_RESONANCE_SOFT: return GEN_FILTERQ;
case AL_MOD_LFO_TO_FILTER_CUTOFF_SOFT: return GEN_MODLFOTOFILTERFC;
case AL_MOD_ENV_TO_FILTER_CUTOFF_SOFT: return GEN_MODENVTOFILTERFC;
case AL_MOD_LFO_TO_VOLUME_SOFT: return GEN_MODLFOTOVOL;
case AL_CHORUS_SEND_SOFT: return GEN_CHORUSSEND;
case AL_REVERB_SEND_SOFT: return GEN_REVERBSEND;
case AL_PAN_SOFT: return GEN_PAN;
case AL_MOD_LFO_DELAY_SOFT: return GEN_MODLFODELAY;
case AL_MOD_LFO_FREQUENCY_SOFT: return GEN_MODLFOFREQ;
case AL_VIBRATO_LFO_DELAY_SOFT: return GEN_VIBLFODELAY;
case AL_VIBRATO_LFO_FREQUENCY_SOFT: return GEN_VIBLFOFREQ;
case AL_MOD_ENV_DELAYTIME_SOFT: return GEN_MODENVDELAY;
case AL_MOD_ENV_ATTACKTIME_SOFT: return GEN_MODENVATTACK;
case AL_MOD_ENV_HOLDTIME_SOFT: return GEN_MODENVHOLD;
case AL_MOD_ENV_DECAYTIME_SOFT: return GEN_MODENVDECAY;
case AL_MOD_ENV_SUSTAINVOLUME_SOFT: return GEN_MODENVSUSTAIN;
case AL_MOD_ENV_RELEASETIME_SOFT: return GEN_MODENVRELEASE;
case AL_MOD_ENV_KEY_TO_HOLDTIME_SOFT: return GEN_KEYTOMODENVHOLD;
case AL_MOD_ENV_KEY_TO_DECAYTIME_SOFT: return GEN_KEYTOMODENVDECAY;
case AL_VOLUME_ENV_DELAYTIME_SOFT: return GEN_VOLENVDELAY;
case AL_VOLUME_ENV_ATTACKTIME_SOFT: return GEN_VOLENVATTACK;
case AL_VOLUME_ENV_HOLDTIME_SOFT: return GEN_VOLENVHOLD;
case AL_VOLUME_ENV_DECAYTIME_SOFT: return GEN_VOLENVDECAY;
case AL_VOLUME_ENV_SUSTAINVOLUME_SOFT: return GEN_VOLENVSUSTAIN;
case AL_VOLUME_ENV_RELEASETIME_SOFT: return GEN_VOLENVRELEASE;
case AL_VOLUME_ENV_KEY_TO_HOLDTIME_SOFT: return GEN_KEYTOVOLENVHOLD;
case AL_VOLUME_ENV_KEY_TO_DECAYTIME_SOFT: return GEN_KEYTOVOLENVDECAY;
case AL_ATTENUATION_SOFT: return GEN_ATTENUATION;
case AL_TUNING_COARSE_SOFT: return GEN_COARSETUNE;
case AL_TUNING_FINE_SOFT: return GEN_FINETUNE;
case AL_TUNING_SCALE_SOFT: return GEN_SCALETUNE;
}
ERR("Unhandled generator: 0x%04x\n", gen);
return 0;
}
static int getSf2LoopMode(ALenum mode)
{
switch(mode)
{
case AL_NONE: return 0;
case AL_LOOP_CONTINUOUS_SOFT: return 1;
case AL_LOOP_UNTIL_RELEASE_SOFT: return 3;
}
return 0;
}
static int getSampleType(ALenum type)
{
switch(type)
{
case AL_MONO_SOFT: return FLUID_SAMPLETYPE_MONO;
case AL_RIGHT_SOFT: return FLUID_SAMPLETYPE_RIGHT;
case AL_LEFT_SOFT: return FLUID_SAMPLETYPE_LEFT;
}
return FLUID_SAMPLETYPE_MONO;
}
typedef struct FSample {
DERIVE_FROM_TYPE(fluid_sample_t);
ALfontsound *Sound;
fluid_mod_t *Mods;
ALsizei NumMods;
} FSample;
static void FSample_Construct(FSample *self, ALfontsound *sound)
{
fluid_sample_t *sample = STATIC_CAST(fluid_sample_t, self);
memset(sample->name, 0, sizeof(sample->name));
sample->start = sound->Start;
sample->end = sound->End;
sample->loopstart = sound->LoopStart;
sample->loopend = sound->LoopEnd;
sample->samplerate = sound->SampleRate;
sample->origpitch = sound->PitchKey;
sample->pitchadj = sound->PitchCorrection;
sample->sampletype = getSampleType(sound->SampleType);
sample->valid = !!sound->Buffer;
sample->data = sound->Buffer ? sound->Buffer->data : NULL;
sample->amplitude_that_reaches_noise_floor_is_valid = 0;
sample->amplitude_that_reaches_noise_floor = 0.0;
sample->refcount = 0;
sample->notify = NULL;
sample->userdata = self;
self->Sound = sound;
self->NumMods = 0;
self->Mods = calloc(sound->ModulatorMap.size*4, sizeof(fluid_mod_t[4]));
if(self->Mods)
{
ALsizei i, j, k;
for(i = j = 0;i < sound->ModulatorMap.size;i++)
{
ALsfmodulator *mod = sound->ModulatorMap.array[i].value;
for(k = 0;k < 4;k++,mod++)
{
if(mod->Dest == AL_NONE)
continue;
fluid_mod_set_source1(&self->Mods[j], getModInput(mod->Source[0].Input),
getModFlags(mod->Source[0].Input, mod->Source[0].Type,
mod->Source[0].Form));
fluid_mod_set_source2(&self->Mods[j], getModInput(mod->Source[1].Input),
getModFlags(mod->Source[1].Input, mod->Source[1].Type,
mod->Source[1].Form));
fluid_mod_set_amount(&self->Mods[j], mod->Amount);
fluid_mod_set_dest(&self->Mods[j], getModDest(mod->Dest));
self->Mods[j++].next = NULL;
}
}
self->NumMods = j;
}
}
static void FSample_Destruct(FSample *self)
{
free(self->Mods);
self->Mods = NULL;
self->NumMods = 0;
}
typedef struct FPreset {
DERIVE_FROM_TYPE(fluid_preset_t);
char Name[16];
int Preset;
int Bank;
FSample *Samples;
ALsizei NumSamples;
} FPreset;
static char* FPreset_getName(fluid_preset_t *preset);
static int FPreset_getPreset(fluid_preset_t *preset);
static int FPreset_getBank(fluid_preset_t *preset);
static int FPreset_noteOn(fluid_preset_t *preset, fluid_synth_t *synth, int channel, int key, int velocity);
static void FPreset_Construct(FPreset *self, ALsfpreset *preset, fluid_sfont_t *parent)
{
STATIC_CAST(fluid_preset_t, self)->data = self;
STATIC_CAST(fluid_preset_t, self)->sfont = parent;
STATIC_CAST(fluid_preset_t, self)->free = NULL;
STATIC_CAST(fluid_preset_t, self)->get_name = FPreset_getName;
STATIC_CAST(fluid_preset_t, self)->get_banknum = FPreset_getBank;
STATIC_CAST(fluid_preset_t, self)->get_num = FPreset_getPreset;
STATIC_CAST(fluid_preset_t, self)->noteon = FPreset_noteOn;
STATIC_CAST(fluid_preset_t, self)->notify = NULL;
memset(self->Name, 0, sizeof(self->Name));
self->Preset = preset->Preset;
self->Bank = preset->Bank;
self->NumSamples = 0;
self->Samples = calloc(1, preset->NumSounds * sizeof(self->Samples[0]));
if(self->Samples)
{
ALsizei i;
self->NumSamples = preset->NumSounds;
for(i = 0;i < self->NumSamples;i++)
FSample_Construct(&self->Samples[i], preset->Sounds[i]);
}
}
static void FPreset_Destruct(FPreset *self)
{
ALsizei i;
for(i = 0;i < self->NumSamples;i++)
FSample_Destruct(&self->Samples[i]);
free(self->Samples);
self->Samples = NULL;
self->NumSamples = 0;
}
static ALboolean FPreset_canDelete(FPreset *self)
{
ALsizei i;
for(i = 0;i < self->NumSamples;i++)
{
if(fluid_sample_refcount(STATIC_CAST(fluid_sample_t, &self->Samples[i])) != 0)
return AL_FALSE;
}
return AL_TRUE;
}
static char* FPreset_getName(fluid_preset_t *preset)
{
return ((FPreset*)preset->data)->Name;
}
static int FPreset_getPreset(fluid_preset_t *preset)
{
return ((FPreset*)preset->data)->Preset;
}
static int FPreset_getBank(fluid_preset_t *preset)
{
return ((FPreset*)preset->data)->Bank;
}
static int FPreset_noteOn(fluid_preset_t *preset, fluid_synth_t *synth, int channel, int key, int vel)
{
FPreset *self = ((FPreset*)preset->data);
ALsizei i;
for(i = 0;i < self->NumSamples;i++)
{
FSample *sample = &self->Samples[i];
ALfontsound *sound = sample->Sound;
fluid_voice_t *voice;
ALsizei m;
if(!(key >= sound->MinKey && key <= sound->MaxKey && vel >= sound->MinVelocity && vel <= sound->MaxVelocity))
continue;
voice = fluid_synth_alloc_voice(synth, STATIC_CAST(fluid_sample_t, sample), channel, key, vel);
if(voice == NULL) return FLUID_FAILED;
fluid_voice_gen_set(voice, GEN_MODLFOTOPITCH, sound->ModLfoToPitch);
fluid_voice_gen_set(voice, GEN_VIBLFOTOPITCH, sound->VibratoLfoToPitch);
fluid_voice_gen_set(voice, GEN_MODENVTOPITCH, sound->ModEnvToPitch);
fluid_voice_gen_set(voice, GEN_FILTERFC, sound->FilterCutoff);
fluid_voice_gen_set(voice, GEN_FILTERQ, sound->FilterQ);
fluid_voice_gen_set(voice, GEN_MODLFOTOFILTERFC, sound->ModLfoToFilterCutoff);
fluid_voice_gen_set(voice, GEN_MODENVTOFILTERFC, sound->ModEnvToFilterCutoff);
fluid_voice_gen_set(voice, GEN_MODLFOTOVOL, sound->ModLfoToVolume);
fluid_voice_gen_set(voice, GEN_CHORUSSEND, sound->ChorusSend);
fluid_voice_gen_set(voice, GEN_REVERBSEND, sound->ReverbSend);
fluid_voice_gen_set(voice, GEN_PAN, sound->Pan);
fluid_voice_gen_set(voice, GEN_MODLFODELAY, sound->ModLfo.Delay);
fluid_voice_gen_set(voice, GEN_MODLFOFREQ, sound->ModLfo.Frequency);
fluid_voice_gen_set(voice, GEN_VIBLFODELAY, sound->VibratoLfo.Delay);
fluid_voice_gen_set(voice, GEN_VIBLFOFREQ, sound->VibratoLfo.Frequency);
fluid_voice_gen_set(voice, GEN_MODENVDELAY, sound->ModEnv.DelayTime);
fluid_voice_gen_set(voice, GEN_MODENVATTACK, sound->ModEnv.AttackTime);
fluid_voice_gen_set(voice, GEN_MODENVHOLD, sound->ModEnv.HoldTime);
fluid_voice_gen_set(voice, GEN_MODENVDECAY, sound->ModEnv.DecayTime);
fluid_voice_gen_set(voice, GEN_MODENVSUSTAIN, sound->ModEnv.SustainAttn);
fluid_voice_gen_set(voice, GEN_MODENVRELEASE, sound->ModEnv.ReleaseTime);
fluid_voice_gen_set(voice, GEN_KEYTOMODENVHOLD, sound->ModEnv.KeyToHoldTime);
fluid_voice_gen_set(voice, GEN_KEYTOMODENVDECAY, sound->ModEnv.KeyToDecayTime);
fluid_voice_gen_set(voice, GEN_VOLENVDELAY, sound->VolEnv.DelayTime);
fluid_voice_gen_set(voice, GEN_VOLENVATTACK, sound->VolEnv.AttackTime);
fluid_voice_gen_set(voice, GEN_VOLENVHOLD, sound->VolEnv.HoldTime);
fluid_voice_gen_set(voice, GEN_VOLENVDECAY, sound->VolEnv.DecayTime);
fluid_voice_gen_set(voice, GEN_VOLENVSUSTAIN, sound->VolEnv.SustainAttn);
fluid_voice_gen_set(voice, GEN_VOLENVRELEASE, sound->VolEnv.ReleaseTime);
fluid_voice_gen_set(voice, GEN_KEYTOVOLENVHOLD, sound->VolEnv.KeyToHoldTime);
fluid_voice_gen_set(voice, GEN_KEYTOVOLENVDECAY, sound->VolEnv.KeyToDecayTime);
fluid_voice_gen_set(voice, GEN_ATTENUATION, sound->Attenuation);
fluid_voice_gen_set(voice, GEN_COARSETUNE, sound->CoarseTuning);
fluid_voice_gen_set(voice, GEN_FINETUNE, sound->FineTuning);
fluid_voice_gen_set(voice, GEN_SAMPLEMODE, getSf2LoopMode(sound->LoopMode));
fluid_voice_gen_set(voice, GEN_SCALETUNE, sound->TuningScale);
fluid_voice_gen_set(voice, GEN_EXCLUSIVECLASS, sound->ExclusiveClass);
for(m = 0;m < sample->NumMods;m++)
fluid_voice_add_mod(voice, &sample->Mods[m], FLUID_VOICE_OVERWRITE);
fluid_synth_start_voice(synth, voice);
}
return FLUID_OK;
}
typedef struct FSfont {
DERIVE_FROM_TYPE(fluid_sfont_t);
char Name[16];
FPreset *Presets;
ALsizei NumPresets;
ALsizei CurrentPos;
} FSfont;
static int FSfont_free(fluid_sfont_t *sfont);
static char* FSfont_getName(fluid_sfont_t *sfont);
static fluid_preset_t* FSfont_getPreset(fluid_sfont_t *sfont, unsigned int bank, unsigned int prenum);
static void FSfont_iterStart(fluid_sfont_t *sfont);
static int FSfont_iterNext(fluid_sfont_t *sfont, fluid_preset_t *preset);
static void FSfont_Construct(FSfont *self, ALsoundfont *sfont)
{
STATIC_CAST(fluid_sfont_t, self)->data = self;
STATIC_CAST(fluid_sfont_t, self)->id = FLUID_FAILED;
STATIC_CAST(fluid_sfont_t, self)->free = FSfont_free;
STATIC_CAST(fluid_sfont_t, self)->get_name = FSfont_getName;
STATIC_CAST(fluid_sfont_t, self)->get_preset = FSfont_getPreset;
STATIC_CAST(fluid_sfont_t, self)->iteration_start = FSfont_iterStart;
STATIC_CAST(fluid_sfont_t, self)->iteration_next = FSfont_iterNext;
memset(self->Name, 0, sizeof(self->Name));
self->CurrentPos = 0;
self->NumPresets = 0;
self->Presets = calloc(1, sfont->NumPresets * sizeof(self->Presets[0]));
if(self->Presets)
{
ALsizei i;
self->NumPresets = sfont->NumPresets;
for(i = 0;i < self->NumPresets;i++)
FPreset_Construct(&self->Presets[i], sfont->Presets[i], STATIC_CAST(fluid_sfont_t, self));
}
}
static void FSfont_Destruct(FSfont *self)
{
ALsizei i;
for(i = 0;i < self->NumPresets;i++)
FPreset_Destruct(&self->Presets[i]);
free(self->Presets);
self->Presets = NULL;
self->NumPresets = 0;
self->CurrentPos = 0;
}
static int FSfont_free(fluid_sfont_t *sfont)
{
FSfont *self = STATIC_UPCAST(FSfont, fluid_sfont_t, sfont);
ALsizei i;
for(i = 0;i < self->NumPresets;i++)
{
if(!FPreset_canDelete(&self->Presets[i]))
return 1;
}
FSfont_Destruct(self);
free(self);
return 0;
}
static char* FSfont_getName(fluid_sfont_t *sfont)
{
return STATIC_UPCAST(FSfont, fluid_sfont_t, sfont)->Name;
}
static fluid_preset_t *FSfont_getPreset(fluid_sfont_t *sfont, unsigned int bank, unsigned int prenum)
{
FSfont *self = STATIC_UPCAST(FSfont, fluid_sfont_t, sfont);
ALsizei i;
for(i = 0;i < self->NumPresets;i++)
{
FPreset *preset = &self->Presets[i];
if(preset->Bank == (int)bank && preset->Preset == (int)prenum)
return STATIC_CAST(fluid_preset_t, preset);
}
return NULL;
}
static void FSfont_iterStart(fluid_sfont_t *sfont)
{
STATIC_UPCAST(FSfont, fluid_sfont_t, sfont)->CurrentPos = 0;
}
static int FSfont_iterNext(fluid_sfont_t *sfont, fluid_preset_t *preset)
{
FSfont *self = STATIC_UPCAST(FSfont, fluid_sfont_t, sfont);
if(self->CurrentPos >= self->NumPresets)
return 0;
*preset = *STATIC_CAST(fluid_preset_t, &self->Presets[self->CurrentPos++]);
preset->free = NULL;
return 1;
}
typedef struct FSynth {
DERIVE_FROM_TYPE(MidiSynth);
DERIVE_FROM_TYPE(fluid_sfloader_t);
fluid_settings_t *Settings;
fluid_synth_t *Synth;
int *FontIDs;
ALsizei NumFontIDs;
ALboolean ForceGM2BankSelect;
ALfloat GainScale;
} FSynth;
static void FSynth_Construct(FSynth *self, ALCdevice *device);
static void FSynth_Destruct(FSynth *self);
static ALboolean FSynth_init(FSynth *self, ALCdevice *device);
static ALenum FSynth_selectSoundfonts(FSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids);
static void FSynth_setGain(FSynth *self, ALfloat gain);
static void FSynth_stop(FSynth *self);
static void FSynth_reset(FSynth *self);
static void FSynth_update(FSynth *self, ALCdevice *device);
static void FSynth_processQueue(FSynth *self, ALuint64 time);
static void FSynth_process(FSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]);
DECLARE_DEFAULT_ALLOCATORS(FSynth)
DEFINE_MIDISYNTH_VTABLE(FSynth);
static fluid_sfont_t *FSynth_loadSfont(fluid_sfloader_t *loader, const char *filename);
static void FSynth_Construct(FSynth *self, ALCdevice *device)
{
MidiSynth_Construct(STATIC_CAST(MidiSynth, self), device);
SET_VTABLE2(FSynth, MidiSynth, self);
STATIC_CAST(fluid_sfloader_t, self)->data = self;
STATIC_CAST(fluid_sfloader_t, self)->free = NULL;
STATIC_CAST(fluid_sfloader_t, self)->load = FSynth_loadSfont;
self->Settings = NULL;
self->Synth = NULL;
self->FontIDs = NULL;
self->NumFontIDs = 0;
self->ForceGM2BankSelect = AL_FALSE;
self->GainScale = 0.2f;
}
static void FSynth_Destruct(FSynth *self)
{
ALsizei i;
for(i = 0;i < self->NumFontIDs;i++)
fluid_synth_sfunload(self->Synth, self->FontIDs[i], 0);
free(self->FontIDs);
self->FontIDs = NULL;
self->NumFontIDs = 0;
if(self->Synth != NULL)
delete_fluid_synth(self->Synth);
self->Synth = NULL;
if(self->Settings != NULL)
delete_fluid_settings(self->Settings);
self->Settings = NULL;
MidiSynth_Destruct(STATIC_CAST(MidiSynth, self));
}
static ALboolean FSynth_init(FSynth *self, ALCdevice *device)
{
ALfloat vol;
if(ConfigValueFloat("midi", "volume", &vol))
{
if(!(vol <= 0.0f))
{
ERR("MIDI volume %f clamped to 0\n", vol);
vol = 0.0f;
}
self->GainScale = powf(10.0f, vol / 20.0f);
}
self->Settings = new_fluid_settings();
if(!self->Settings)
{
ERR("Failed to create FluidSettings\n");
return AL_FALSE;
}
fluid_settings_setint(self->Settings, "synth.polyphony", 256);
fluid_settings_setnum(self->Settings, "synth.gain", self->GainScale);
fluid_settings_setnum(self->Settings, "synth.sample-rate", device->Frequency);
self->Synth = new_fluid_synth(self->Settings);
if(!self->Synth)
{
ERR("Failed to create FluidSynth\n");
return AL_FALSE;
}
fluid_synth_add_sfloader(self->Synth, STATIC_CAST(fluid_sfloader_t, self));
return AL_TRUE;
}
static fluid_sfont_t *FSynth_loadSfont(fluid_sfloader_t *loader, const char *filename)
{
FSynth *self = STATIC_UPCAST(FSynth, fluid_sfloader_t, loader);
FSfont *sfont;
int idx;
if(!filename || sscanf(filename, "_al_internal %d", &idx) != 1)
return NULL;
if(idx < 0 || idx >= STATIC_CAST(MidiSynth, self)->NumSoundfonts)
{
ERR("Received invalid soundfont index %d (max: %d)\n", idx, STATIC_CAST(MidiSynth, self)->NumSoundfonts);
return NULL;
}
sfont = calloc(1, sizeof(sfont[0]));
if(!sfont) return NULL;
FSfont_Construct(sfont, STATIC_CAST(MidiSynth, self)->Soundfonts[idx]);
return STATIC_CAST(fluid_sfont_t, sfont);
}
static ALenum FSynth_selectSoundfonts(FSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids)
{
int *fontid;
ALenum ret;
ALsizei i;
ret = MidiSynth_selectSoundfonts(STATIC_CAST(MidiSynth, self), context, count, ids);
if(ret != AL_NO_ERROR) return ret;
ALCdevice_Lock(context->Device);
for(i = 0;i < 16;i++)
fluid_synth_all_sounds_off(self->Synth, i);
ALCdevice_Unlock(context->Device);
fontid = malloc(count * sizeof(fontid[0]));
if(fontid)
{
for(i = 0;i < STATIC_CAST(MidiSynth, self)->NumSoundfonts;i++)
{
char name[16];
snprintf(name, sizeof(name), "_al_internal %d", i);
fontid[i] = fluid_synth_sfload(self->Synth, name, 0);
if(fontid[i] == FLUID_FAILED)
ERR("Failed to load selected soundfont %d\n", i);
}
fontid = ExchangePtr((XchgPtr*)&self->FontIDs, fontid);
count = ExchangeInt(&self->NumFontIDs, count);
}
else
{
ERR("Failed to allocate space for %d font IDs!\n", count);
fontid = ExchangePtr((XchgPtr*)&self->FontIDs, NULL);
count = ExchangeInt(&self->NumFontIDs, 0);
}
for(i = 0;i < count;i++)
fluid_synth_sfunload(self->Synth, fontid[i], 0);
free(fontid);
return ret;
}
static void FSynth_setGain(FSynth *self, ALfloat gain)
{
fluid_settings_setnum(self->Settings, "synth.gain", self->GainScale * gain);
fluid_synth_set_gain(self->Synth, self->GainScale * gain);
MidiSynth_setGain(STATIC_CAST(MidiSynth, self), gain);
}
static void FSynth_stop(FSynth *self)
{
MidiSynth *synth = STATIC_CAST(MidiSynth, self);
ALuint64 curtime;
ALsizei chan;
/* Make sure all pending events are processed. */
curtime = MidiSynth_getTime(synth);
FSynth_processQueue(self, curtime);
/* All notes off */
for(chan = 0;chan < 16;chan++)
fluid_synth_cc(self->Synth, chan, CTRL_ALLNOTESOFF, 0);
MidiSynth_stop(STATIC_CAST(MidiSynth, self));
}
static void FSynth_reset(FSynth *self)
{
/* Reset to power-up status. */
fluid_synth_system_reset(self->Synth);
MidiSynth_reset(STATIC_CAST(MidiSynth, self));
}
static void FSynth_update(FSynth *self, ALCdevice *device)
{
fluid_settings_setnum(self->Settings, "synth.sample-rate", device->Frequency);
fluid_synth_set_sample_rate(self->Synth, device->Frequency);
MidiSynth_update(STATIC_CAST(MidiSynth, self), device);
}
static void FSynth_processQueue(FSynth *self, ALuint64 time)
{
EvtQueue *queue = &STATIC_CAST(MidiSynth, self)->EventQueue;
while(queue->pos < queue->size && queue->events[queue->pos].time <= time)
{
const MidiEvent *evt = &queue->events[queue->pos];
if(evt->event == SYSEX_EVENT)
{
static const ALbyte gm2_on[] = { 0x7E, 0x7F, 0x09, 0x03 };
static const ALbyte gm2_off[] = { 0x7E, 0x7F, 0x09, 0x02 };
int handled = 0;
fluid_synth_sysex(self->Synth, evt->param.sysex.data, evt->param.sysex.size, NULL, NULL, &handled, 0);
if(!handled && evt->param.sysex.size >= (ALsizei)sizeof(gm2_on))
{
if(memcmp(evt->param.sysex.data, gm2_on, sizeof(gm2_on)) == 0)
self->ForceGM2BankSelect = AL_TRUE;
else if(memcmp(evt->param.sysex.data, gm2_off, sizeof(gm2_off)) == 0)
self->ForceGM2BankSelect = AL_FALSE;
}
}
else switch((evt->event&0xF0))
{
case AL_NOTEOFF_SOFT:
fluid_synth_noteoff(self->Synth, (evt->event&0x0F), evt->param.val[0]);
break;
case AL_NOTEON_SOFT:
fluid_synth_noteon(self->Synth, (evt->event&0x0F), evt->param.val[0], evt->param.val[1]);
break;
case AL_KEYPRESSURE_SOFT:
break;
case AL_CONTROLLERCHANGE_SOFT:
if(self->ForceGM2BankSelect)
{
int chan = (evt->event&0x0F);
if(evt->param.val[0] == CTRL_BANKSELECT_MSB)
{
if(evt->param.val[1] == 120 && (chan == 9 || chan == 10))
fluid_synth_set_channel_type(self->Synth, chan, CHANNEL_TYPE_DRUM);
else if(evt->param.val[1] == 121)
fluid_synth_set_channel_type(self->Synth, chan, CHANNEL_TYPE_MELODIC);
break;
}
if(evt->param.val[0] == CTRL_BANKSELECT_LSB)
{
fluid_synth_bank_select(self->Synth, chan, evt->param.val[1]);
break;
}
}
fluid_synth_cc(self->Synth, (evt->event&0x0F), evt->param.val[0], evt->param.val[1]);
break;
case AL_PROGRAMCHANGE_SOFT:
fluid_synth_program_change(self->Synth, (evt->event&0x0F), evt->param.val[0]);
break;
case AL_CHANNELPRESSURE_SOFT:
fluid_synth_channel_pressure(self->Synth, (evt->event&0x0F), evt->param.val[0]);
break;
case AL_PITCHBEND_SOFT:
fluid_synth_pitch_bend(self->Synth, (evt->event&0x0F), (evt->param.val[0]&0x7F) |
((evt->param.val[1]&0x7F)<<7));
break;
}
queue->pos++;
}
}
static void FSynth_process(FSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE])
{
MidiSynth *synth = STATIC_CAST(MidiSynth, self);
ALenum state = synth->State;
ALuint64 curtime;
ALuint total = 0;
if(state == AL_INITIAL)
return;
if(state != AL_PLAYING)
{
fluid_synth_write_float(self->Synth, SamplesToDo, DryBuffer[FrontLeft], 0, 1,
DryBuffer[FrontRight], 0, 1);
return;
}
curtime = MidiSynth_getTime(synth);
while(total < SamplesToDo)
{
ALuint64 time, diff;
ALint tonext;
time = MidiSynth_getNextEvtTime(synth);
diff = maxu64(time, curtime) - curtime;
if(diff >= MIDI_CLOCK_RES || time == UINT64_MAX)
{
/* If there's no pending event, or if it's more than 1 second
* away, do as many samples as we can. */
tonext = INT_MAX;
}
else
{
/* Figure out how many samples until the next event. */
tonext = (ALint)((diff*synth->SampleRate + (MIDI_CLOCK_RES-1)) / MIDI_CLOCK_RES);
tonext -= total;
}
if(tonext > 0)
{
ALuint todo = minu(tonext, SamplesToDo-total);
fluid_synth_write_float(self->Synth, todo, DryBuffer[FrontLeft], total, 1,
DryBuffer[FrontRight], total, 1);
total += todo;
tonext -= todo;
}
if(total < SamplesToDo && tonext <= 0)
FSynth_processQueue(self, time);
}
synth->SamplesDone += SamplesToDo;
synth->ClockBase += (synth->SamplesDone/synth->SampleRate) * MIDI_CLOCK_RES;
synth->SamplesDone %= synth->SampleRate;
}
MidiSynth *FSynth_create(ALCdevice *device)
{
FSynth *synth;
if(!LoadFSynth())
return NULL;
synth = FSynth_New(sizeof(*synth));
if(!synth)
{
ERR("Failed to allocate FSynth\n");
return NULL;
}
memset(synth, 0, sizeof(*synth));
FSynth_Construct(synth, device);
if(FSynth_init(synth, device) == AL_FALSE)
{
DELETE_OBJ(STATIC_CAST(MidiSynth, synth));
return NULL;
}
return STATIC_CAST(MidiSynth, synth);
}
#else
MidiSynth *FSynth_create(ALCdevice* UNUSED(device))
{
return NULL;
}
#endif
+1377
View File
File diff suppressed because it is too large Load Diff
+140
View File
@@ -0,0 +1,140 @@
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "alMain.h"
#include "alError.h"
#include "evtqueue.h"
#include "alu.h"
#include "midi/base.h"
typedef struct SSynth {
DERIVE_FROM_TYPE(MidiSynth);
} SSynth;
static void SSynth_mixSamples(SSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]);
static void SSynth_Construct(SSynth *self, ALCdevice *device);
static void SSynth_Destruct(SSynth *self);
static DECLARE_FORWARD3(SSynth, MidiSynth, ALenum, selectSoundfonts, ALCcontext*, ALsizei, const ALuint*)
static DECLARE_FORWARD1(SSynth, MidiSynth, void, setGain, ALfloat)
static DECLARE_FORWARD(SSynth, MidiSynth, void, stop)
static DECLARE_FORWARD(SSynth, MidiSynth, void, reset)
static void SSynth_update(SSynth *self, ALCdevice *device);
static void SSynth_process(SSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]);
DECLARE_DEFAULT_ALLOCATORS(SSynth)
DEFINE_MIDISYNTH_VTABLE(SSynth);
static void SSynth_Construct(SSynth *self, ALCdevice *device)
{
MidiSynth_Construct(STATIC_CAST(MidiSynth, self), device);
SET_VTABLE2(SSynth, MidiSynth, self);
}
static void SSynth_Destruct(SSynth* UNUSED(self))
{
}
static void SSynth_update(SSynth* UNUSED(self), ALCdevice* UNUSED(device))
{
}
static void SSynth_mixSamples(SSynth* UNUSED(self), ALuint UNUSED(SamplesToDo), ALfloatBUFFERSIZE *restrict UNUSED(DryBuffer))
{
}
static void SSynth_processQueue(SSynth *self, ALuint64 time)
{
EvtQueue *queue = &STATIC_CAST(MidiSynth, self)->EventQueue;
while(queue->pos < queue->size && queue->events[queue->pos].time <= time)
queue->pos++;
}
static void SSynth_process(SSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE])
{
MidiSynth *synth = STATIC_CAST(MidiSynth, self);
ALenum state = synth->State;
ALuint64 curtime;
ALuint total = 0;
if(state == AL_INITIAL)
return;
if(state != AL_PLAYING)
{
SSynth_mixSamples(self, SamplesToDo, DryBuffer);
return;
}
curtime = MidiSynth_getTime(synth);
while(total < SamplesToDo)
{
ALuint64 time, diff;
ALint tonext;
time = MidiSynth_getNextEvtTime(synth);
diff = maxu64(time, curtime) - curtime;
if(diff >= MIDI_CLOCK_RES || time == UINT64_MAX)
{
/* If there's no pending event, or if it's more than 1 second
* away, do as many samples as we can. */
tonext = INT_MAX;
}
else
{
/* Figure out how many samples until the next event. */
tonext = (ALint)((diff*synth->SampleRate + (MIDI_CLOCK_RES-1)) / MIDI_CLOCK_RES);
tonext -= total;
/* For efficiency reasons, try to mix a multiple of 64 samples
* (~1ms @ 44.1khz) before processing the next event. */
tonext = (tonext+63) & ~63;
}
if(tonext > 0)
{
ALuint todo = mini(tonext, SamplesToDo-total);
SSynth_mixSamples(self, todo, DryBuffer);
total += todo;
tonext -= todo;
}
if(total < SamplesToDo && tonext <= 0)
SSynth_processQueue(self, time);
}
synth->SamplesDone += SamplesToDo;
synth->ClockBase += (synth->SamplesDone/synth->SampleRate) * MIDI_CLOCK_RES;
synth->SamplesDone %= synth->SampleRate;
}
MidiSynth *SSynth_create(ALCdevice *device)
{
SSynth *synth;
/* This option is temporary. Once this synth is in a more usable state, a
* more generic selector should be used. */
if(!GetConfigValueBool("midi", "internal-synth", 0))
{
TRACE("Not using internal MIDI synth\n");
return NULL;
}
synth = SSynth_New(sizeof(*synth));
if(!synth)
{
ERR("Failed to allocate SSynth\n");
return NULL;
}
SSynth_Construct(synth, device);
return STATIC_CAST(MidiSynth, synth);
}
+510
View File
@@ -0,0 +1,510 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include "alMain.h"
#include "AL/al.h"
#include "AL/alc.h"
#include "alSource.h"
#include "alBuffer.h"
#include "alListener.h"
#include "alAuxEffectSlot.h"
#include "alu.h"
#include "mixer_defs.h"
extern inline void InitiatePositionArrays(ALuint frac, ALuint increment, ALuint *frac_arr, ALuint *pos_arr, ALuint size);
static inline HrtfMixerFunc SelectHrtfMixer(void)
{
#ifdef HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
return MixHrtf_SSE;
#endif
#ifdef HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
return MixHrtf_Neon;
#endif
return MixHrtf_C;
}
static inline MixerFunc SelectMixer(void)
{
#ifdef HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
return Mix_SSE;
#endif
#ifdef HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
return Mix_Neon;
#endif
return Mix_C;
}
static inline ResamplerFunc SelectResampler(enum Resampler Resampler, ALuint increment)
{
if(increment == FRACTIONONE)
return Resample_copy32_C;
switch(Resampler)
{
case PointResampler:
return Resample_point32_C;
case LinearResampler:
#ifdef HAVE_SSE4_1
if((CPUCapFlags&CPU_CAP_SSE4_1))
return Resample_lerp32_SSE41;
#endif
#ifdef HAVE_SSE2
if((CPUCapFlags&CPU_CAP_SSE2))
return Resample_lerp32_SSE2;
#endif
return Resample_lerp32_C;
case CubicResampler:
return Resample_cubic32_C;
case ResamplerMax:
/* Shouldn't happen */
break;
}
return Resample_point32_C;
}
static inline ALfloat Sample_ALbyte(ALbyte val)
{ return val * (1.0f/127.0f); }
static inline ALfloat Sample_ALshort(ALshort val)
{ return val * (1.0f/32767.0f); }
static inline ALfloat Sample_ALfloat(ALfloat val)
{ return val; }
#define DECL_TEMPLATE(T) \
static void Load_##T(ALfloat *dst, const T *src, ALuint srcstep, ALuint samples)\
{ \
ALuint i; \
for(i = 0;i < samples;i++) \
dst[i] = Sample_##T(src[i*srcstep]); \
}
DECL_TEMPLATE(ALbyte)
DECL_TEMPLATE(ALshort)
DECL_TEMPLATE(ALfloat)
#undef DECL_TEMPLATE
static void LoadSamples(ALfloat *dst, const ALvoid *src, ALuint srcstep, enum FmtType srctype, ALuint samples)
{
switch(srctype)
{
case FmtByte:
Load_ALbyte(dst, src, srcstep, samples);
break;
case FmtShort:
Load_ALshort(dst, src, srcstep, samples);
break;
case FmtFloat:
Load_ALfloat(dst, src, srcstep, samples);
break;
}
}
static void SilenceSamples(ALfloat *dst, ALuint samples)
{
ALuint i;
for(i = 0;i < samples;i++)
dst[i] = 0.0f;
}
static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter,
ALfloat *restrict dst, const ALfloat *restrict src,
ALuint numsamples, enum ActiveFilters type)
{
ALuint i;
switch(type)
{
case AF_None:
break;
case AF_LowPass:
ALfilterState_process(lpfilter, dst, src, numsamples);
return dst;
case AF_HighPass:
ALfilterState_process(hpfilter, dst, src, numsamples);
return dst;
case AF_BandPass:
for(i = 0;i < numsamples;)
{
ALfloat temp[64];
ALuint todo = minu(64, numsamples-i);
ALfilterState_process(lpfilter, temp, src+i, todo);
ALfilterState_process(hpfilter, dst+i, temp, todo);
i += todo;
}
return dst;
}
return src;
}
ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo)
{
MixerFunc Mix;
HrtfMixerFunc HrtfMix;
ResamplerFunc Resample;
ALsource *Source = src->Source;
ALbufferlistitem *BufferListItem;
ALuint DataPosInt, DataPosFrac;
ALboolean Looping;
ALuint increment;
enum Resampler Resampler;
ALenum State;
ALuint OutPos;
ALuint NumChannels;
ALuint SampleSize;
ALint64 DataSize64;
ALuint chan, j;
/* Get source info */
State = Source->state;
BufferListItem = ATOMIC_LOAD(&Source->current_buffer);
DataPosInt = Source->position;
DataPosFrac = Source->position_fraction;
Looping = Source->Looping;
increment = src->Step;
Resampler = (increment==FRACTIONONE) ? PointResampler : Source->Resampler;
NumChannels = Source->NumChannels;
SampleSize = Source->SampleSize;
Mix = SelectMixer();
HrtfMix = SelectHrtfMixer();
Resample = SelectResampler(Resampler, increment);
OutPos = 0;
do {
const ALuint BufferPrePadding = ResamplerPrePadding[Resampler];
const ALuint BufferPadding = ResamplerPadding[Resampler];
ALuint SrcBufferSize, DstBufferSize;
/* Figure out how many buffer samples will be needed */
DataSize64 = SamplesToDo-OutPos;
DataSize64 *= increment;
DataSize64 += DataPosFrac+FRACTIONMASK;
DataSize64 >>= FRACTIONBITS;
DataSize64 += BufferPadding+BufferPrePadding;
SrcBufferSize = (ALuint)mini64(DataSize64, BUFFERSIZE);
/* Figure out how many samples we can actually mix from this. */
DataSize64 = SrcBufferSize;
DataSize64 -= BufferPadding+BufferPrePadding;
DataSize64 <<= FRACTIONBITS;
DataSize64 -= DataPosFrac;
DstBufferSize = (ALuint)((DataSize64+(increment-1)) / increment);
DstBufferSize = minu(DstBufferSize, (SamplesToDo-OutPos));
/* Some mixers like having a multiple of 4, so try to give that unless
* this is the last update. */
if(OutPos+DstBufferSize < SamplesToDo)
DstBufferSize &= ~3;
for(chan = 0;chan < NumChannels;chan++)
{
const ALfloat *ResampledData;
ALfloat *SrcData = Device->SourceData;
ALuint SrcDataSize = 0;
if(Source->SourceType == AL_STATIC)
{
const ALbuffer *ALBuffer = BufferListItem->buffer;
const ALubyte *Data = ALBuffer->data;
ALuint DataSize;
ALuint pos;
/* If current pos is beyond the loop range, do not loop */
if(Looping == AL_FALSE || DataPosInt >= (ALuint)ALBuffer->LoopEnd)
{
Looping = AL_FALSE;
if(DataPosInt >= BufferPrePadding)
pos = DataPosInt - BufferPrePadding;
else
{
DataSize = BufferPrePadding - DataPosInt;
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
SilenceSamples(&SrcData[SrcDataSize], DataSize);
SrcDataSize += DataSize;
pos = 0;
}
/* Copy what's left to play in the source buffer, and clear the
* rest of the temp buffer */
DataSize = minu(SrcBufferSize - SrcDataSize, ALBuffer->SampleLen - pos);
LoadSamples(&SrcData[SrcDataSize], &Data[(pos*NumChannels + chan)*SampleSize],
NumChannels, ALBuffer->FmtType, DataSize);
SrcDataSize += DataSize;
SilenceSamples(&SrcData[SrcDataSize], SrcBufferSize - SrcDataSize);
SrcDataSize += SrcBufferSize - SrcDataSize;
}
else
{
ALuint LoopStart = ALBuffer->LoopStart;
ALuint LoopEnd = ALBuffer->LoopEnd;
if(DataPosInt >= LoopStart)
{
pos = DataPosInt-LoopStart;
while(pos < BufferPrePadding)
pos += LoopEnd-LoopStart;
pos -= BufferPrePadding;
pos += LoopStart;
}
else if(DataPosInt >= BufferPrePadding)
pos = DataPosInt - BufferPrePadding;
else
{
DataSize = BufferPrePadding - DataPosInt;
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
SilenceSamples(&SrcData[SrcDataSize], DataSize);
SrcDataSize += DataSize;
pos = 0;
}
/* Copy what's left of this loop iteration, then copy repeats
* of the loop section */
DataSize = LoopEnd - pos;
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
LoadSamples(&SrcData[SrcDataSize], &Data[(pos*NumChannels + chan)*SampleSize],
NumChannels, ALBuffer->FmtType, DataSize);
SrcDataSize += DataSize;
DataSize = LoopEnd-LoopStart;
while(SrcBufferSize > SrcDataSize)
{
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
LoadSamples(&SrcData[SrcDataSize], &Data[(LoopStart*NumChannels + chan)*SampleSize],
NumChannels, ALBuffer->FmtType, DataSize);
SrcDataSize += DataSize;
}
}
}
else
{
/* Crawl the buffer queue to fill in the temp buffer */
ALbufferlistitem *tmpiter = BufferListItem;
ALuint pos;
if(DataPosInt >= BufferPrePadding)
pos = DataPosInt - BufferPrePadding;
else
{
pos = BufferPrePadding - DataPosInt;
while(pos > 0)
{
ALbufferlistitem *prev;
if((prev=tmpiter->prev) != NULL)
tmpiter = prev;
else if(Looping)
{
while(tmpiter->next)
tmpiter = tmpiter->next;
}
else
{
ALuint DataSize = minu(SrcBufferSize - SrcDataSize, pos);
SilenceSamples(&SrcData[SrcDataSize], DataSize);
SrcDataSize += DataSize;
pos = 0;
break;
}
if(tmpiter->buffer)
{
if((ALuint)tmpiter->buffer->SampleLen > pos)
{
pos = tmpiter->buffer->SampleLen - pos;
break;
}
pos -= tmpiter->buffer->SampleLen;
}
}
}
while(tmpiter && SrcBufferSize > SrcDataSize)
{
const ALbuffer *ALBuffer;
if((ALBuffer=tmpiter->buffer) != NULL)
{
const ALubyte *Data = ALBuffer->data;
ALuint DataSize = ALBuffer->SampleLen;
/* Skip the data already played */
if(DataSize <= pos)
pos -= DataSize;
else
{
Data += (pos*NumChannels + chan)*SampleSize;
DataSize -= pos;
pos -= pos;
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
LoadSamples(&SrcData[SrcDataSize], Data, NumChannels,
ALBuffer->FmtType, DataSize);
SrcDataSize += DataSize;
}
}
tmpiter = tmpiter->next;
if(!tmpiter && Looping)
tmpiter = ATOMIC_LOAD(&Source->queue);
else if(!tmpiter)
{
SilenceSamples(&SrcData[SrcDataSize], SrcBufferSize - SrcDataSize);
SrcDataSize += SrcBufferSize - SrcDataSize;
}
}
}
/* Now resample, then filter and mix to the appropriate outputs. */
ResampledData = Resample(
&SrcData[BufferPrePadding], DataPosFrac, increment,
Device->ResampledData, DstBufferSize
);
{
DirectParams *parms = &src->Direct;
const ALfloat *samples;
samples = DoFilters(
&parms->Filters[chan].LowPass, &parms->Filters[chan].HighPass,
Device->FilteredData, ResampledData, DstBufferSize,
parms->Filters[chan].ActiveType
);
if(!src->IsHrtf)
Mix(samples, MaxChannels, parms->OutBuffer, parms->Mix.Gains[chan],
parms->Counter, OutPos, DstBufferSize);
else
HrtfMix(parms->OutBuffer, samples, parms->Counter, src->Offset,
OutPos, parms->Mix.Hrtf.IrSize, &parms->Mix.Hrtf.Params[chan],
&parms->Mix.Hrtf.State[chan], DstBufferSize);
}
for(j = 0;j < Device->NumAuxSends;j++)
{
SendParams *parms = &src->Send[j];
const ALfloat *samples;
if(!parms->OutBuffer)
continue;
samples = DoFilters(
&parms->Filters[chan].LowPass, &parms->Filters[chan].HighPass,
Device->FilteredData, ResampledData, DstBufferSize,
parms->Filters[chan].ActiveType
);
Mix(samples, 1, parms->OutBuffer, &parms->Gain,
parms->Counter, OutPos, DstBufferSize);
}
}
/* Update positions */
DataPosFrac += increment*DstBufferSize;
DataPosInt += DataPosFrac>>FRACTIONBITS;
DataPosFrac &= FRACTIONMASK;
OutPos += DstBufferSize;
src->Offset += DstBufferSize;
src->Direct.Counter = maxu(src->Direct.Counter, DstBufferSize) - DstBufferSize;
for(j = 0;j < Device->NumAuxSends;j++)
src->Send[j].Counter = maxu(src->Send[j].Counter, DstBufferSize) - DstBufferSize;
/* Handle looping sources */
while(1)
{
const ALbuffer *ALBuffer;
ALuint DataSize = 0;
ALuint LoopStart = 0;
ALuint LoopEnd = 0;
if((ALBuffer=BufferListItem->buffer) != NULL)
{
DataSize = ALBuffer->SampleLen;
LoopStart = ALBuffer->LoopStart;
LoopEnd = ALBuffer->LoopEnd;
if(LoopEnd > DataPosInt)
break;
}
if(Looping && Source->SourceType == AL_STATIC)
{
assert(LoopEnd > LoopStart);
DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart;
break;
}
if(DataSize > DataPosInt)
break;
if(!(BufferListItem=BufferListItem->next))
{
if(Looping)
BufferListItem = ATOMIC_LOAD(&Source->queue);
else
{
State = AL_STOPPED;
BufferListItem = NULL;
DataPosInt = 0;
DataPosFrac = 0;
break;
}
}
DataPosInt -= DataSize;
}
} while(State == AL_PLAYING && OutPos < SamplesToDo);
/* Update source info */
Source->state = State;
ATOMIC_STORE(&Source->current_buffer, BufferListItem);
Source->position = DataPosInt;
Source->position_fraction = DataPosFrac;
}
+126
View File
@@ -0,0 +1,126 @@
#include "config.h"
#include <assert.h>
#include "alMain.h"
#include "alu.h"
#include "alSource.h"
#include "alAuxEffectSlot.h"
static inline ALfloat point32(const ALfloat *vals, ALuint UNUSED(frac))
{ return vals[0]; }
static inline ALfloat lerp32(const ALfloat *vals, ALuint frac)
{ return lerp(vals[0], vals[1], frac * (1.0f/FRACTIONONE)); }
static inline ALfloat cubic32(const ALfloat *vals, ALuint frac)
{ return cubic(vals[-1], vals[0], vals[1], vals[2], frac * (1.0f/FRACTIONONE)); }
const ALfloat *Resample_copy32_C(const ALfloat *src, ALuint UNUSED(frac),
ALuint increment, ALfloat *restrict dst, ALuint numsamples)
{
assert(increment==FRACTIONONE);
#if defined(HAVE_SSE) || defined(HAVE_NEON)
/* Avoid copying the source data if it's aligned like the destination. */
if((((intptr_t)src)&15) == (((intptr_t)dst)&15))
return src;
#endif
memcpy(dst, src, numsamples*sizeof(ALfloat));
return dst;
}
#define DECL_TEMPLATE(Sampler) \
const ALfloat *Resample_##Sampler##_C(const ALfloat *src, ALuint frac, \
ALuint increment, ALfloat *restrict dst, ALuint numsamples) \
{ \
ALuint i; \
for(i = 0;i < numsamples;i++) \
{ \
dst[i] = Sampler(src, frac); \
\
frac += increment; \
src += frac>>FRACTIONBITS; \
frac &= FRACTIONMASK; \
} \
return dst; \
}
DECL_TEMPLATE(point32)
DECL_TEMPLATE(lerp32)
DECL_TEMPLATE(cubic32)
#undef DECL_TEMPLATE
void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples)
{
ALuint i;
for(i = 0;i < numsamples;i++)
*(dst++) = ALfilterState_processSingle(filter, *(src++));
}
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint IrSize,
ALfloat (*restrict Coeffs)[2],
const ALfloat (*restrict CoeffStep)[2],
ALfloat left, ALfloat right)
{
ALuint c;
for(c = 0;c < IrSize;c++)
{
const ALuint off = (Offset+c)&HRIR_MASK;
Values[off][0] += Coeffs[c][0] * left;
Values[off][1] += Coeffs[c][1] * right;
Coeffs[c][0] += CoeffStep[c][0];
Coeffs[c][1] += CoeffStep[c][1];
}
}
static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint IrSize,
ALfloat (*restrict Coeffs)[2],
ALfloat left, ALfloat right)
{
ALuint c;
for(c = 0;c < IrSize;c++)
{
const ALuint off = (Offset+c)&HRIR_MASK;
Values[off][0] += Coeffs[c][0] * left;
Values[off][1] += Coeffs[c][1] * right;
}
}
#define SUFFIX C
#include "mixer_inc.c"
#undef SUFFIX
void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize)
{
ALfloat gain, step;
ALuint c;
for(c = 0;c < OutChans;c++)
{
ALuint pos = 0;
gain = Gains[c].Current;
step = Gains[c].Step;
if(step != 1.0f && Counter > 0)
{
for(;pos < BufferSize && pos < Counter;pos++)
{
OutBuffer[c][OutPos+pos] += data[pos]*gain;
gain *= step;
}
if(pos == Counter)
gain = Gains[c].Target;
Gains[c].Current = gain;
}
if(!(gain > GAIN_SILENCE_THRESHOLD))
continue;
for(;pos < BufferSize;pos++)
OutBuffer[c][OutPos+pos] += data[pos]*gain;
}
}
+65
View File
@@ -0,0 +1,65 @@
#ifndef MIXER_DEFS_H
#define MIXER_DEFS_H
#include "AL/alc.h"
#include "AL/al.h"
#include "alMain.h"
#include "alu.h"
struct MixGains;
struct HrtfParams;
struct HrtfState;
/* C resamplers */
const ALfloat *Resample_copy32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
const ALfloat *Resample_point32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
const ALfloat *Resample_lerp32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
const ALfloat *Resample_cubic32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
/* C mixers */
void MixHrtf_C(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize,
const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate,
ALuint BufferSize);
void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize);
/* SSE mixers */
void MixHrtf_SSE(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize,
const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate,
ALuint BufferSize);
void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize);
/* SSE resamplers */
inline void InitiatePositionArrays(ALuint frac, ALuint increment, ALuint *frac_arr, ALuint *pos_arr, ALuint size)
{
ALuint i;
pos_arr[0] = 0;
frac_arr[0] = frac;
for(i = 1;i < size;i++)
{
ALuint frac_tmp = frac_arr[i-1] + increment;
pos_arr[i] = pos_arr[i-1] + (frac_tmp>>FRACTIONBITS);
frac_arr[i] = frac_tmp&FRACTIONMASK;
}
}
const ALfloat *Resample_lerp32_SSE2(const ALfloat *src, ALuint frac, ALuint increment,
ALfloat *restrict dst, ALuint numsamples);
const ALfloat *Resample_lerp32_SSE41(const ALfloat *src, ALuint frac, ALuint increment,
ALfloat *restrict dst, ALuint numsamples);
/* Neon mixers */
void MixHrtf_Neon(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize,
const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate,
ALuint BufferSize);
void Mix_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize);
#endif /* MIXER_DEFS_H */
+93
View File
@@ -0,0 +1,93 @@
#include "config.h"
#include "alMain.h"
#include "alSource.h"
#include "hrtf.h"
#include "mixer_defs.h"
#include "align.h"
#define REAL_MERGE(a,b) a##b
#define MERGE(a,b) REAL_MERGE(a,b)
#define MixHrtf MERGE(MixHrtf_,SUFFIX)
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint irSize,
ALfloat (*restrict Coeffs)[2],
const ALfloat (*restrict CoeffStep)[2],
ALfloat left, ALfloat right);
static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint irSize,
ALfloat (*restrict Coeffs)[2],
ALfloat left, ALfloat right);
void MixHrtf(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize,
const HrtfParams *hrtfparams, HrtfState *hrtfstate, ALuint BufferSize)
{
alignas(16) ALfloat Coeffs[HRIR_LENGTH][2];
ALuint Delay[2];
ALfloat left, right;
ALuint pos;
ALuint c;
for(c = 0;c < IrSize;c++)
{
Coeffs[c][0] = hrtfparams->Coeffs[c][0] - (hrtfparams->CoeffStep[c][0]*Counter);
Coeffs[c][1] = hrtfparams->Coeffs[c][1] - (hrtfparams->CoeffStep[c][1]*Counter);
}
Delay[0] = hrtfparams->Delay[0] - (hrtfparams->DelayStep[0]*Counter);
Delay[1] = hrtfparams->Delay[1] - (hrtfparams->DelayStep[1]*Counter);
for(pos = 0;pos < BufferSize && pos < Counter;pos++)
{
hrtfstate->History[Offset&SRC_HISTORY_MASK] = data[pos];
left = lerp(hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS))&SRC_HISTORY_MASK],
hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS)-1)&SRC_HISTORY_MASK],
(Delay[0]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE));
right = lerp(hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS))&SRC_HISTORY_MASK],
hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS)-1)&SRC_HISTORY_MASK],
(Delay[1]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE));
Delay[0] += hrtfparams->DelayStep[0];
Delay[1] += hrtfparams->DelayStep[1];
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f;
Offset++;
ApplyCoeffsStep(Offset, hrtfstate->Values, IrSize, Coeffs, hrtfparams->CoeffStep, left, right);
OutBuffer[FrontLeft][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0];
OutBuffer[FrontRight][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1];
OutPos++;
}
Delay[0] >>= HRTFDELAY_BITS;
Delay[1] >>= HRTFDELAY_BITS;
for(;pos < BufferSize;pos++)
{
hrtfstate->History[Offset&SRC_HISTORY_MASK] = data[pos];
left = hrtfstate->History[(Offset-Delay[0])&SRC_HISTORY_MASK];
right = hrtfstate->History[(Offset-Delay[1])&SRC_HISTORY_MASK];
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f;
Offset++;
ApplyCoeffs(Offset, hrtfstate->Values, IrSize, Coeffs, left, right);
OutBuffer[FrontLeft][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0];
OutBuffer[FrontRight][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1];
OutPos++;
}
}
#undef MixHrtf
#undef MERGE
#undef REAL_MERGE
+118
View File
@@ -0,0 +1,118 @@
#include "config.h"
#include <arm_neon.h>
#include "AL/al.h"
#include "AL/alc.h"
#include "alMain.h"
#include "alu.h"
#include "hrtf.h"
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint IrSize,
ALfloat (*restrict Coeffs)[2],
const ALfloat (*restrict CoeffStep)[2],
ALfloat left, ALfloat right)
{
ALuint c;
float32x4_t leftright4;
{
float32x2_t leftright2 = vdup_n_f32(0.0);
leftright2 = vset_lane_f32(left, leftright2, 0);
leftright2 = vset_lane_f32(right, leftright2, 1);
leftright4 = vcombine_f32(leftright2, leftright2);
}
for(c = 0;c < IrSize;c += 2)
{
const ALuint o0 = (Offset+c)&HRIR_MASK;
const ALuint o1 = (o0+1)&HRIR_MASK;
float32x4_t vals = vcombine_f32(vld1_f32((float32_t*)&Values[o0][0]),
vld1_f32((float32_t*)&Values[o1][0]));
float32x4_t coefs = vld1q_f32((float32_t*)&Coeffs[c][0]);
float32x4_t deltas = vld1q_f32(&CoeffStep[c][0]);
vals = vmlaq_f32(vals, coefs, leftright4);
coefs = vaddq_f32(coefs, deltas);
vst1_f32((float32_t*)&Values[o0][0], vget_low_f32(vals));
vst1_f32((float32_t*)&Values[o1][0], vget_high_f32(vals));
vst1q_f32(&Coeffs[c][0], coefs);
}
}
static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint IrSize,
ALfloat (*restrict Coeffs)[2],
ALfloat left, ALfloat right)
{
ALuint c;
float32x4_t leftright4;
{
float32x2_t leftright2 = vdup_n_f32(0.0);
leftright2 = vset_lane_f32(left, leftright2, 0);
leftright2 = vset_lane_f32(right, leftright2, 1);
leftright4 = vcombine_f32(leftright2, leftright2);
}
for(c = 0;c < IrSize;c += 2)
{
const ALuint o0 = (Offset+c)&HRIR_MASK;
const ALuint o1 = (o0+1)&HRIR_MASK;
float32x4_t vals = vcombine_f32(vld1_f32((float32_t*)&Values[o0][0]),
vld1_f32((float32_t*)&Values[o1][0]));
float32x4_t coefs = vld1q_f32((float32_t*)&Coeffs[c][0]);
vals = vmlaq_f32(vals, coefs, leftright4);
vst1_f32((float32_t*)&Values[o0][0], vget_low_f32(vals));
vst1_f32((float32_t*)&Values[o1][0], vget_high_f32(vals));
}
}
#define SUFFIX Neon
#include "mixer_inc.c"
#undef SUFFIX
void MixDirect_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize)
{
ALfloat gain, step;
float32x4_t gain4;
ALuint c;
for(c = 0;c < OutChans;c++)
{
ALuint pos = 0;
gain = Gains[c].Current;
step = Gains[c].Step;
if(step != 1.0f && Counter > 0)
{
for(;pos < BufferSize && pos < Counter;pos++)
{
OutBuffer[c][OutPos+pos] += data[pos]*gain;
gain *= step;
}
if(pos == Counter)
gain = Gains[c].Target;
Gains[c].Current = gain;
/* Mix until pos is aligned with 4 or the mix is done. */
for(;pos < BufferSize && (pos&3) != 0;pos++)
OutBuffer[c][OutPos+pos] += data[pos]*gain;
}
if(!(gain > GAIN_SILENCE_THRESHOLD))
continue;
gain4 = vdupq_n_f32(gain);
for(;BufferSize-pos > 3;pos += 4)
{
const float32x4_t val4 = vld1q_f32(&data[pos]);
float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]);
dry4 = vaddq_f32(dry4, vmulq_f32(val4, gain4));
vst1q_f32(&OutBuffer[c][OutPos+pos], dry4);
}
for(;pos < BufferSize;pos++)
OutBuffer[c][OutPos+pos] += data[pos]*gain;
}
}
+202
View File
@@ -0,0 +1,202 @@
#include "config.h"
#ifdef IN_IDE_PARSER
/* KDevelop's parser won't recognize these defines that get added by the -msse
* switch used to compile this source. Without them, xmmintrin.h fails to
* declare anything. */
#define __MMX__
#define __SSE__
#endif
#include <xmmintrin.h>
#include "AL/al.h"
#include "AL/alc.h"
#include "alMain.h"
#include "alu.h"
#include "alSource.h"
#include "alAuxEffectSlot.h"
#include "mixer_defs.h"
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint IrSize,
ALfloat (*restrict Coeffs)[2],
const ALfloat (*restrict CoeffStep)[2],
ALfloat left, ALfloat right)
{
const __m128 lrlr = _mm_setr_ps(left, right, left, right);
__m128 coeffs, deltas, imp0, imp1;
__m128 vals = _mm_setzero_ps();
ALuint i;
if((Offset&1))
{
const ALuint o0 = Offset&HRIR_MASK;
const ALuint o1 = (Offset+IrSize-1)&HRIR_MASK;
coeffs = _mm_load_ps(&Coeffs[0][0]);
deltas = _mm_load_ps(&CoeffStep[0][0]);
vals = _mm_loadl_pi(vals, (__m64*)&Values[o0][0]);
imp0 = _mm_mul_ps(lrlr, coeffs);
coeffs = _mm_add_ps(coeffs, deltas);
vals = _mm_add_ps(imp0, vals);
_mm_store_ps(&Coeffs[0][0], coeffs);
_mm_storel_pi((__m64*)&Values[o0][0], vals);
for(i = 1;i < IrSize-1;i += 2)
{
const ALuint o2 = (Offset+i)&HRIR_MASK;
coeffs = _mm_load_ps(&Coeffs[i+1][0]);
deltas = _mm_load_ps(&CoeffStep[i+1][0]);
vals = _mm_load_ps(&Values[o2][0]);
imp1 = _mm_mul_ps(lrlr, coeffs);
coeffs = _mm_add_ps(coeffs, deltas);
imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2));
vals = _mm_add_ps(imp0, vals);
_mm_store_ps(&Coeffs[i+1][0], coeffs);
_mm_store_ps(&Values[o2][0], vals);
imp0 = imp1;
}
vals = _mm_loadl_pi(vals, (__m64*)&Values[o1][0]);
imp0 = _mm_movehl_ps(imp0, imp0);
vals = _mm_add_ps(imp0, vals);
_mm_storel_pi((__m64*)&Values[o1][0], vals);
}
else
{
for(i = 0;i < IrSize;i += 2)
{
const ALuint o = (Offset + i)&HRIR_MASK;
coeffs = _mm_load_ps(&Coeffs[i][0]);
deltas = _mm_load_ps(&CoeffStep[i][0]);
vals = _mm_load_ps(&Values[o][0]);
imp0 = _mm_mul_ps(lrlr, coeffs);
coeffs = _mm_add_ps(coeffs, deltas);
vals = _mm_add_ps(imp0, vals);
_mm_store_ps(&Coeffs[i][0], coeffs);
_mm_store_ps(&Values[o][0], vals);
}
}
}
static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint IrSize,
ALfloat (*restrict Coeffs)[2],
ALfloat left, ALfloat right)
{
const __m128 lrlr = _mm_setr_ps(left, right, left, right);
__m128 vals = _mm_setzero_ps();
__m128 coeffs;
ALuint i;
if((Offset&1))
{
const ALuint o0 = Offset&HRIR_MASK;
const ALuint o1 = (Offset+IrSize-1)&HRIR_MASK;
__m128 imp0, imp1;
coeffs = _mm_load_ps(&Coeffs[0][0]);
vals = _mm_loadl_pi(vals, (__m64*)&Values[o0][0]);
imp0 = _mm_mul_ps(lrlr, coeffs);
vals = _mm_add_ps(imp0, vals);
_mm_storel_pi((__m64*)&Values[o0][0], vals);
for(i = 1;i < IrSize-1;i += 2)
{
const ALuint o2 = (Offset+i)&HRIR_MASK;
coeffs = _mm_load_ps(&Coeffs[i+1][0]);
vals = _mm_load_ps(&Values[o2][0]);
imp1 = _mm_mul_ps(lrlr, coeffs);
imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2));
vals = _mm_add_ps(imp0, vals);
_mm_store_ps(&Values[o2][0], vals);
imp0 = imp1;
}
vals = _mm_loadl_pi(vals, (__m64*)&Values[o1][0]);
imp0 = _mm_movehl_ps(imp0, imp0);
vals = _mm_add_ps(imp0, vals);
_mm_storel_pi((__m64*)&Values[o1][0], vals);
}
else
{
for(i = 0;i < IrSize;i += 2)
{
const ALuint o = (Offset + i)&HRIR_MASK;
coeffs = _mm_load_ps(&Coeffs[i][0]);
vals = _mm_load_ps(&Values[o][0]);
vals = _mm_add_ps(vals, _mm_mul_ps(lrlr, coeffs));
_mm_store_ps(&Values[o][0], vals);
}
}
}
#define SUFFIX SSE
#include "mixer_inc.c"
#undef SUFFIX
void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize)
{
ALfloat gain, step;
__m128 gain4, step4;
ALuint c;
for(c = 0;c < OutChans;c++)
{
ALuint pos = 0;
gain = Gains[c].Current;
step = Gains[c].Step;
if(step != 1.0f && Counter > 0)
{
/* Mix with applying gain steps in aligned multiples of 4. */
if(BufferSize-pos > 3 && Counter-pos > 3)
{
gain4 = _mm_setr_ps(
gain,
gain * step,
gain * step * step,
gain * step * step * step
);
step4 = _mm_set1_ps(step * step * step * step);
do {
const __m128 val4 = _mm_load_ps(&data[pos]);
__m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]);
dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
gain4 = _mm_mul_ps(gain4, step4);
_mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
pos += 4;
} while(BufferSize-pos > 3 && Counter-pos > 3);
gain = _mm_cvtss_f32(gain4);
}
/* Mix with applying left over gain steps that aren't aligned multiples of 4. */
for(;pos < BufferSize && pos < Counter;pos++)
{
OutBuffer[c][OutPos+pos] += data[pos]*gain;
gain *= step;
}
if(pos == Counter)
gain = Gains[c].Target;
Gains[c].Current = gain;
/* Mix until pos is aligned with 4 or the mix is done. */
for(;pos < BufferSize && (pos&3) != 0;pos++)
OutBuffer[c][OutPos+pos] += data[pos]*gain;
}
if(!(gain > GAIN_SILENCE_THRESHOLD))
continue;
gain4 = _mm_set1_ps(gain);
for(;BufferSize-pos > 3;pos += 4)
{
const __m128 val4 = _mm_load_ps(&data[pos]);
__m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]);
dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
_mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
}
for(;pos < BufferSize;pos++)
OutBuffer[c][OutPos+pos] += data[pos]*gain;
}
}
+78
View File
@@ -0,0 +1,78 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2014 by Timothy Arceri <t_arceri@yahoo.com.au>.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <xmmintrin.h>
#include <emmintrin.h>
#include "alu.h"
#include "mixer_defs.h"
const ALfloat *Resample_lerp32_SSE2(const ALfloat *src, ALuint frac, ALuint increment,
ALfloat *restrict dst, ALuint numsamples)
{
const __m128i increment4 = _mm_set1_epi32(increment*4);
const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE);
const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
alignas(16) union { ALuint i[4]; float f[4]; } pos_;
alignas(16) union { ALuint i[4]; float f[4]; } frac_;
__m128i frac4, pos4;
ALuint pos;
ALuint i;
InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
for(i = 0;numsamples-i > 3;i += 4)
{
const __m128 val1 = _mm_setr_ps(src[pos_.i[0]], src[pos_.i[1]], src[pos_.i[2]], src[pos_.i[3]]);
const __m128 val2 = _mm_setr_ps(src[pos_.i[0]+1], src[pos_.i[1]+1], src[pos_.i[2]+1], src[pos_.i[3]+1]);
/* val1 + (val2-val1)*mu */
const __m128 r0 = _mm_sub_ps(val2, val1);
const __m128 mu = _mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4);
const __m128 out = _mm_add_ps(val1, _mm_mul_ps(mu, r0));
_mm_store_ps(&dst[i], out);
frac4 = _mm_add_epi32(frac4, increment4);
pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
frac4 = _mm_and_si128(frac4, fracMask4);
_mm_store_ps(pos_.f, _mm_castsi128_ps(pos4));
}
pos = pos_.i[0];
frac = _mm_cvtsi128_si32(frac4);
for(;i < numsamples;i++)
{
dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
frac += increment;
pos += frac>>FRACTIONBITS;
frac &= FRACTIONMASK;
}
return dst;
}
+82
View File
@@ -0,0 +1,82 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2014 by Timothy Arceri <t_arceri@yahoo.com.au>.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <xmmintrin.h>
#include <emmintrin.h>
#include <smmintrin.h>
#include "alu.h"
#include "mixer_defs.h"
const ALfloat *Resample_lerp32_SSE41(const ALfloat *src, ALuint frac, ALuint increment,
ALfloat *restrict dst, ALuint numsamples)
{
const __m128i increment4 = _mm_set1_epi32(increment*4);
const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE);
const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
alignas(16) union { ALuint i[4]; float f[4]; } pos_;
alignas(16) union { ALuint i[4]; float f[4]; } frac_;
__m128i frac4, pos4;
ALuint pos;
ALuint i;
InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
for(i = 0;numsamples-i > 3;i += 4)
{
const __m128 val1 = _mm_setr_ps(src[pos_.i[0]], src[pos_.i[1]], src[pos_.i[2]], src[pos_.i[3]]);
const __m128 val2 = _mm_setr_ps(src[pos_.i[0]+1], src[pos_.i[1]+1], src[pos_.i[2]+1], src[pos_.i[3]+1]);
/* val1 + (val2-val1)*mu */
const __m128 r0 = _mm_sub_ps(val2, val1);
const __m128 mu = _mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4);
const __m128 out = _mm_add_ps(val1, _mm_mul_ps(mu, r0));
_mm_store_ps(&dst[i], out);
frac4 = _mm_add_epi32(frac4, increment4);
pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
frac4 = _mm_and_si128(frac4, fracMask4);
pos_.i[0] = _mm_extract_epi32(pos4, 0);
pos_.i[1] = _mm_extract_epi32(pos4, 1);
pos_.i[2] = _mm_extract_epi32(pos4, 2);
pos_.i[3] = _mm_extract_epi32(pos4, 3);
}
pos = pos_.i[0];
frac = _mm_cvtsi128_si32(frac4);
for(;i < numsamples;i++)
{
dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
frac += increment;
pos += frac>>FRACTIONBITS;
frac &= FRACTIONMASK;
}
return dst;
}
-493
View File
@@ -1,493 +0,0 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <unistd.h>
#include <errno.h>
#include <math.h>
#include "alMain.h"
#include "AL/al.h"
#include "AL/alc.h"
#include <sys/soundcard.h>
/*
* The OSS documentation talks about SOUND_MIXER_READ, but the header
* only contains MIXER_READ. Play safe. Same for WRITE.
*/
#ifndef SOUND_MIXER_READ
#define SOUND_MIXER_READ MIXER_READ
#endif
#ifndef SOUND_MIXER_WRITE
#define SOUND_MIXER_WRITE MIXER_WRITE
#endif
static const ALCchar oss_device[] = "OSS Software";
static const ALCchar oss_device_capture[] = "OSS Capture";
typedef struct {
int fd;
volatile int killNow;
ALvoid *thread;
ALubyte *mix_data;
int data_size;
RingBuffer *ring;
int doCapture;
} oss_data;
static int log2i(ALCuint x)
{
int y = 0;
while (x > 1)
{
x >>= 1;
y++;
}
return y;
}
static ALuint OSSProc(ALvoid *ptr)
{
ALCdevice *pDevice = (ALCdevice*)ptr;
oss_data *data = (oss_data*)pDevice->ExtraData;
ALint frameSize;
ssize_t wrote;
frameSize = aluChannelsFromFormat(pDevice->Format) *
aluBytesFromFormat(pDevice->Format);
while(!data->killNow && pDevice->Connected)
{
ALint len = data->data_size;
ALubyte *WritePtr = data->mix_data;
aluMixData(pDevice, WritePtr, len/frameSize);
while(len > 0 && !data->killNow)
{
wrote = write(data->fd, WritePtr, len);
if(wrote < 0)
{
if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
{
AL_PRINT("write failed: %s\n", strerror(errno));
aluHandleDisconnect(pDevice);
len = 0;
}
else
Sleep(1);
continue;
}
len -= wrote;
WritePtr += wrote;
}
}
return 0;
}
static ALuint OSSCaptureProc(ALvoid *ptr)
{
ALCdevice *pDevice = (ALCdevice*)ptr;
oss_data *data = (oss_data*)pDevice->ExtraData;
int frameSize;
int amt;
frameSize = aluBytesFromFormat(pDevice->Format);
frameSize *= aluChannelsFromFormat(pDevice->Format);
while(!data->killNow)
{
amt = read(data->fd, data->mix_data, data->data_size);
if(amt < 0)
{
AL_PRINT("read failed: %s\n", strerror(errno));
aluHandleDisconnect(pDevice);
break;
}
if(amt == 0)
{
Sleep(1);
continue;
}
if(data->doCapture)
WriteRingBuffer(data->ring, data->mix_data, amt/frameSize);
}
return 0;
}
static ALCboolean oss_open_playback(ALCdevice *device, const ALCchar *deviceName)
{
char driver[64];
oss_data *data;
strncpy(driver, GetConfigValue("oss", "device", "/dev/dsp"), sizeof(driver)-1);
driver[sizeof(driver)-1] = 0;
if(!deviceName)
deviceName = oss_device;
else if(strcmp(deviceName, oss_device) != 0)
return ALC_FALSE;
data = (oss_data*)calloc(1, sizeof(oss_data));
data->killNow = 0;
data->fd = open(driver, O_WRONLY);
if(data->fd == -1)
{
free(data);
AL_PRINT("Could not open %s: %s\n", driver, strerror(errno));
return ALC_FALSE;
}
device->szDeviceName = strdup(deviceName);
device->ExtraData = data;
return ALC_TRUE;
}
static void oss_close_playback(ALCdevice *device)
{
oss_data *data = (oss_data*)device->ExtraData;
close(data->fd);
free(data);
device->ExtraData = NULL;
}
static ALCboolean oss_reset_playback(ALCdevice *device)
{
oss_data *data = (oss_data*)device->ExtraData;
int numFragmentsLogSize;
int log2FragmentSize;
unsigned int periods;
audio_buf_info info;
ALuint frameSize;
int numChannels;
int ossFormat;
int ossSpeed;
char *err;
int i;
switch(aluBytesFromFormat(device->Format))
{
case 1:
ossFormat = AFMT_U8;
break;
case 4:
switch(aluChannelsFromFormat(device->Format))
{
case 1: device->Format = AL_FORMAT_MONO16; break;
case 2: device->Format = AL_FORMAT_STEREO16; break;
case 4: device->Format = AL_FORMAT_QUAD16; break;
case 6: device->Format = AL_FORMAT_51CHN16; break;
case 7: device->Format = AL_FORMAT_61CHN16; break;
case 8: device->Format = AL_FORMAT_71CHN16; break;
}
/* fall-through */
case 2:
ossFormat = AFMT_S16_NE;
break;
default:
AL_PRINT("Unknown format: 0x%x\n", device->Format);
return ALC_FALSE;
}
periods = device->NumUpdates;
numChannels = aluChannelsFromFormat(device->Format);
frameSize = numChannels * aluBytesFromFormat(device->Format);
ossSpeed = device->Frequency;
log2FragmentSize = log2i(device->UpdateSize * frameSize);
/* according to the OSS spec, 16 bytes are the minimum */
if (log2FragmentSize < 4)
log2FragmentSize = 4;
/* Subtract one period since the temp mixing buffer counts as one. Still
* need at least two on the card, though. */
if(periods > 2) periods--;
numFragmentsLogSize = (periods << 16) | log2FragmentSize;
#define ok(func, str) (i=(func),((i<0)?(err=(str)),0:1))
/* Don't fail if SETFRAGMENT fails. We can handle just about anything
* that's reported back via GETOSPACE */
ioctl(data->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize);
if (!(ok(ioctl(data->fd, SNDCTL_DSP_SETFMT, &ossFormat), "set format") &&
ok(ioctl(data->fd, SNDCTL_DSP_CHANNELS, &numChannels), "set channels") &&
ok(ioctl(data->fd, SNDCTL_DSP_SPEED, &ossSpeed), "set speed") &&
ok(ioctl(data->fd, SNDCTL_DSP_GETOSPACE, &info), "get space")))
{
AL_PRINT("%s failed: %s\n", err, strerror(errno));
return ALC_FALSE;
}
#undef ok
if((int)aluChannelsFromFormat(device->Format) != numChannels)
{
AL_PRINT("Could not set %d channels, got %d instead\n", aluChannelsFromFormat(device->Format), numChannels);
return ALC_FALSE;
}
if(!((ossFormat == AFMT_U8 && aluBytesFromFormat(device->Format) == 1) ||
(ossFormat == AFMT_S16_NE && aluBytesFromFormat(device->Format) == 2)))
{
AL_PRINT("Could not set %d-bit output, got format %#x\n", aluBytesFromFormat(device->Format)*8, ossFormat);
return ALC_FALSE;
}
device->Frequency = ossSpeed;
device->UpdateSize = info.fragsize / frameSize;
device->NumUpdates = info.fragments + 1;
data->data_size = device->UpdateSize * frameSize;
data->mix_data = calloc(1, data->data_size);
data->thread = StartThread(OSSProc, device);
if(data->thread == NULL)
{
free(data->mix_data);
data->mix_data = NULL;
return ALC_FALSE;
}
return ALC_TRUE;
}
static void oss_stop_playback(ALCdevice *device)
{
oss_data *data = (oss_data*)device->ExtraData;
if(!data->thread)
return;
data->killNow = 1;
StopThread(data->thread);
data->thread = NULL;
if(ioctl(data->fd, SNDCTL_DSP_RESET) != 0)
AL_PRINT("Error resetting device: %s\n", strerror(errno));
free(data->mix_data);
data->mix_data = NULL;
}
static ALCboolean oss_open_capture(ALCdevice *device, const ALCchar *deviceName)
{
int numFragmentsLogSize;
int log2FragmentSize;
unsigned int periods;
audio_buf_info info;
ALuint frameSize;
int numChannels;
char driver[64];
oss_data *data;
int ossFormat;
int ossSpeed;
char *err;
int i;
strncpy(driver, GetConfigValue("oss", "capture", "/dev/dsp"), sizeof(driver)-1);
driver[sizeof(driver)-1] = 0;
if(!deviceName)
deviceName = oss_device_capture;
else if(strcmp(deviceName, oss_device_capture) != 0)
return ALC_FALSE;
data = (oss_data*)calloc(1, sizeof(oss_data));
data->killNow = 0;
data->fd = open(driver, O_RDONLY);
if(data->fd == -1)
{
free(data);
AL_PRINT("Could not open %s: %s\n", driver, strerror(errno));
return ALC_FALSE;
}
switch(aluBytesFromFormat(device->Format))
{
case 1:
ossFormat = AFMT_U8;
break;
case 2:
ossFormat = AFMT_S16_NE;
break;
default:
AL_PRINT("Unknown format: 0x%x\n", device->Format);
close(data->fd);
free(data);
return ALC_FALSE;
}
periods = 4;
numChannels = aluChannelsFromFormat(device->Format);
frameSize = numChannels * aluBytesFromFormat(device->Format);
ossSpeed = device->Frequency;
log2FragmentSize = log2i(device->UpdateSize * device->NumUpdates *
frameSize / periods);
/* according to the OSS spec, 16 bytes are the minimum */
if (log2FragmentSize < 4)
log2FragmentSize = 4;
numFragmentsLogSize = (periods << 16) | log2FragmentSize;
#define ok(func, str) (i=(func),((i<0)?(err=(str)),0:1))
if (!(ok(ioctl(data->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize), "set fragment") &&
ok(ioctl(data->fd, SNDCTL_DSP_SETFMT, &ossFormat), "set format") &&
ok(ioctl(data->fd, SNDCTL_DSP_CHANNELS, &numChannels), "set channels") &&
ok(ioctl(data->fd, SNDCTL_DSP_SPEED, &ossSpeed), "set speed") &&
ok(ioctl(data->fd, SNDCTL_DSP_GETISPACE, &info), "get space")))
{
AL_PRINT("%s failed: %s\n", err, strerror(errno));
close(data->fd);
free(data);
return ALC_FALSE;
}
#undef ok
if((int)aluChannelsFromFormat(device->Format) != numChannels)
{
AL_PRINT("Could not set %d channels, got %d instead\n", aluChannelsFromFormat(device->Format), numChannels);
close(data->fd);
free(data);
return ALC_FALSE;
}
if(!((ossFormat == AFMT_U8 && aluBytesFromFormat(device->Format) == 1) ||
(ossFormat == AFMT_S16_NE && aluBytesFromFormat(device->Format) == 2)))
{
AL_PRINT("Could not set %d-bit input, got format %#x\n", aluBytesFromFormat(device->Format)*8, ossFormat);
close(data->fd);
free(data);
return ALC_FALSE;
}
data->ring = CreateRingBuffer(frameSize, device->UpdateSize * device->NumUpdates);
if(!data->ring)
{
AL_PRINT("ring buffer create failed\n");
close(data->fd);
free(data);
return ALC_FALSE;
}
data->data_size = info.fragsize;
data->mix_data = calloc(1, data->data_size);
device->ExtraData = data;
data->thread = StartThread(OSSCaptureProc, device);
if(data->thread == NULL)
{
device->ExtraData = NULL;
free(data->mix_data);
free(data);
return ALC_FALSE;
}
device->szDeviceName = strdup(deviceName);
return ALC_TRUE;
}
static void oss_close_capture(ALCdevice *device)
{
oss_data *data = (oss_data*)device->ExtraData;
data->killNow = 1;
StopThread(data->thread);
close(data->fd);
DestroyRingBuffer(data->ring);
free(data->mix_data);
free(data);
device->ExtraData = NULL;
}
static void oss_start_capture(ALCdevice *pDevice)
{
oss_data *data = (oss_data*)pDevice->ExtraData;
data->doCapture = 1;
}
static void oss_stop_capture(ALCdevice *pDevice)
{
oss_data *data = (oss_data*)pDevice->ExtraData;
data->doCapture = 0;
}
static void oss_capture_samples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
{
oss_data *data = (oss_data*)pDevice->ExtraData;
if(lSamples <= (ALCuint)RingBufferSize(data->ring))
ReadRingBuffer(data->ring, pBuffer, lSamples);
else
alcSetError(ALC_INVALID_VALUE);
}
static ALCuint oss_available_samples(ALCdevice *pDevice)
{
oss_data *data = (oss_data*)pDevice->ExtraData;
return RingBufferSize(data->ring);
}
BackendFuncs oss_funcs = {
oss_open_playback,
oss_close_playback,
oss_reset_playback,
oss_stop_playback,
oss_open_capture,
oss_close_capture,
oss_start_capture,
oss_stop_capture,
oss_capture_samples,
oss_available_samples
};
void alc_oss_init(BackendFuncs *func_list)
{
*func_list = oss_funcs;
}
void alc_oss_deinit(void)
{
}
void alc_oss_probe(int type)
{
if(type == DEVICE_PROBE)
AppendDeviceList(oss_device);
else if(type == ALL_DEVICE_PROBE)
AppendAllDeviceList(oss_device);
else if(type == CAPTURE_DEVICE_PROBE)
AppendCaptureDeviceList(oss_device_capture);
}
+450
View File
@@ -0,0 +1,450 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2010 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include "alMain.h"
#include "AL/al.h"
#include "AL/alc.h"
#include "alu.h"
extern inline void SetGains(const ALCdevice *device, ALfloat ingain, ALfloat gains[MaxChannels]);
static void SetSpeakerArrangement(const char *name, ALfloat SpeakerAngle[MaxChannels],
enum Channel Speaker2Chan[MaxChannels], ALint chans)
{
char *confkey, *next;
char *layout_str;
char *sep, *end;
enum Channel val;
const char *str;
int i;
if(!ConfigValueStr(NULL, name, &str) && !ConfigValueStr(NULL, "layout", &str))
return;
layout_str = strdup(str);
next = confkey = layout_str;
while(next && *next)
{
confkey = next;
next = strchr(confkey, ',');
if(next)
{
*next = 0;
do {
next++;
} while(isspace(*next) || *next == ',');
}
sep = strchr(confkey, '=');
if(!sep || confkey == sep)
{
ERR("Malformed speaker key: %s\n", confkey);
continue;
}
end = sep - 1;
while(isspace(*end) && end != confkey)
end--;
*(++end) = 0;
if(strcmp(confkey, "fl") == 0 || strcmp(confkey, "front-left") == 0)
val = FrontLeft;
else if(strcmp(confkey, "fr") == 0 || strcmp(confkey, "front-right") == 0)
val = FrontRight;
else if(strcmp(confkey, "fc") == 0 || strcmp(confkey, "front-center") == 0)
val = FrontCenter;
else if(strcmp(confkey, "bl") == 0 || strcmp(confkey, "back-left") == 0)
val = BackLeft;
else if(strcmp(confkey, "br") == 0 || strcmp(confkey, "back-right") == 0)
val = BackRight;
else if(strcmp(confkey, "bc") == 0 || strcmp(confkey, "back-center") == 0)
val = BackCenter;
else if(strcmp(confkey, "sl") == 0 || strcmp(confkey, "side-left") == 0)
val = SideLeft;
else if(strcmp(confkey, "sr") == 0 || strcmp(confkey, "side-right") == 0)
val = SideRight;
else
{
ERR("Unknown speaker for %s: \"%s\"\n", name, confkey);
continue;
}
*(sep++) = 0;
while(isspace(*sep))
sep++;
for(i = 0;i < chans;i++)
{
if(Speaker2Chan[i] == val)
{
long angle = strtol(sep, NULL, 10);
if(angle >= -180 && angle <= 180)
SpeakerAngle[i] = DEG2RAD(angle);
else
ERR("Invalid angle for speaker \"%s\": %ld\n", confkey, angle);
break;
}
}
}
free(layout_str);
layout_str = NULL;
for(i = 0;i < chans;i++)
{
int min = i;
int i2;
for(i2 = i+1;i2 < chans;i2++)
{
if(SpeakerAngle[i2] < SpeakerAngle[min])
min = i2;
}
if(min != i)
{
ALfloat tmpf;
enum Channel tmpc;
tmpf = SpeakerAngle[i];
SpeakerAngle[i] = SpeakerAngle[min];
SpeakerAngle[min] = tmpf;
tmpc = Speaker2Chan[i];
Speaker2Chan[i] = Speaker2Chan[min];
Speaker2Chan[min] = tmpc;
}
}
}
void ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat hwidth, ALfloat ingain, ALfloat gains[MaxChannels])
{
ALfloat tmpgains[MaxChannels] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
enum Channel Speaker2Chan[MaxChannels];
ALfloat SpeakerAngle[MaxChannels];
ALfloat langle, rangle;
ALfloat a;
ALuint i;
for(i = 0;i < device->NumChan;i++)
Speaker2Chan[i] = device->Speaker2Chan[i];
for(i = 0;i < device->NumChan;i++)
SpeakerAngle[i] = device->SpeakerAngle[i];
/* Some easy special-cases first... */
if(device->NumChan <= 1 || hwidth >= F_PI)
{
/* Full coverage for all speakers. */
for(i = 0;i < MaxChannels;i++)
gains[i] = 0.0f;
for(i = 0;i < device->NumChan;i++)
{
enum Channel chan = Speaker2Chan[i];
gains[chan] = ingain;
}
return;
}
if(hwidth <= 0.0f)
{
/* Infinitely small sound point. */
for(i = 0;i < MaxChannels;i++)
gains[i] = 0.0f;
for(i = 0;i < device->NumChan-1;i++)
{
if(angle >= SpeakerAngle[i] && angle < SpeakerAngle[i+1])
{
/* Sound is between speakers i and i+1 */
a = (angle-SpeakerAngle[i]) /
(SpeakerAngle[i+1]-SpeakerAngle[i]);
gains[Speaker2Chan[i]] = sqrtf(1.0f-a) * ingain;
gains[Speaker2Chan[i+1]] = sqrtf( a) * ingain;
return;
}
}
/* Sound is between last and first speakers */
if(angle < SpeakerAngle[0])
angle += F_2PI;
a = (angle-SpeakerAngle[i]) /
(F_2PI + SpeakerAngle[0]-SpeakerAngle[i]);
gains[Speaker2Chan[i]] = sqrtf(1.0f-a) * ingain;
gains[Speaker2Chan[0]] = sqrtf( a) * ingain;
return;
}
if(fabsf(angle)+hwidth > F_PI)
{
/* The coverage area would go outside of -pi...+pi. Instead, rotate the
* speaker angles so it would be as if angle=0, and keep them wrapped
* within -pi...+pi. */
if(angle > 0.0f)
{
ALuint done;
ALuint i = 0;
while(i < device->NumChan && device->SpeakerAngle[i]-angle < -F_PI)
i++;
for(done = 0;i < device->NumChan;done++)
{
SpeakerAngle[done] = device->SpeakerAngle[i]-angle;
Speaker2Chan[done] = device->Speaker2Chan[i];
i++;
}
for(i = 0;done < device->NumChan;i++)
{
SpeakerAngle[done] = device->SpeakerAngle[i]-angle + F_2PI;
Speaker2Chan[done] = device->Speaker2Chan[i];
done++;
}
}
else
{
/* NOTE: '< device->NumChan' on the iterators is correct here since
* we need to handle index 0. Because the iterators are unsigned,
* they'll underflow and wrap to become 0xFFFFFFFF, which will
* break as expected. */
ALuint done;
ALuint i = device->NumChan-1;
while(i < device->NumChan && device->SpeakerAngle[i]-angle > F_PI)
i--;
for(done = device->NumChan-1;i < device->NumChan;done--)
{
SpeakerAngle[done] = device->SpeakerAngle[i]-angle;
Speaker2Chan[done] = device->Speaker2Chan[i];
i--;
}
for(i = device->NumChan-1;done < device->NumChan;i--)
{
SpeakerAngle[done] = device->SpeakerAngle[i]-angle - F_2PI;
Speaker2Chan[done] = device->Speaker2Chan[i];
done--;
}
}
angle = 0.0f;
}
langle = angle - hwidth;
rangle = angle + hwidth;
/* First speaker */
i = 0;
do {
ALuint last = device->NumChan-1;
enum Channel chan = Speaker2Chan[i];
if(SpeakerAngle[i] >= langle && SpeakerAngle[i] <= rangle)
{
tmpgains[chan] = 1.0f;
continue;
}
if(SpeakerAngle[i] < langle && SpeakerAngle[i+1] > langle)
{
a = (langle-SpeakerAngle[i]) /
(SpeakerAngle[i+1]-SpeakerAngle[i]);
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, 1.0f-a);
}
if(SpeakerAngle[i] > rangle)
{
a = (F_2PI + rangle-SpeakerAngle[last]) /
(F_2PI + SpeakerAngle[i]-SpeakerAngle[last]);
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, a);
}
else if(SpeakerAngle[last] < rangle)
{
a = (rangle-SpeakerAngle[last]) /
(F_2PI + SpeakerAngle[i]-SpeakerAngle[last]);
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, a);
}
} while(0);
for(i = 1;i < device->NumChan-1;i++)
{
enum Channel chan = Speaker2Chan[i];
if(SpeakerAngle[i] >= langle && SpeakerAngle[i] <= rangle)
{
tmpgains[chan] = 1.0f;
continue;
}
if(SpeakerAngle[i] < langle && SpeakerAngle[i+1] > langle)
{
a = (langle-SpeakerAngle[i]) /
(SpeakerAngle[i+1]-SpeakerAngle[i]);
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, 1.0f-a);
}
if(SpeakerAngle[i] > rangle && SpeakerAngle[i-1] < rangle)
{
a = (rangle-SpeakerAngle[i-1]) /
(SpeakerAngle[i]-SpeakerAngle[i-1]);
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, a);
}
}
/* Last speaker */
i = device->NumChan-1;
do {
enum Channel chan = Speaker2Chan[i];
if(SpeakerAngle[i] >= langle && SpeakerAngle[i] <= rangle)
{
tmpgains[Speaker2Chan[i]] = 1.0f;
continue;
}
if(SpeakerAngle[i] > rangle && SpeakerAngle[i-1] < rangle)
{
a = (rangle-SpeakerAngle[i-1]) /
(SpeakerAngle[i]-SpeakerAngle[i-1]);
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, a);
}
if(SpeakerAngle[i] < langle)
{
a = (langle-SpeakerAngle[i]) /
(F_2PI + SpeakerAngle[0]-SpeakerAngle[i]);
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, 1.0f-a);
}
else if(SpeakerAngle[0] > langle)
{
a = (F_2PI + langle-SpeakerAngle[i]) /
(F_2PI + SpeakerAngle[0]-SpeakerAngle[i]);
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, 1.0f-a);
}
} while(0);
for(i = 0;i < device->NumChan;i++)
{
enum Channel chan = device->Speaker2Chan[i];
gains[chan] = sqrtf(tmpgains[chan]) * ingain;
}
}
ALvoid aluInitPanning(ALCdevice *Device)
{
const char *layoutname = NULL;
enum Channel *Speaker2Chan;
ALfloat *SpeakerAngle;
Speaker2Chan = Device->Speaker2Chan;
SpeakerAngle = Device->SpeakerAngle;
switch(Device->FmtChans)
{
case DevFmtMono:
Device->NumChan = 1;
Speaker2Chan[0] = FrontCenter;
SpeakerAngle[0] = DEG2RAD(0.0f);
layoutname = NULL;
break;
case DevFmtStereo:
Device->NumChan = 2;
Speaker2Chan[0] = FrontLeft;
Speaker2Chan[1] = FrontRight;
SpeakerAngle[0] = DEG2RAD(-90.0f);
SpeakerAngle[1] = DEG2RAD( 90.0f);
layoutname = "layout_stereo";
break;
case DevFmtQuad:
Device->NumChan = 4;
Speaker2Chan[0] = BackLeft;
Speaker2Chan[1] = FrontLeft;
Speaker2Chan[2] = FrontRight;
Speaker2Chan[3] = BackRight;
SpeakerAngle[0] = DEG2RAD(-135.0f);
SpeakerAngle[1] = DEG2RAD( -45.0f);
SpeakerAngle[2] = DEG2RAD( 45.0f);
SpeakerAngle[3] = DEG2RAD( 135.0f);
layoutname = "layout_quad";
break;
case DevFmtX51:
Device->NumChan = 5;
Speaker2Chan[0] = BackLeft;
Speaker2Chan[1] = FrontLeft;
Speaker2Chan[2] = FrontCenter;
Speaker2Chan[3] = FrontRight;
Speaker2Chan[4] = BackRight;
SpeakerAngle[0] = DEG2RAD(-110.0f);
SpeakerAngle[1] = DEG2RAD( -30.0f);
SpeakerAngle[2] = DEG2RAD( 0.0f);
SpeakerAngle[3] = DEG2RAD( 30.0f);
SpeakerAngle[4] = DEG2RAD( 110.0f);
layoutname = "layout_surround51";
break;
case DevFmtX51Side:
Device->NumChan = 5;
Speaker2Chan[0] = SideLeft;
Speaker2Chan[1] = FrontLeft;
Speaker2Chan[2] = FrontCenter;
Speaker2Chan[3] = FrontRight;
Speaker2Chan[4] = SideRight;
SpeakerAngle[0] = DEG2RAD(-90.0f);
SpeakerAngle[1] = DEG2RAD(-30.0f);
SpeakerAngle[2] = DEG2RAD( 0.0f);
SpeakerAngle[3] = DEG2RAD( 30.0f);
SpeakerAngle[4] = DEG2RAD( 90.0f);
layoutname = "layout_side51";
break;
case DevFmtX61:
Device->NumChan = 6;
Speaker2Chan[0] = SideLeft;
Speaker2Chan[1] = FrontLeft;
Speaker2Chan[2] = FrontCenter;
Speaker2Chan[3] = FrontRight;
Speaker2Chan[4] = SideRight;
Speaker2Chan[5] = BackCenter;
SpeakerAngle[0] = DEG2RAD(-90.0f);
SpeakerAngle[1] = DEG2RAD(-30.0f);
SpeakerAngle[2] = DEG2RAD( 0.0f);
SpeakerAngle[3] = DEG2RAD( 30.0f);
SpeakerAngle[4] = DEG2RAD( 90.0f);
SpeakerAngle[5] = DEG2RAD(180.0f);
layoutname = "layout_surround61";
break;
case DevFmtX71:
Device->NumChan = 7;
Speaker2Chan[0] = BackLeft;
Speaker2Chan[1] = SideLeft;
Speaker2Chan[2] = FrontLeft;
Speaker2Chan[3] = FrontCenter;
Speaker2Chan[4] = FrontRight;
Speaker2Chan[5] = SideRight;
Speaker2Chan[6] = BackRight;
SpeakerAngle[0] = DEG2RAD(-150.0f);
SpeakerAngle[1] = DEG2RAD( -90.0f);
SpeakerAngle[2] = DEG2RAD( -30.0f);
SpeakerAngle[3] = DEG2RAD( 0.0f);
SpeakerAngle[4] = DEG2RAD( 30.0f);
SpeakerAngle[5] = DEG2RAD( 90.0f);
SpeakerAngle[6] = DEG2RAD( 150.0f);
layoutname = "layout_surround71";
break;
}
if(layoutname && Device->Type != Loopback)
SetSpeakerArrangement(layoutname, SpeakerAngle, Speaker2Chan, Device->NumChan);
}
-298
View File
@@ -1,298 +0,0 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "alMain.h"
#include "AL/al.h"
#include "AL/alc.h"
#ifdef HAVE_DLFCN_H
#include <dlfcn.h>
#endif
#include <portaudio.h>
static void *pa_handle;
#define MAKE_FUNC(x) static typeof(x) * p##x
MAKE_FUNC(Pa_Initialize);
MAKE_FUNC(Pa_Terminate);
MAKE_FUNC(Pa_GetErrorText);
MAKE_FUNC(Pa_StartStream);
MAKE_FUNC(Pa_StopStream);
MAKE_FUNC(Pa_OpenStream);
MAKE_FUNC(Pa_CloseStream);
MAKE_FUNC(Pa_GetDefaultOutputDevice);
MAKE_FUNC(Pa_GetStreamInfo);
#undef MAKE_FUNC
static const ALCchar pa_device[] = "PortAudio Software";
static volatile ALuint load_count;
void pa_load(void)
{
const char *str;
PaError err;
if(load_count == 0)
{
#ifdef HAVE_DLFCN_H
#if defined(__APPLE__) && defined(__MACH__)
# define PALIB "libportaudio.2.dylib"
#else
# define PALIB "libportaudio.so.2"
#endif
pa_handle = dlopen(PALIB, RTLD_NOW);
if(!pa_handle)
return;
dlerror();
#define LOAD_FUNC(f) do { \
p##f = (typeof(f)*)dlsym(pa_handle, #f); \
if((str=dlerror()) != NULL) \
{ \
dlclose(pa_handle); \
pa_handle = NULL; \
AL_PRINT("Could not load %s from "PALIB": %s\n", #f, str); \
return; \
} \
} while(0)
#else
str = NULL;
pa_handle = (void*)0xDEADBEEF;
#define LOAD_FUNC(f) p##f = f
#endif
LOAD_FUNC(Pa_Initialize);
LOAD_FUNC(Pa_Terminate);
LOAD_FUNC(Pa_GetErrorText);
LOAD_FUNC(Pa_StartStream);
LOAD_FUNC(Pa_StopStream);
LOAD_FUNC(Pa_OpenStream);
LOAD_FUNC(Pa_CloseStream);
LOAD_FUNC(Pa_GetDefaultOutputDevice);
LOAD_FUNC(Pa_GetStreamInfo);
#undef LOAD_FUNC
if((err=pPa_Initialize()) != paNoError)
{
AL_PRINT("Pa_Initialize() returned an error: %s\n", pPa_GetErrorText(err));
#ifdef HAVE_DLFCN_H
dlclose(pa_handle);
#endif
pa_handle = NULL;
return;
}
}
++load_count;
}
void pa_unload(void)
{
if(load_count == 0 || --load_count > 0)
return;
pPa_Terminate();
#ifdef HAVE_DLFCN_H
dlclose(pa_handle);
#endif
pa_handle = NULL;
}
typedef struct {
PaStream *stream;
} pa_data;
static int pa_callback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
const PaStreamCallbackFlags statusFlags, void *userData)
{
ALCdevice *device = (ALCdevice*)userData;
(void)inputBuffer;
(void)timeInfo;
(void)statusFlags;
aluMixData(device, outputBuffer, framesPerBuffer);
return 0;
}
static ALCboolean pa_open_playback(ALCdevice *device, const ALCchar *deviceName)
{
const PaStreamInfo *streamInfo;
PaStreamParameters outParams;
pa_data *data;
PaError err;
if(!deviceName)
deviceName = pa_device;
else if(strcmp(deviceName, pa_device) != 0)
return ALC_FALSE;
pa_load();
if(pa_handle == NULL)
return ALC_FALSE;
data = (pa_data*)calloc(1, sizeof(pa_data));
device->ExtraData = data;
outParams.device = GetConfigValueInt("port", "device", -1);
if(outParams.device < 0)
outParams.device = pPa_GetDefaultOutputDevice();
outParams.suggestedLatency = (device->UpdateSize*device->NumUpdates) /
(float)device->Frequency;
outParams.hostApiSpecificStreamInfo = NULL;
switch(aluBytesFromFormat(device->Format))
{
case 1:
outParams.sampleFormat = paUInt8;
break;
case 2:
outParams.sampleFormat = paInt16;
break;
case 4:
outParams.sampleFormat = paFloat32;
break;
default:
AL_PRINT("Unknown format: 0x%x\n", device->Format);
device->ExtraData = NULL;
free(data);
pa_unload();
return ALC_FALSE;
}
outParams.channelCount = aluChannelsFromFormat(device->Format);
err = pPa_OpenStream(&data->stream, NULL, &outParams, device->Frequency,
device->UpdateSize, paNoFlag, pa_callback, device);
if(err != paNoError)
{
AL_PRINT("Pa_OpenStream() returned an error: %s\n", pPa_GetErrorText(err));
device->ExtraData = NULL;
free(data);
pa_unload();
return ALC_FALSE;
}
streamInfo = pPa_GetStreamInfo(data->stream);
err = pPa_StartStream(data->stream);
if(err != paNoError)
{
AL_PRINT("Pa_StartStream() returned an error: %s\n", pPa_GetErrorText(err));
pPa_CloseStream(data->stream);
device->ExtraData = NULL;
free(data);
pa_unload();
return ALC_FALSE;
}
device->szDeviceName = strdup(deviceName);
device->Frequency = streamInfo->sampleRate;
return ALC_TRUE;
}
static void pa_close_playback(ALCdevice *device)
{
pa_data *data = (pa_data*)device->ExtraData;
PaError err;
err = pPa_StopStream(data->stream);
if(err != paNoError)
fprintf(stderr, "Error stopping stream: %s\n", pPa_GetErrorText(err));
err = pPa_CloseStream(data->stream);
if(err != paNoError)
fprintf(stderr, "Error closing stream: %s\n", pPa_GetErrorText(err));
free(data);
device->ExtraData = NULL;
pa_unload();
}
static ALCboolean pa_reset_playback(ALCdevice *device)
{
pa_data *data = (pa_data*)device->ExtraData;
const PaStreamInfo *streamInfo;
streamInfo = pPa_GetStreamInfo(data->stream);
device->Frequency = streamInfo->sampleRate;
return ALC_TRUE;
}
static void pa_stop_playback(ALCdevice *device)
{
(void)device;
}
static ALCboolean pa_open_capture(ALCdevice *device, const ALCchar *deviceName)
{
return ALC_FALSE;
(void)device;
(void)deviceName;
}
static const BackendFuncs pa_funcs = {
pa_open_playback,
pa_close_playback,
pa_reset_playback,
pa_stop_playback,
pa_open_capture,
NULL,
NULL,
NULL,
NULL,
NULL
};
void alc_pa_init(BackendFuncs *func_list)
{
*func_list = pa_funcs;
}
void alc_pa_deinit(void)
{
}
void alc_pa_probe(int type)
{
pa_load();
if(!pa_handle) return;
if(type == DEVICE_PROBE)
AppendDeviceList(pa_device);
else if(type == ALL_DEVICE_PROBE)
AppendAllDeviceList(pa_device);
pa_unload();
}
-814
View File
@@ -1,814 +0,0 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2009 by Konstantinos Natsakis <konstantinos.natsakis@gmail.com>
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include "alMain.h"
#ifdef HAVE_DLFCN_H
#include <dlfcn.h>
#endif
#include <pulse/pulseaudio.h>
#if PA_API_VERSION == 11
#define PA_STREAM_ADJUST_LATENCY 0x2000U
static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
{
return (x == PA_STREAM_CREATING || x == PA_STREAM_READY);
}
static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x)
{
return (x == PA_CONTEXT_CONNECTING || x == PA_CONTEXT_AUTHORIZING ||
x == PA_CONTEXT_SETTING_NAME || x == PA_CONTEXT_READY);
}
#define PA_STREAM_IS_GOOD PA_STREAM_IS_GOOD
#define PA_CONTEXT_IS_GOOD PA_CONTEXT_IS_GOOD
#elif PA_API_VERSION != 12
#error Invalid PulseAudio API version
#endif
static void *pa_handle;
#define MAKE_FUNC(x) static typeof(x) * p##x
MAKE_FUNC(pa_context_unref);
MAKE_FUNC(pa_sample_spec_valid);
MAKE_FUNC(pa_stream_drop);
MAKE_FUNC(pa_strerror);
MAKE_FUNC(pa_context_get_state);
MAKE_FUNC(pa_stream_get_state);
MAKE_FUNC(pa_threaded_mainloop_signal);
MAKE_FUNC(pa_stream_peek);
MAKE_FUNC(pa_threaded_mainloop_wait);
MAKE_FUNC(pa_threaded_mainloop_unlock);
MAKE_FUNC(pa_threaded_mainloop_in_thread);
MAKE_FUNC(pa_context_new);
MAKE_FUNC(pa_threaded_mainloop_stop);
MAKE_FUNC(pa_context_disconnect);
MAKE_FUNC(pa_threaded_mainloop_start);
MAKE_FUNC(pa_threaded_mainloop_get_api);
MAKE_FUNC(pa_context_set_state_callback);
MAKE_FUNC(pa_stream_write);
MAKE_FUNC(pa_xfree);
MAKE_FUNC(pa_stream_connect_record);
MAKE_FUNC(pa_stream_connect_playback);
MAKE_FUNC(pa_path_get_filename);
MAKE_FUNC(pa_get_binary_name);
MAKE_FUNC(pa_threaded_mainloop_free);
MAKE_FUNC(pa_context_errno);
MAKE_FUNC(pa_xmalloc0);
MAKE_FUNC(pa_stream_unref);
MAKE_FUNC(pa_threaded_mainloop_accept);
MAKE_FUNC(pa_stream_set_write_callback);
MAKE_FUNC(pa_threaded_mainloop_new);
MAKE_FUNC(pa_context_connect);
MAKE_FUNC(pa_stream_get_buffer_attr);
MAKE_FUNC(pa_stream_set_buffer_attr_callback);
MAKE_FUNC(pa_stream_set_read_callback);
MAKE_FUNC(pa_stream_set_state_callback);
MAKE_FUNC(pa_stream_new);
MAKE_FUNC(pa_stream_disconnect);
MAKE_FUNC(pa_threaded_mainloop_lock);
MAKE_FUNC(pa_channel_map_init_auto);
MAKE_FUNC(pa_channel_map_parse);
#undef MAKE_FUNC
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif
typedef struct {
ALCuint samples;
ALCuint frame_size;
RingBuffer *ring;
pa_buffer_attr attr;
pa_sample_spec spec;
char path_name[PATH_MAX];
const char *context_name;
const char *stream_name;
pa_threaded_mainloop *loop;
pa_stream *stream;
pa_context *context;
} pulse_data;
static const ALCchar pulse_device[] = "PulseAudio Software";
static const ALCchar pulse_capture_device[] = "PulseAudio Capture";
static volatile ALuint load_count;
void pulse_load(void) //{{{
{
if(load_count == 0)
{
#ifdef _WIN32
pa_handle = LoadLibrary("libpulse-0.dll");
#define LOAD_FUNC(x) do { \
p##x = GetProcAddress(pa_handle, #x); \
if(!(p##x)) { \
AL_PRINT("Could not load %s from libpulse-0.dll\n", #x); \
FreeLibrary(pa_handle); \
pa_handle = NULL; \
return; \
} \
} while(0)
#elif defined (HAVE_DLFCN_H)
const char *err;
#if defined(__APPLE__) && defined(__MACH__)
pa_handle = dlopen("libpulse.0.dylib", RTLD_NOW);
#else
pa_handle = dlopen("libpulse.so.0", RTLD_NOW);
#endif
dlerror();
#define LOAD_FUNC(x) do { \
p##x = dlsym(pa_handle, #x); \
if((err=dlerror()) != NULL) { \
AL_PRINT("Could not load %s from libpulse: %s\n", #x, err); \
dlclose(pa_handle); \
pa_handle = NULL; \
return; \
} \
} while(0)
#else
pa_handle = (void*)0xDEADBEEF;
#define LOAD_FUNC(x) p##x = (x)
#endif
if(!pa_handle)
return;
LOAD_FUNC(pa_context_unref);
LOAD_FUNC(pa_sample_spec_valid);
LOAD_FUNC(pa_stream_drop);
LOAD_FUNC(pa_strerror);
LOAD_FUNC(pa_context_get_state);
LOAD_FUNC(pa_stream_get_state);
LOAD_FUNC(pa_threaded_mainloop_signal);
LOAD_FUNC(pa_stream_peek);
LOAD_FUNC(pa_threaded_mainloop_wait);
LOAD_FUNC(pa_threaded_mainloop_unlock);
LOAD_FUNC(pa_threaded_mainloop_in_thread);
LOAD_FUNC(pa_context_new);
LOAD_FUNC(pa_threaded_mainloop_stop);
LOAD_FUNC(pa_context_disconnect);
LOAD_FUNC(pa_threaded_mainloop_start);
LOAD_FUNC(pa_threaded_mainloop_get_api);
LOAD_FUNC(pa_context_set_state_callback);
LOAD_FUNC(pa_stream_write);
LOAD_FUNC(pa_xfree);
LOAD_FUNC(pa_stream_connect_record);
LOAD_FUNC(pa_stream_connect_playback);
LOAD_FUNC(pa_path_get_filename);
LOAD_FUNC(pa_get_binary_name);
LOAD_FUNC(pa_threaded_mainloop_free);
LOAD_FUNC(pa_context_errno);
LOAD_FUNC(pa_xmalloc0);
LOAD_FUNC(pa_stream_unref);
LOAD_FUNC(pa_threaded_mainloop_accept);
LOAD_FUNC(pa_stream_set_write_callback);
LOAD_FUNC(pa_threaded_mainloop_new);
LOAD_FUNC(pa_context_connect);
LOAD_FUNC(pa_stream_get_buffer_attr);
LOAD_FUNC(pa_stream_set_buffer_attr_callback);
LOAD_FUNC(pa_stream_set_read_callback);
LOAD_FUNC(pa_stream_set_state_callback);
LOAD_FUNC(pa_stream_new);
LOAD_FUNC(pa_stream_disconnect);
LOAD_FUNC(pa_threaded_mainloop_lock);
LOAD_FUNC(pa_channel_map_init_auto);
LOAD_FUNC(pa_channel_map_parse);
#undef LOAD_FUNC
}
++load_count;
} //}}}
void pulse_unload(void) //{{{
{
if(load_count == 0 || --load_count > 0)
return;
#ifdef _WIN32
FreeLibrary(pa_handle);
#elif defined (HAVE_DLFCN_H)
dlclose(pa_handle);
#endif
pa_handle = NULL;
} //}}}
// PulseAudio Event Callbacks //{{{
static void context_state_callback(pa_context *context, void *pdata) //{{{
{
ALCdevice *Device = pdata;
pulse_data *data = Device->ExtraData;
(void)context;
if(ppa_threaded_mainloop_in_thread(data->loop))
ppa_threaded_mainloop_signal(data->loop, 1);
}//}}}
static void stream_state_callback(pa_stream *stream, void *pdata) //{{{
{
ALCdevice *Device = pdata;
pulse_data *data = Device->ExtraData;
(void)stream;
if(ppa_threaded_mainloop_in_thread(data->loop))
ppa_threaded_mainloop_signal(data->loop, 1);
}//}}}
static void stream_buffer_attr_callback(pa_stream *stream, void *pdata) //{{{
{
ALCdevice *Device = pdata;
pulse_data *data = Device->ExtraData;
SuspendContext(NULL);
data->attr = *(ppa_stream_get_buffer_attr(stream));
if((data->attr.tlength%data->attr.minreq) != 0)
AL_PRINT("new tlength (%d) is not a multiple of minreq (%d)!\n",
data->attr.tlength, data->attr.minreq);
Device->UpdateSize = data->attr.minreq;
Device->NumUpdates = data->attr.tlength/data->attr.minreq;
ProcessContext(NULL);
}//}}}
static void context_state_callback2(pa_context *context, void *pdata) //{{{
{
ALCdevice *Device = pdata;
if(ppa_context_get_state(context) == PA_CONTEXT_FAILED)
{
AL_PRINT("Received context failure!\n");
aluHandleDisconnect(Device);
}
}//}}}
static void stream_state_callback2(pa_stream *stream, void *pdata) //{{{
{
ALCdevice *Device = pdata;
if(ppa_stream_get_state(stream) == PA_STREAM_FAILED)
{
AL_PRINT("Received stream failure!\n");
aluHandleDisconnect(Device);
}
}//}}}
//}}}
// PulseAudio I/O Callbacks //{{{
static void stream_write_callback(pa_stream *stream, size_t len, void *pdata) //{{{
{
ALCdevice *Device = pdata;
pulse_data *data = Device->ExtraData;
len -= len%data->attr.minreq;
if(len > 0)
{
void *buf = ppa_xmalloc0(len);
aluMixData(Device, buf, len/data->frame_size);
ppa_stream_write(stream, buf, len, ppa_xfree, 0, PA_SEEK_RELATIVE);
}
} //}}}
static void stream_read_callback(pa_stream *stream, size_t length, void *pdata) //{{{
{
ALCdevice *Device = pdata;
pulse_data *data = Device->ExtraData;
const void *buf;
if(ppa_stream_peek(stream, &buf, &length) < 0)
{
AL_PRINT("pa_stream_peek() failed: %s\n",
ppa_strerror(ppa_context_errno(data->context)));
return;
}
assert(buf);
assert(length);
length /= data->frame_size;
if(data->samples < length)
AL_PRINT("stream_read_callback: buffer overflow!\n");
WriteRingBuffer(data->ring, buf, (length<data->samples) ? length : data->samples);
ppa_stream_drop(stream);
} //}}}
//}}}
static ALCboolean pulse_open(ALCdevice *device, const ALCchar *device_name) //{{{
{
pulse_data *data = ppa_xmalloc0(sizeof(pulse_data));
pa_context_state_t state;
if(ppa_get_binary_name(data->path_name, sizeof(data->path_name)))
data->context_name = ppa_path_get_filename(data->path_name);
else
data->context_name = "OpenAL Soft";
if(!(data->loop = ppa_threaded_mainloop_new()))
{
AL_PRINT("pa_threaded_mainloop_new() failed!\n");
goto out;
}
if(ppa_threaded_mainloop_start(data->loop) < 0)
{
AL_PRINT("pa_threaded_mainloop_start() failed\n");
goto out;
}
ppa_threaded_mainloop_lock(data->loop);
device->ExtraData = data;
data->context = ppa_context_new(ppa_threaded_mainloop_get_api(data->loop), data->context_name);
if(!data->context)
{
AL_PRINT("pa_context_new() failed: %s\n",
ppa_strerror(ppa_context_errno(data->context)));
ppa_threaded_mainloop_unlock(data->loop);
goto out;
}
ppa_context_set_state_callback(data->context, context_state_callback, device);
if(ppa_context_connect(data->context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0)
{
AL_PRINT("Context did not connect: %s\n",
ppa_strerror(ppa_context_errno(data->context)));
ppa_context_unref(data->context);
data->context = NULL;
ppa_threaded_mainloop_unlock(data->loop);
goto out;
}
while((state=ppa_context_get_state(data->context)) != PA_CONTEXT_READY)
{
if(!PA_CONTEXT_IS_GOOD(state))
{
AL_PRINT("Context did not get ready: %s\n",
ppa_strerror(ppa_context_errno(data->context)));
ppa_context_unref(data->context);
data->context = NULL;
ppa_threaded_mainloop_unlock(data->loop);
goto out;
}
ppa_threaded_mainloop_wait(data->loop);
ppa_threaded_mainloop_accept(data->loop);
}
ppa_context_set_state_callback(data->context, context_state_callback2, device);
device->szDeviceName = strdup(device_name);
ppa_threaded_mainloop_unlock(data->loop);
return ALC_TRUE;
out:
if(data->loop)
{
ppa_threaded_mainloop_stop(data->loop);
ppa_threaded_mainloop_free(data->loop);
}
device->ExtraData = NULL;
ppa_xfree(data);
return ALC_FALSE;
} //}}}
static void pulse_close(ALCdevice *device) //{{{
{
pulse_data *data = device->ExtraData;
ppa_threaded_mainloop_lock(data->loop);
if(data->stream)
{
ppa_stream_disconnect(data->stream);
ppa_stream_unref(data->stream);
}
ppa_context_disconnect(data->context);
ppa_context_unref(data->context);
ppa_threaded_mainloop_unlock(data->loop);
ppa_threaded_mainloop_stop(data->loop);
ppa_threaded_mainloop_free(data->loop);
DestroyRingBuffer(data->ring);
device->ExtraData = NULL;
ppa_xfree(data);
} //}}}
//}}}
// OpenAL {{{
static ALCboolean pulse_open_playback(ALCdevice *device, const ALCchar *device_name) //{{{
{
if(!device_name)
device_name = pulse_device;
else if(strcmp(device_name, pulse_device) != 0)
return ALC_FALSE;
pulse_load();
if(!pa_handle)
return ALC_FALSE;
if(pulse_open(device, device_name) != ALC_FALSE)
return ALC_TRUE;
pulse_unload();
return ALC_FALSE;
} //}}}
static void pulse_close_playback(ALCdevice *device) //{{{
{
pulse_close(device);
pulse_unload();
} //}}}
static ALCboolean pulse_reset_playback(ALCdevice *device) //{{{
{
pulse_data *data = device->ExtraData;
pa_stream_state_t state;
pa_channel_map chanmap;
ppa_threaded_mainloop_lock(data->loop);
data->frame_size = aluBytesFromFormat(device->Format) *
aluChannelsFromFormat(device->Format);
data->attr.minreq = data->frame_size * device->UpdateSize;
data->attr.prebuf = -1;
data->attr.maxlength = -1;
data->attr.fragsize = -1;
data->attr.tlength = data->attr.minreq * device->NumUpdates;
data->stream_name = "Playback Stream";
switch(aluBytesFromFormat(device->Format))
{
case 1:
data->spec.format = PA_SAMPLE_U8;
break;
case 2:
data->spec.format = PA_SAMPLE_S16NE;
break;
case 4:
data->spec.format = PA_SAMPLE_FLOAT32NE;
break;
default:
AL_PRINT("Unknown format: 0x%x\n", device->Format);
ppa_threaded_mainloop_unlock(data->loop);
return ALC_FALSE;
}
data->spec.rate = device->Frequency;
data->spec.channels = aluChannelsFromFormat(device->Format);
if(ppa_sample_spec_valid(&data->spec) == 0)
{
AL_PRINT("Invalid sample format\n");
ppa_threaded_mainloop_unlock(data->loop);
return ALC_FALSE;
}
#ifdef _WIN32
if(!ppa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX))
{
AL_PRINT("Couldn't build map for channel count (%d)!", data->spec.channels);
ppa_threaded_mainloop_unlock(data->loop);
return ALC_FALSE;
}
#else
switch(data->spec.channels)
{
case 1:
ppa_channel_map_parse(&chanmap, "mono");
break;
case 2:
ppa_channel_map_parse(&chanmap, "front-left,front-right");
break;
case 4:
ppa_channel_map_parse(&chanmap, "front-left,front-right,rear-left,rear-right");
break;
case 6:
ppa_channel_map_parse(&chanmap, "front-left,front-right,rear-left,rear-right,front-center,lfe");
break;
case 7:
ppa_channel_map_parse(&chanmap, "front-left,front-right,front-center,lfe,rear-center,side-left,side-right");
break;
case 8:
ppa_channel_map_parse(&chanmap, "front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right");
break;
default:
AL_PRINT("Got unhandled channel count (%d)!", data->spec.channels);
ppa_threaded_mainloop_unlock(data->loop);
return ALC_FALSE;
}
#endif
data->stream = ppa_stream_new(data->context, data->stream_name, &data->spec, &chanmap);
if(!data->stream)
{
AL_PRINT("pa_stream_new() failed: %s\n",
ppa_strerror(ppa_context_errno(data->context)));
ppa_threaded_mainloop_unlock(data->loop);
return ALC_FALSE;
}
ppa_stream_set_state_callback(data->stream, stream_state_callback, device);
ppa_stream_set_write_callback(data->stream, stream_write_callback, device);
if(ppa_stream_connect_playback(data->stream, NULL, &data->attr, PA_STREAM_ADJUST_LATENCY, NULL, NULL) < 0)
{
AL_PRINT("Stream did not connect: %s\n",
ppa_strerror(ppa_context_errno(data->context)));
ppa_stream_unref(data->stream);
data->stream = NULL;
ppa_threaded_mainloop_unlock(data->loop);
return ALC_FALSE;
}
while((state=ppa_stream_get_state(data->stream)) != PA_STREAM_READY)
{
if(!PA_STREAM_IS_GOOD(state))
{
AL_PRINT("Stream did not get ready: %s\n",
ppa_strerror(ppa_context_errno(data->context)));
ppa_stream_unref(data->stream);
data->stream = NULL;
ppa_threaded_mainloop_unlock(data->loop);
return ALC_FALSE;
}
ppa_threaded_mainloop_wait(data->loop);
ppa_threaded_mainloop_accept(data->loop);
}
ppa_stream_set_state_callback(data->stream, stream_state_callback2, device);
stream_buffer_attr_callback(data->stream, device);
ppa_stream_set_buffer_attr_callback(data->stream, stream_buffer_attr_callback, device);
ppa_threaded_mainloop_unlock(data->loop);
return ALC_TRUE;
} //}}}
static void pulse_stop_playback(ALCdevice *device) //{{{
{
pulse_data *data = device->ExtraData;
if(!data->stream)
return;
ppa_threaded_mainloop_lock(data->loop);
ppa_stream_disconnect(data->stream);
ppa_stream_unref(data->stream);
data->stream = NULL;
ppa_threaded_mainloop_unlock(data->loop);
} //}}}
static ALCboolean pulse_open_capture(ALCdevice *device, const ALCchar *device_name) //{{{
{
pulse_data *data;
pa_stream_state_t state;
if(!device_name)
device_name = pulse_capture_device;
else if(strcmp(device_name, pulse_capture_device) != 0)
return ALC_FALSE;
pulse_load();
if(!pa_handle)
return ALC_FALSE;
if(pulse_open(device, device_name) == ALC_FALSE)
{
pulse_unload();
return ALC_FALSE;
}
data = device->ExtraData;
ppa_threaded_mainloop_lock(data->loop);
data->samples = device->UpdateSize * device->NumUpdates;
data->frame_size = aluBytesFromFormat(device->Format) *
aluChannelsFromFormat(device->Format);
if(!(data->ring = CreateRingBuffer(data->frame_size, data->samples)))
{
ppa_threaded_mainloop_unlock(data->loop);
pulse_close(device);
pulse_unload();
return ALC_FALSE;
}
data->attr.minreq = -1;
data->attr.prebuf = -1;
data->attr.maxlength = -1;
data->attr.tlength = -1;
data->attr.fragsize = data->frame_size * data->samples / 2;
data->stream_name = "Capture Stream";
data->spec.rate = device->Frequency;
data->spec.channels = aluChannelsFromFormat(device->Format);
switch(aluBytesFromFormat(device->Format))
{
case 1:
data->spec.format = PA_SAMPLE_U8;
break;
case 2:
data->spec.format = PA_SAMPLE_S16NE;
break;
case 4:
data->spec.format = PA_SAMPLE_FLOAT32NE;
break;
default:
AL_PRINT("Unknown format: 0x%x\n", device->Format);
ppa_threaded_mainloop_unlock(data->loop);
pulse_close(device);
pulse_unload();
return ALC_FALSE;
}
if(ppa_sample_spec_valid(&data->spec) == 0)
{
AL_PRINT("Invalid sample format\n");
ppa_threaded_mainloop_unlock(data->loop);
pulse_close(device);
pulse_unload();
return ALC_FALSE;
}
data->stream = ppa_stream_new(data->context, data->stream_name, &data->spec, NULL);
if(!data->stream)
{
AL_PRINT("pa_stream_new() failed: %s\n",
ppa_strerror(ppa_context_errno(data->context)));
ppa_threaded_mainloop_unlock(data->loop);
pulse_close(device);
pulse_unload();
return ALC_FALSE;
}
ppa_stream_set_state_callback(data->stream, stream_state_callback, device);
if(ppa_stream_connect_record(data->stream, NULL, &data->attr, PA_STREAM_ADJUST_LATENCY) < 0)
{
AL_PRINT("Stream did not connect: %s\n",
ppa_strerror(ppa_context_errno(data->context)));
ppa_stream_unref(data->stream);
ppa_threaded_mainloop_unlock(data->loop);
data->stream = NULL;
pulse_close(device);
pulse_unload();
return ALC_FALSE;
}
while((state=ppa_stream_get_state(data->stream)) != PA_STREAM_READY)
{
if(!PA_STREAM_IS_GOOD(state))
{
AL_PRINT("Stream did not get ready: %s\n",
ppa_strerror(ppa_context_errno(data->context)));
ppa_stream_unref(data->stream);
ppa_threaded_mainloop_unlock(data->loop);
data->stream = NULL;
pulse_close(device);
pulse_unload();
return ALC_FALSE;
}
ppa_threaded_mainloop_wait(data->loop);
ppa_threaded_mainloop_accept(data->loop);
}
ppa_stream_set_state_callback(data->stream, stream_state_callback2, device);
ppa_threaded_mainloop_unlock(data->loop);
return ALC_TRUE;
} //}}}
static void pulse_close_capture(ALCdevice *device) //{{{
{
pulse_close(device);
pulse_unload();
} //}}}
static void pulse_start_capture(ALCdevice *device) //{{{
{
pulse_data *data = device->ExtraData;
ppa_threaded_mainloop_lock(data->loop);
ppa_stream_set_read_callback(data->stream, stream_read_callback, device);
ppa_threaded_mainloop_unlock(data->loop);
} //}}}
static void pulse_stop_capture(ALCdevice *device) //{{{
{
pulse_data *data = device->ExtraData;
ppa_threaded_mainloop_lock(data->loop);
ppa_stream_set_read_callback(data->stream, NULL, NULL);
ppa_threaded_mainloop_unlock(data->loop);
} //}}}
static void pulse_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) //{{{
{
pulse_data *data = device->ExtraData;
ALCuint available = RingBufferSize(data->ring);
if(available < samples)
alcSetError(ALC_INVALID_VALUE);
else
ReadRingBuffer(data->ring, buffer, samples);
} //}}}
static ALCuint pulse_available_samples(ALCdevice *device) //{{{
{
pulse_data *data = device->ExtraData;
return RingBufferSize(data->ring);
} //}}}
BackendFuncs pulse_funcs = { //{{{
pulse_open_playback,
pulse_close_playback,
pulse_reset_playback,
pulse_stop_playback,
pulse_open_capture,
pulse_close_capture,
pulse_start_capture,
pulse_stop_capture,
pulse_capture_samples,
pulse_available_samples
}; //}}}
void alc_pulse_init(BackendFuncs *func_list) //{{{
{
*func_list = pulse_funcs;
} //}}}
void alc_pulse_deinit(void) //{{{
{
} //}}}
void alc_pulse_probe(int type) //{{{
{
pulse_load();
if(!pa_handle) return;
if(type == DEVICE_PROBE)
AppendDeviceList(pulse_device);
else if(type == ALL_DEVICE_PROBE)
AppendAllDeviceList(pulse_device);
else if(type == CAPTURE_DEVICE_PROBE)
AppendCaptureDeviceList(pulse_capture_device);
pulse_unload();
} //}}}
//}}}
-292
View File
@@ -1,292 +0,0 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <unistd.h>
#include <errno.h>
#include <math.h>
#include "alMain.h"
#include "AL/al.h"
#include "AL/alc.h"
#include <sys/audioio.h>
static const ALCchar solaris_device[] = "Solaris Software";
typedef struct {
int fd;
volatile int killNow;
ALvoid *thread;
ALubyte *mix_data;
int data_size;
} solaris_data;
static ALuint SolarisProc(ALvoid *ptr)
{
ALCdevice *pDevice = (ALCdevice*)ptr;
solaris_data *data = (solaris_data*)pDevice->ExtraData;
int remaining = 0;
ALint frameSize;
int wrote;
frameSize = aluChannelsFromFormat(pDevice->Format) *
aluBytesFromFormat(pDevice->Format);
while(!data->killNow && pDevice->Connected)
{
ALint len = data->data_size;
ALubyte *WritePtr = data->mix_data;
aluMixData(pDevice, WritePtr, len/frameSize);
while(len > 0 && !data->killNow)
{
wrote = write(data->fd, WritePtr, len);
if(wrote < 0)
{
if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
{
AL_PRINT("write failed: %s\n", strerror(errno));
aluHandleDisconnect(pDevice);
len = 0;
}
else
Sleep(1);
continue;
}
len -= wrote;
WritePtr += wrote;
}
}
return 0;
}
static ALCboolean solaris_open_playback(ALCdevice *device, const ALCchar *deviceName)
{
char driver[64];
solaris_data *data;
strncpy(driver, GetConfigValue("solaris", "device", "/dev/audio"), sizeof(driver)-1);
driver[sizeof(driver)-1] = 0;
if(!deviceName)
deviceName = solaris_device;
else if(strcmp(deviceName, solaris_device) != 0)
return ALC_FALSE;
data = (solaris_data*)calloc(1, sizeof(solaris_data));
data->killNow = 0;
data->fd = open(driver, O_WRONLY);
if(data->fd == -1)
{
free(data);
AL_PRINT("Could not open %s: %s\n", driver, strerror(errno));
return ALC_FALSE;
}
device->szDeviceName = strdup(deviceName);
device->ExtraData = data;
return ALC_TRUE;
}
static void solaris_close_playback(ALCdevice *device)
{
solaris_data *data = (solaris_data*)device->ExtraData;
close(data->fd);
free(data);
device->ExtraData = NULL;
}
static ALCboolean solaris_reset_playback(ALCdevice *device)
{
solaris_data *data = (solaris_data*)device->ExtraData;
audio_info_t info;
ALuint frameSize;
int numChannels;
AUDIO_INITINFO(&info);
switch(aluBytesFromFormat(device->Format))
{
case 1:
info.play.precision = 8;
info.play.encoding = AUDIO_ENCODING_LINEAR8;
break;
case 4:
switch(numChannels)
{
case 1: device->Format = AL_FORMAT_MONO16; break;
case 2: device->Format = AL_FORMAT_STEREO16; break;
case 4: device->Format = AL_FORMAT_QUAD16; break;
case 6: device->Format = AL_FORMAT_51CHN16; break;
case 7: device->Format = AL_FORMAT_61CHN16; break;
case 8: device->Format = AL_FORMAT_71CHN16; break;
}
/* fall-through */
case 2:
info.play.precision = 16;
info.play.encoding = AUDIO_ENCODING_LINEAR;
break;
default:
AL_PRINT("Unknown format: 0x%x\n", device->Format);
return ALC_FALSE;
}
numChannels = aluChannelsFromFormat(device->Format);
info.play.sample_rate = device->Frequency;
info.play.channels = numChannels;
frameSize = numChannels * aluBytesFromFormat(device->Format);
info.play.buffer_size = device->UpdateSize*device->NumUpdates * frameSize;
if(ioctl(data->fd, AUDIO_SETINFO, &info) < 0)
{
AL_PRINT("ioctl failed: %s\n", strerror(errno));
return ALC_FALSE;
}
if(aluChannelsFromFormat(device->Format) != info.play.channels)
{
AL_PRINT("Could not set %d channels, got %d instead\n", aluChannelsFromFormat(device->Format), info.play.channels);
return ALC_FALSE;
}
if(!((info.play.precision == 8 && aluBytesFromFormat(device->Format) == 1) ||
(info.play.precision == 16 && aluBytesFromFormat(device->Format) == 2)))
{
AL_PRINT("Could not set %d-bit output, got %d\n", aluBytesFromFormat(device->Format)*8, info.play.precision);
return ALC_FALSE;
}
device->Frequency = info.play.sample_rate;
device->UpdateSize = (info.play.buffer_size/device->NumUpdates) + 1;
data->data_size = device->UpdateSize * frameSize;
data->mix_data = calloc(1, data->data_size);
data->thread = StartThread(SolarisProc, device);
if(data->thread == NULL)
{
free(data->mix_data);
data->mix_data = NULL;
return ALC_FALSE;
}
return ALC_TRUE;
}
static void solaris_stop_playback(ALCdevice *device)
{
solaris_data *data = (solaris_data*)device->ExtraData;
if(!data->thread)
return;
data->killNow = 1;
StopThread(data->thread);
data->thread = NULL;
free(data->mix_data);
data->mix_data = NULL;
}
static ALCboolean solaris_open_capture(ALCdevice *device, const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei SampleSize)
{
(void)device;
(void)deviceName;
(void)frequency;
(void)format;
(void)SampleSize;
return ALC_FALSE;
}
static void solaris_close_capture(ALCdevice *device)
{
(void)device;
}
static void solaris_start_capture(ALCdevice *pDevice)
{
(void)pDevice;
}
static void solaris_stop_capture(ALCdevice *pDevice)
{
(void)pDevice;
}
static void solaris_capture_samples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
{
(void)pDevice;
(void)pBuffer;
(void)lSamples;
}
static ALCuint solaris_available_samples(ALCdevice *pDevice)
{
(void)pDevice;
return 0;
}
BackendFuncs solaris_funcs = {
solaris_open_playback,
solaris_close_playback,
solaris_reset_playback,
solaris_stop_playback,
solaris_open_capture,
solaris_close_capture,
solaris_start_capture,
solaris_stop_capture,
solaris_capture_samples,
solaris_available_samples
};
void alc_solaris_init(BackendFuncs *func_list)
{
*func_list = solaris_funcs;
}
void alc_solaris_deinit(void)
{
}
void alc_solaris_probe(ALCboolean capture)
{
if(type == DEVICE_PROBE)
AppendDeviceList(solaris_device);
else if(type == ALL_DEVICE_PROBE)
AppendAllDeviceList(solaris_device);
}
+87
View File
@@ -0,0 +1,87 @@
#ifndef AL_VECTOR_H
#define AL_VECTOR_H
#include <stdlib.h>
#include <AL/al.h>
/* "Base" vector type, designed to alias with the actual vector types. */
typedef struct vector__s {
ALsizei Capacity;
ALsizei Size;
} *vector_;
#define TYPEDEF_VECTOR(T, N) typedef struct { \
ALsizei Capacity; \
ALsizei Size; \
T Data[]; \
} _##N; \
typedef _##N* N; \
typedef const _##N* const_##N;
#define VECTOR(T) struct { \
ALsizei Capacity; \
ALsizei Size; \
T Data[]; \
}*
#define VECTOR_INIT(_x) do { (_x) = NULL; } while(0)
#define VECTOR_INIT_STATIC() NULL
#define VECTOR_DEINIT(_x) do { free((_x)); (_x) = NULL; } while(0)
/* Helper to increase a vector's reserve. Do not call directly. */
ALboolean vector_reserve(char *ptr, size_t base_size, size_t obj_size, ALsizei obj_count, ALboolean exact);
#define VECTOR_RESERVE(_x, _c) (vector_reserve((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_c), AL_TRUE))
ALboolean vector_resize(char *ptr, size_t base_size, size_t obj_size, ALsizei obj_count);
#define VECTOR_RESIZE(_x, _c) (vector_resize((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_c)))
#define VECTOR_CAPACITY(_x) ((_x) ? (_x)->Capacity : 0)
#define VECTOR_SIZE(_x) ((_x) ? (_x)->Size : 0)
#define VECTOR_ITER_BEGIN(_x) ((_x) ? (_x)->Data + 0 : NULL)
#define VECTOR_ITER_END(_x) ((_x) ? (_x)->Data + (_x)->Size : NULL)
ALboolean vector_insert(char *ptr, size_t base_size, size_t obj_size, void *ins_pos, const void *datstart, const void *datend);
#ifdef __GNUC__
#define TYPE_CHECK(T1, T2) __builtin_types_compatible_p(T1, T2)
#define VECTOR_INSERT(_x, _i, _s, _e) __extension__({ \
ALboolean _r; \
static_assert(TYPE_CHECK(__typeof((_x)->Data[0]), __typeof(*(_i))), "Incompatible insertion iterator"); \
static_assert(TYPE_CHECK(__typeof((_x)->Data[0]), __typeof(*(_s))), "Incompatible insertion source type"); \
static_assert(TYPE_CHECK(__typeof(*(_s)), __typeof(*(_e))), "Incompatible iterator sources"); \
_r = vector_insert((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_i), (_s), (_e)); \
_r; \
})
#else
#define VECTOR_INSERT(_x, _i, _s, _e) (vector_insert((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_i), (_s), (_e)))
#endif
#define VECTOR_PUSH_BACK(_x, _obj) (vector_reserve((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), VECTOR_SIZE(_x)+1, AL_FALSE) && \
(((_x)->Data[(_x)->Size++] = (_obj)),AL_TRUE))
#define VECTOR_POP_BACK(_x) ((void)((_x)->Size--))
#define VECTOR_BACK(_x) ((_x)->Data[(_x)->Size-1])
#define VECTOR_FRONT(_x) ((_x)->Data[0])
#define VECTOR_ELEM(_x, _o) ((_x)->Data[(_o)])
#define VECTOR_FOR_EACH(_t, _x, _f) do { \
_t *_iter = VECTOR_ITER_BEGIN((_x)); \
_t *_end = VECTOR_ITER_END((_x)); \
for(;_iter != _end;++_iter) \
_f(_iter); \
} while(0)
#define VECTOR_FIND_IF(_i, _t, _x, _f) do { \
_t *_iter = VECTOR_ITER_BEGIN((_x)); \
_t *_end = VECTOR_ITER_END((_x)); \
for(;_iter != _end;++_iter) \
{ \
if(_f(_iter)) \
break; \
} \
(_i) = _iter; \
} while(0)
#endif /* AL_VECTOR_H */
-347
View File
@@ -1,347 +0,0 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include "alMain.h"
#include "AL/al.h"
#include "AL/alc.h"
typedef struct {
FILE *f;
long DataStart;
ALvoid *buffer;
ALuint size;
volatile int killNow;
ALvoid *thread;
} wave_data;
static const ALCchar waveDevice[] = "Wave File Writer";
static ALuint WaveProc(ALvoid *ptr)
{
ALCdevice *pDevice = (ALCdevice*)ptr;
wave_data *data = (wave_data*)pDevice->ExtraData;
ALuint frameSize;
ALuint now, last;
size_t fs;
ALuint avail;
union {
short s;
char b[sizeof(short)];
} uSB;
uSB.s = 1;
frameSize = aluBytesFromFormat(pDevice->Format) *
aluChannelsFromFormat(pDevice->Format);
last = timeGetTime();
while(!data->killNow && pDevice->Connected)
{
now = timeGetTime();
avail = (now-last) * pDevice->Frequency / 1000;
if(avail < pDevice->UpdateSize)
{
Sleep(1);
continue;
}
while(avail >= pDevice->UpdateSize)
{
aluMixData(pDevice, data->buffer, pDevice->UpdateSize);
if(uSB.b[0] != 1 && aluBytesFromFormat(pDevice->Format) > 1)
{
ALubyte *bytes = data->buffer;
ALuint i;
for(i = 0;i < data->size;i++)
fputc(bytes[i^1], data->f);
}
else
fs = fwrite(data->buffer, frameSize, pDevice->UpdateSize,
data->f);
if(ferror(data->f))
{
AL_PRINT("Error writing to file\n");
aluHandleDisconnect(pDevice);
break;
}
avail -= pDevice->UpdateSize;
}
last = now;
}
return 0;
}
static ALCboolean wave_open_playback(ALCdevice *device, const ALCchar *deviceName)
{
wave_data *data;
const char *fname;
fname = GetConfigValue("wave", "file", "");
if(!fname[0])
return ALC_FALSE;
if(!deviceName)
deviceName = waveDevice;
else if(strcmp(deviceName, waveDevice) != 0)
return ALC_FALSE;
data = (wave_data*)calloc(1, sizeof(wave_data));
data->f = fopen(fname, "wb");
if(!data->f)
{
free(data);
AL_PRINT("Could not open file '%s': %s\n", fname, strerror(errno));
return ALC_FALSE;
}
device->szDeviceName = strdup(deviceName);
device->ExtraData = data;
return ALC_TRUE;
}
static void wave_close_playback(ALCdevice *device)
{
wave_data *data = (wave_data*)device->ExtraData;
fclose(data->f);
free(data);
device->ExtraData = NULL;
}
static ALCboolean wave_reset_playback(ALCdevice *device)
{
wave_data *data = (wave_data*)device->ExtraData;
ALuint channels, bits, i;
fseek(data->f, 0, SEEK_SET);
clearerr(data->f);
bits = aluBytesFromFormat(device->Format) * 8;
channels = aluChannelsFromFormat(device->Format);
switch(bits)
{
case 8:
case 16:
case 32:
if(channels == 0)
{
AL_PRINT("Unknown format?! %x\n", device->Format);
return ALC_FALSE;
}
break;
default:
AL_PRINT("Unknown format?! %x\n", device->Format);
return ALC_FALSE;
}
fprintf(data->f, "RIFF");
fputc(0, data->f); // 'RIFF' header len; filled in at close
fputc(0, data->f);
fputc(0, data->f);
fputc(0, data->f);
fprintf(data->f, "WAVE");
fprintf(data->f, "fmt ");
fputc(16, data->f); // 'fmt ' header len; 16 bytes for PCM
fputc(0, data->f);
fputc(0, data->f);
fputc(0, data->f);
// 16-bit val, format type id (PCM: 1)
fputc(1, data->f);
fputc(0, data->f);
// 16-bit val, channel count
fputc(channels&0xff, data->f);
fputc((channels>>8)&0xff, data->f);
// 32-bit val, frequency
fputc(device->Frequency&0xff, data->f);
fputc((device->Frequency>>8)&0xff, data->f);
fputc((device->Frequency>>16)&0xff, data->f);
fputc((device->Frequency>>24)&0xff, data->f);
// 32-bit val, bytes per second
i = device->Frequency * channels * bits / 8;
fputc(i&0xff, data->f);
fputc((i>>8)&0xff, data->f);
fputc((i>>16)&0xff, data->f);
fputc((i>>24)&0xff, data->f);
// 16-bit val, frame size
i = channels * bits / 8;
fputc(i&0xff, data->f);
fputc((i>>8)&0xff, data->f);
// 16-bit val, bits per sample
fputc(bits&0xff, data->f);
fputc((bits>>8)&0xff, data->f);
fprintf(data->f, "data");
fputc(0, data->f); // 'data' header len; filled in at close
fputc(0, data->f);
fputc(0, data->f);
fputc(0, data->f);
if(ferror(data->f))
{
AL_PRINT("Error writing header: %s\n", strerror(errno));
return ALC_FALSE;
}
data->DataStart = ftell(data->f);
data->size = device->UpdateSize * channels * bits / 8;
data->buffer = malloc(data->size);
if(!data->buffer)
{
AL_PRINT("buffer malloc failed\n");
return ALC_FALSE;
}
data->thread = StartThread(WaveProc, device);
if(data->thread == NULL)
{
free(data->buffer);
data->buffer = NULL;
return ALC_FALSE;
}
return ALC_TRUE;
}
static void wave_stop_playback(ALCdevice *device)
{
wave_data *data = (wave_data*)device->ExtraData;
ALuint dataLen;
long size;
if(!data->thread)
return;
data->killNow = 1;
StopThread(data->thread);
data->thread = NULL;
free(data->buffer);
data->buffer = NULL;
size = ftell(data->f);
if(size > 0)
{
dataLen = size - data->DataStart;
if(fseek(data->f, data->DataStart-4, SEEK_SET) == 0)
{
fputc(dataLen&0xff, data->f); // 'data' header len
fputc((dataLen>>8)&0xff, data->f);
fputc((dataLen>>16)&0xff, data->f);
fputc((dataLen>>24)&0xff, data->f);
}
if(fseek(data->f, 4, SEEK_SET) == 0)
{
size -= 8;
fputc(size&0xff, data->f); // 'WAVE' header len
fputc((size>>8)&0xff, data->f);
fputc((size>>16)&0xff, data->f);
fputc((size>>24)&0xff, data->f);
}
}
}
static ALCboolean wave_open_capture(ALCdevice *pDevice, const ALCchar *deviceName)
{
(void)pDevice;
(void)deviceName;
return ALC_FALSE;
}
static void wave_close_capture(ALCdevice *pDevice)
{
(void)pDevice;
}
static void wave_start_capture(ALCdevice *pDevice)
{
(void)pDevice;
}
static void wave_stop_capture(ALCdevice *pDevice)
{
(void)pDevice;
}
static void wave_capture_samples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
{
(void)pDevice;
(void)pBuffer;
(void)lSamples;
}
static ALCuint wave_available_samples(ALCdevice *pDevice)
{
(void)pDevice;
return 0;
}
BackendFuncs wave_funcs = {
wave_open_playback,
wave_close_playback,
wave_reset_playback,
wave_stop_playback,
wave_open_capture,
wave_close_capture,
wave_start_capture,
wave_stop_capture,
wave_capture_samples,
wave_available_samples
};
void alc_wave_init(BackendFuncs *func_list)
{
*func_list = wave_funcs;
}
void alc_wave_deinit(void)
{
}
void alc_wave_probe(int type)
{
if(*(GetConfigValue("wave", "file", "")) == 0)
return;
if(type == DEVICE_PROBE)
AppendDeviceList(waveDevice);
else if(type == ALL_DEVICE_PROBE)
AppendAllDeviceList(waveDevice);
}
-470
View File
@@ -1,470 +0,0 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#define _WIN32_WINNT 0x0500
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <windows.h>
#include <mmsystem.h>
#include "alMain.h"
#include "AL/al.h"
#include "AL/alc.h"
typedef struct {
// MMSYSTEM Capture Device
ALboolean bWaveInShutdown;
HANDLE hWaveInHdrEvent;
HANDLE hWaveInThreadEvent;
HANDLE hWaveInThread;
DWORD ulWaveInThreadID;
ALint lWaveInBuffersCommitted;
HWAVEIN hWaveInHandle;
WAVEHDR WaveInBuffer[4];
ALCchar *pCapturedSampleData;
ALuint ulCapturedDataSize;
ALuint ulReadCapturedDataPos;
ALuint ulWriteCapturedDataPos;
} WinMMData;
static ALCchar **CaptureDeviceList;
static ALuint NumCaptureDevices;
/*
WaveInProc
Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and
returns to the application (with more data)
*/
static void CALLBACK WaveInProc(HWAVEIN hDevice,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2)
{
ALCdevice *pDevice = (ALCdevice *)dwInstance;
WinMMData *pData = pDevice->ExtraData;
(void)hDevice;
(void)dwParam2;
if ((uMsg==WIM_DATA))
{
// Decrement number of buffers in use
pData->lWaveInBuffersCommitted--;
if (pData->bWaveInShutdown == AL_FALSE)
{
// Notify Wave Processor Thread that a Wave Header has returned
PostThreadMessage(pData->ulWaveInThreadID,uMsg,0,dwParam1);
}
else
{
if (pData->lWaveInBuffersCommitted == 0)
{
// Signal Wave Buffers Returned event
if (pData->hWaveInHdrEvent)
SetEvent(pData->hWaveInHdrEvent);
// Post 'Quit' Message to WaveIn Processor Thread
PostThreadMessage(pData->ulWaveInThreadID,WM_QUIT,0,0);
}
}
}
}
/*
CaptureThreadProc
Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new
audio data.
*/
DWORD WINAPI CaptureThreadProc(LPVOID lpParameter)
{
ALCdevice *pDevice = (ALCdevice*)lpParameter;
WinMMData *pData = pDevice->ExtraData;
ALuint ulOffset, ulMaxSize, ulSection;
LPWAVEHDR pWaveHdr;
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
if ((msg.message==WIM_DATA)&&(!pData->bWaveInShutdown))
{
SuspendContext(NULL);
pWaveHdr = ((LPWAVEHDR)msg.lParam);
// Calculate offset in local buffer to write data to
ulOffset = pData->ulWriteCapturedDataPos % pData->ulCapturedDataSize;
if ((ulOffset + pWaveHdr->dwBytesRecorded) > pData->ulCapturedDataSize)
{
ulSection = pData->ulCapturedDataSize - ulOffset;
memcpy(pData->pCapturedSampleData + ulOffset, pWaveHdr->lpData, ulSection);
memcpy(pData->pCapturedSampleData, pWaveHdr->lpData + ulSection, pWaveHdr->dwBytesRecorded - ulSection);
}
else
{
memcpy(pData->pCapturedSampleData + ulOffset, pWaveHdr->lpData, pWaveHdr->dwBytesRecorded);
}
pData->ulWriteCapturedDataPos += pWaveHdr->dwBytesRecorded;
if (pData->ulWriteCapturedDataPos > (pData->ulReadCapturedDataPos + pData->ulCapturedDataSize))
{
// Application has not read enough audio data from the capture buffer so data has been
// overwritten. Reset ReadPosition.
pData->ulReadCapturedDataPos = pData->ulWriteCapturedDataPos - pData->ulCapturedDataSize;
}
// To prevent an over-flow prevent the offset values from getting too large
ulMaxSize = pData->ulCapturedDataSize << 4;
if ((pData->ulReadCapturedDataPos > ulMaxSize) && (pData->ulWriteCapturedDataPos > ulMaxSize))
{
pData->ulReadCapturedDataPos -= ulMaxSize;
pData->ulWriteCapturedDataPos -= ulMaxSize;
}
// Send buffer back to capture more data
waveInAddBuffer(pData->hWaveInHandle,pWaveHdr,sizeof(WAVEHDR));
pData->lWaveInBuffersCommitted++;
ProcessContext(NULL);
}
}
// Signal Wave Thread completed event
if (pData->hWaveInThreadEvent)
SetEvent(pData->hWaveInThreadEvent);
ExitThread(0);
return 0;
}
static ALCboolean WinMMOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
{
(void)device;
(void)deviceName;
return ALC_FALSE;
}
static void WinMMClosePlayback(ALCdevice *device)
{
(void)device;
}
static ALCboolean WinMMOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName)
{
WAVEFORMATEX wfexCaptureFormat;
WinMMData *pData = NULL;
ALint lDeviceID = 0;
ALint lBufferSize;
ALuint i;
// Find the Device ID matching the deviceName if valid
if (deviceName)
{
for(i = 0;i < NumCaptureDevices;i++)
{
if (!strcmp(deviceName, CaptureDeviceList[i]))
{
lDeviceID = i;
break;
}
}
if(i == NumCaptureDevices)
return ALC_FALSE;
}
pData = calloc(1, sizeof(*pData));
if(!pData)
{
alcSetError(ALC_OUT_OF_MEMORY);
return ALC_FALSE;
}
memset(&wfexCaptureFormat, 0, sizeof(WAVEFORMATEX));
wfexCaptureFormat.wFormatTag = WAVE_FORMAT_PCM;
wfexCaptureFormat.nChannels = aluChannelsFromFormat(pDevice->Format);
wfexCaptureFormat.wBitsPerSample = aluBytesFromFormat(pDevice->Format) * 8;
wfexCaptureFormat.nBlockAlign = wfexCaptureFormat.wBitsPerSample *
wfexCaptureFormat.nChannels / 8;
wfexCaptureFormat.nSamplesPerSec = pDevice->Frequency;
wfexCaptureFormat.nAvgBytesPerSec = wfexCaptureFormat.nSamplesPerSec *
wfexCaptureFormat.nBlockAlign;
wfexCaptureFormat.cbSize = 0;
if (waveInOpen(&pData->hWaveInHandle, lDeviceID, &wfexCaptureFormat, (DWORD_PTR)&WaveInProc, (DWORD_PTR)pDevice, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
goto failure;
pData->hWaveInHdrEvent = CreateEvent(NULL, AL_TRUE, AL_FALSE, "WaveInAllHeadersReturned");
if (pData->hWaveInHdrEvent == NULL)
goto failure;
pData->hWaveInThreadEvent = CreateEvent(NULL, AL_TRUE, AL_FALSE, "WaveInThreadDestroyed");
if (pData->hWaveInThreadEvent == NULL)
goto failure;
// Allocate circular memory buffer for the captured audio
pData->ulCapturedDataSize = pDevice->UpdateSize*pDevice->NumUpdates *
wfexCaptureFormat.nBlockAlign;
// Make sure circular buffer is at least 100ms in size (and an exact multiple of
// the block alignment
if (pData->ulCapturedDataSize < (wfexCaptureFormat.nAvgBytesPerSec / 10))
{
pData->ulCapturedDataSize = wfexCaptureFormat.nAvgBytesPerSec / 10;
pData->ulCapturedDataSize -= (pData->ulCapturedDataSize % wfexCaptureFormat.nBlockAlign);
}
pData->pCapturedSampleData = (ALCchar*)malloc(pData->ulCapturedDataSize);
pData->lWaveInBuffersCommitted=0;
// Create 4 Buffers of 50ms each
lBufferSize = wfexCaptureFormat.nAvgBytesPerSec / 20;
lBufferSize -= (lBufferSize % wfexCaptureFormat.nBlockAlign);
for (i=0;i<4;i++)
{
memset(&pData->WaveInBuffer[i], 0, sizeof(WAVEHDR));
pData->WaveInBuffer[i].dwBufferLength = lBufferSize;
pData->WaveInBuffer[i].lpData = calloc(1,pData->WaveInBuffer[i].dwBufferLength);
pData->WaveInBuffer[i].dwFlags = 0;
pData->WaveInBuffer[i].dwLoops = 0;
waveInPrepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
waveInAddBuffer(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
pData->lWaveInBuffersCommitted++;
}
pData->ulReadCapturedDataPos = 0;
pData->ulWriteCapturedDataPos = 0;
pDevice->ExtraData = pData;
pData->hWaveInThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThreadProc, (LPVOID)pDevice, 0, &pData->ulWaveInThreadID);
if (pData->hWaveInThread == NULL)
goto failure;
pDevice->szDeviceName = strdup(CaptureDeviceList[lDeviceID]);
return ALC_TRUE;
failure:
for (i=0;i<4;i++)
{
if(pData->WaveInBuffer[i].lpData)
{
waveInUnprepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
free(pData->WaveInBuffer[i].lpData);
}
}
free(pData->pCapturedSampleData);
if(pData->hWaveInHandle)
waveInClose(pData->hWaveInHandle);
if(pData->hWaveInThread)
CloseHandle(pData->hWaveInThread);
if (pData->hWaveInHdrEvent)
CloseHandle(pData->hWaveInHdrEvent);
if (pData->hWaveInThreadEvent)
CloseHandle(pData->hWaveInThreadEvent);
free(pData);
pDevice->ExtraData = NULL;
return ALC_FALSE;
}
static void WinMMCloseCapture(ALCdevice *pDevice)
{
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
int i;
// Call waveOutReset to shutdown wave device
pData->bWaveInShutdown = AL_TRUE;
waveInReset(pData->hWaveInHandle);
// Wait for signal that all Wave Buffers have returned
WaitForSingleObjectEx(pData->hWaveInHdrEvent, 5000, FALSE);
// Wait for signal that Wave Thread has been destroyed
WaitForSingleObjectEx(pData->hWaveInThreadEvent, 5000, FALSE);
// Release the wave buffers
for (i=0;i<4;i++)
{
waveInUnprepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
free(pData->WaveInBuffer[i].lpData);
}
// Free Audio Buffer data
free(pData->pCapturedSampleData);
pData->pCapturedSampleData = NULL;
// Close the Wave device
waveInClose(pData->hWaveInHandle);
pData->hWaveInHandle = 0;
CloseHandle(pData->hWaveInThread);
pData->hWaveInThread = 0;
if (pData->hWaveInHdrEvent)
{
CloseHandle(pData->hWaveInHdrEvent);
pData->hWaveInHdrEvent = 0;
}
if (pData->hWaveInThreadEvent)
{
CloseHandle(pData->hWaveInThreadEvent);
pData->hWaveInThreadEvent = 0;
}
free(pData);
pDevice->ExtraData = NULL;
}
static void WinMMStartCapture(ALCdevice *pDevice)
{
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
waveInStart(pData->hWaveInHandle);
}
static void WinMMStopCapture(ALCdevice *pDevice)
{
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
waveInStop(pData->hWaveInHandle);
}
static void WinMMCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
{
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
ALuint ulSamples = (unsigned long)lSamples;
ALuint ulBytes, ulBytesToCopy;
ALuint ulCapturedSamples;
ALuint ulReadOffset;
ALuint frameSize = aluBytesFromFormat(pDevice->Format) *
aluChannelsFromFormat(pDevice->Format);
// Check that we have the requested numbers of Samples
ulCapturedSamples = (pData->ulWriteCapturedDataPos -
pData->ulReadCapturedDataPos) /
frameSize;
if(ulSamples > ulCapturedSamples)
{
alcSetError(ALC_INVALID_VALUE);
return;
}
ulBytes = ulSamples * frameSize;
// Get Read Offset
ulReadOffset = (pData->ulReadCapturedDataPos % pData->ulCapturedDataSize);
// Check for wrap-around condition
if ((ulReadOffset + ulBytes) > pData->ulCapturedDataSize)
{
// Copy data from last Read position to end of data
ulBytesToCopy = pData->ulCapturedDataSize - ulReadOffset;
memcpy(pBuffer, pData->pCapturedSampleData + ulReadOffset, ulBytesToCopy);
// Copy rest of the data from the start of the captured data
memcpy(((char *)pBuffer) + ulBytesToCopy, pData->pCapturedSampleData, ulBytes - ulBytesToCopy);
}
else
{
// Copy data from the read position in the captured data
memcpy(pBuffer, pData->pCapturedSampleData + ulReadOffset, ulBytes);
}
// Update Read Position
pData->ulReadCapturedDataPos += ulBytes;
}
static ALCuint WinMMAvailableSamples(ALCdevice *pDevice)
{
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
ALCuint lCapturedBytes = (pData->ulWriteCapturedDataPos - pData->ulReadCapturedDataPos);
return lCapturedBytes / (aluBytesFromFormat(pDevice->Format) *
aluChannelsFromFormat(pDevice->Format));
}
BackendFuncs WinMMFuncs = {
WinMMOpenPlayback,
WinMMClosePlayback,
NULL,
NULL,
WinMMOpenCapture,
WinMMCloseCapture,
WinMMStartCapture,
WinMMStopCapture,
WinMMCaptureSamples,
WinMMAvailableSamples
};
void alcWinMMInit(BackendFuncs *FuncList)
{
*FuncList = WinMMFuncs;
}
void alcWinMMDeinit()
{
ALuint lLoop;
for(lLoop = 0; lLoop < NumCaptureDevices; lLoop++)
free(CaptureDeviceList[lLoop]);
free(CaptureDeviceList);
CaptureDeviceList = NULL;
NumCaptureDevices = 0;
}
void alcWinMMProbe(int type)
{
ALuint lLoop;
if(type != CAPTURE_DEVICE_PROBE)
return;
for(lLoop = 0; lLoop < NumCaptureDevices; lLoop++)
free(CaptureDeviceList[lLoop]);
NumCaptureDevices = waveInGetNumDevs();
CaptureDeviceList = realloc(CaptureDeviceList, sizeof(ALCchar*) * NumCaptureDevices);
for(lLoop = 0; lLoop < NumCaptureDevices; lLoop++)
{
WAVEINCAPS WaveInCaps;
if(waveInGetDevCaps(lLoop, &WaveInCaps, sizeof(WAVEINCAPS)) == MMSYSERR_NOERROR)
{
char name[128];
snprintf(name, sizeof(name), "WaveIn on %s", WaveInCaps.szPname);
AppendCaptureDeviceList(name);
CaptureDeviceList[lLoop] = strdup(name);
}
else
CaptureDeviceList[lLoop] = strdup("");
}
}
+964 -204
View File
File diff suppressed because it is too large Load Diff
+84 -45
View File
@@ -1,75 +1,114 @@
#ifndef _AL_AUXEFFECTSLOT_H_
#define _AL_AUXEFFECTSLOT_H_
#include "AL/al.h"
#include "alMain.h"
#include "alEffect.h"
#include "alFilter.h"
#include "align.h"
#ifdef __cplusplus
extern "C" {
#endif
#define AL_EFFECTSLOT_EFFECT 0x0001
#define AL_EFFECTSLOT_GAIN 0x0002
#define AL_EFFECTSLOT_AUXILIARY_SEND_AUTO 0x0003
struct ALeffectStateVtable;
struct ALeffectslot;
#define AL_EFFECTSLOT_NULL 0x0000
typedef struct ALeffectState {
const struct ALeffectStateVtable *vtbl;
} ALeffectState;
typedef struct ALeffectState ALeffectState;
struct ALeffectStateVtable {
void (*const Destruct)(ALeffectState *state);
typedef struct ALeffectslot
{
ALeffect effect;
ALboolean (*const deviceUpdate)(ALeffectState *state, ALCdevice *device);
void (*const update)(ALeffectState *state, ALCdevice *device, const struct ALeffectslot *slot);
void (*const process)(ALeffectState *state, ALuint samplesToDo, const ALfloat *restrict samplesIn, ALfloat (*restrict samplesOut)[BUFFERSIZE]);
ALfloat Gain;
ALboolean AuxSendAuto;
void (*const Delete)(void *ptr);
};
#define DEFINE_ALEFFECTSTATE_VTABLE(T) \
DECLARE_THUNK(T, ALeffectState, void, Destruct) \
DECLARE_THUNK1(T, ALeffectState, ALboolean, deviceUpdate, ALCdevice*) \
DECLARE_THUNK2(T, ALeffectState, void, update, ALCdevice*, const ALeffectslot*) \
DECLARE_THUNK3(T, ALeffectState, void, process, ALuint, const ALfloat*restrict, ALfloatBUFFERSIZE*restrict) \
static void T##_ALeffectState_Delete(void *ptr) \
{ return T##_Delete(STATIC_UPCAST(T, ALeffectState, (ALeffectState*)ptr)); } \
\
static const struct ALeffectStateVtable T##_ALeffectState_vtable = { \
T##_ALeffectState_Destruct, \
\
T##_ALeffectState_deviceUpdate, \
T##_ALeffectState_update, \
T##_ALeffectState_process, \
\
T##_ALeffectState_Delete, \
}
struct ALeffectStateFactoryVtable;
typedef struct ALeffectStateFactory {
const struct ALeffectStateFactoryVtable *vtbl;
} ALeffectStateFactory;
struct ALeffectStateFactoryVtable {
ALeffectState *(*const create)(ALeffectStateFactory *factory);
};
#define DEFINE_ALEFFECTSTATEFACTORY_VTABLE(T) \
DECLARE_THUNK(T, ALeffectStateFactory, ALeffectState*, create) \
\
static const struct ALeffectStateFactoryVtable T##_ALeffectStateFactory_vtable = { \
T##_ALeffectStateFactory_create, \
}
typedef struct ALeffectslot {
ALenum EffectType;
ALeffectProps EffectProps;
volatile ALfloat Gain;
volatile ALboolean AuxSendAuto;
ATOMIC(ALenum) NeedsUpdate;
ALeffectState *EffectState;
ALfloat WetBuffer[BUFFERSIZE];
alignas(16) ALfloat WetBuffer[1][BUFFERSIZE];
ALuint refcount;
RefCount ref;
// Index to itself
ALuint effectslot;
struct ALeffectslot *next;
/* Self ID */
ALuint id;
} ALeffectslot;
ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots);
ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots);
ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot);
ALvoid AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint iValue);
ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues);
ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat flValue);
ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues);
ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *piValue);
ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues);
ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *pflValue);
ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues);
inline struct ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id)
{ return (struct ALeffectslot*)LookupUIntMapKey(&context->EffectSlotMap, id); }
inline struct ALeffectslot *RemoveEffectSlot(ALCcontext *context, ALuint id)
{ return (struct ALeffectslot*)RemoveUIntMapKey(&context->EffectSlotMap, id); }
ALenum InitEffectSlot(ALeffectslot *slot);
ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context);
struct ALeffectState {
ALvoid (*Destroy)(ALeffectState *State);
ALboolean (*DeviceUpdate)(ALeffectState *State, ALCdevice *Device);
ALvoid (*Update)(ALeffectState *State, ALCcontext *Context, const ALeffect *Effect);
ALvoid (*Process)(ALeffectState *State, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[OUTPUTCHANNELS]);
};
ALeffectStateFactory *ALnullStateFactory_getFactory(void);
ALeffectStateFactory *ALreverbStateFactory_getFactory(void);
ALeffectStateFactory *ALautowahStateFactory_getFactory(void);
ALeffectStateFactory *ALchorusStateFactory_getFactory(void);
ALeffectStateFactory *ALcompressorStateFactory_getFactory(void);
ALeffectStateFactory *ALdistortionStateFactory_getFactory(void);
ALeffectStateFactory *ALechoStateFactory_getFactory(void);
ALeffectStateFactory *ALequalizerStateFactory_getFactory(void);
ALeffectStateFactory *ALflangerStateFactory_getFactory(void);
ALeffectStateFactory *ALmodulatorStateFactory_getFactory(void);
ALeffectState *NoneCreate(void);
ALeffectState *EAXVerbCreate(void);
ALeffectState *VerbCreate(void);
ALeffectState *EchoCreate(void);
ALeffectStateFactory *ALdedicatedStateFactory_getFactory(void);
#define ALEffect_Destroy(a) ((a)->Destroy((a)))
#define ALEffect_DeviceUpdate(a,b) ((a)->DeviceUpdate((a),(b)))
#define ALEffect_Update(a,b,c) ((a)->Update((a),(b),(c)))
#define ALEffect_Process(a,b,c,d,e) ((a)->Process((a),(b),(c),(d),(e)))
ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *effect);
void InitEffectFactoryMap(void);
void DeinitEffectFactoryMap(void);
#ifdef __cplusplus
}
+92 -13
View File
@@ -1,32 +1,111 @@
#ifndef _AL_BUFFER_H_
#define _AL_BUFFER_H_
#include "AL/al.h"
#include "alMain.h"
#ifdef __cplusplus
extern "C" {
#endif
#define BUFFER_PADDING 2
/* User formats */
enum UserFmtType {
UserFmtByte = AL_BYTE_SOFT,
UserFmtUByte = AL_UNSIGNED_BYTE_SOFT,
UserFmtShort = AL_SHORT_SOFT,
UserFmtUShort = AL_UNSIGNED_SHORT_SOFT,
UserFmtInt = AL_INT_SOFT,
UserFmtUInt = AL_UNSIGNED_INT_SOFT,
UserFmtFloat = AL_FLOAT_SOFT,
UserFmtDouble = AL_DOUBLE_SOFT,
UserFmtByte3 = AL_BYTE3_SOFT,
UserFmtUByte3 = AL_UNSIGNED_BYTE3_SOFT,
UserFmtMulaw,
UserFmtAlaw,
UserFmtIMA4,
UserFmtMSADPCM,
};
enum UserFmtChannels {
UserFmtMono = AL_MONO_SOFT,
UserFmtStereo = AL_STEREO_SOFT,
UserFmtRear = AL_REAR_SOFT,
UserFmtQuad = AL_QUAD_SOFT,
UserFmtX51 = AL_5POINT1_SOFT, /* (WFX order) */
UserFmtX61 = AL_6POINT1_SOFT, /* (WFX order) */
UserFmtX71 = AL_7POINT1_SOFT, /* (WFX order) */
};
typedef struct ALbuffer
ALuint BytesFromUserFmt(enum UserFmtType type) DECL_CONST;
ALuint ChannelsFromUserFmt(enum UserFmtChannels chans) DECL_CONST;
inline ALuint FrameSizeFromUserFmt(enum UserFmtChannels chans, enum UserFmtType type)
{
ALshort *data;
ALsizei size;
return ChannelsFromUserFmt(chans) * BytesFromUserFmt(type);
}
ALenum format;
ALenum eOriginalFormat;
ALsizei frequency;
ALuint refcount; // Number of sources using this buffer (deletion can only occur when this is 0)
/* Storable formats */
enum FmtType {
FmtByte = UserFmtByte,
FmtShort = UserFmtShort,
FmtFloat = UserFmtFloat,
};
enum FmtChannels {
FmtMono = UserFmtMono,
FmtStereo = UserFmtStereo,
FmtRear = UserFmtRear,
FmtQuad = UserFmtQuad,
FmtX51 = UserFmtX51,
FmtX61 = UserFmtX61,
FmtX71 = UserFmtX71,
};
#define MAX_INPUT_CHANNELS (8)
// Index to itself
ALuint buffer;
ALuint BytesFromFmt(enum FmtType type) DECL_CONST;
ALuint ChannelsFromFmt(enum FmtChannels chans) DECL_CONST;
inline ALuint FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type)
{
return ChannelsFromFmt(chans) * BytesFromFmt(type);
}
struct ALbuffer *next;
typedef struct ALbuffer {
ALvoid *data;
ALsizei Frequency;
ALenum Format;
ALsizei SampleLen;
enum FmtChannels FmtChannels;
enum FmtType FmtType;
enum UserFmtChannels OriginalChannels;
enum UserFmtType OriginalType;
ALsizei OriginalSize;
ALsizei OriginalAlign;
ALsizei LoopStart;
ALsizei LoopEnd;
ALsizei UnpackAlign;
ALsizei PackAlign;
/* Number of times buffer was attached to a source (deletion can only occur when 0) */
RefCount ref;
RWLock lock;
/* Self ID */
ALuint id;
} ALbuffer;
ALvoid ALAPIENTRY alBufferSubDataEXT(ALuint buffer,ALenum format,const ALvoid *data,ALsizei offset,ALsizei length);
ALbuffer *NewBuffer(ALCcontext *context);
void DeleteBuffer(ALCdevice *device, ALbuffer *buffer);
ALenum LoadData(ALbuffer *buffer, ALuint freq, ALenum NewFormat, ALsizei frames, enum UserFmtChannels SrcChannels, enum UserFmtType SrcType, const ALvoid *data, ALsizei align, ALboolean storesrc);
inline struct ALbuffer *LookupBuffer(ALCdevice *device, ALuint id)
{ return (struct ALbuffer*)LookupUIntMapKey(&device->BufferMap, id); }
inline struct ALbuffer *RemoveBuffer(ALCdevice *device, ALuint id)
{ return (struct ALbuffer*)RemoveUIntMapKey(&device->BufferMap, id); }
ALvoid ReleaseALBuffers(ALCdevice *device);
-55
View File
@@ -1,55 +0,0 @@
#ifndef _AL_DATABUFFER_H_
#define _AL_DATABUFFER_H_
#include "AL/al.h"
#ifdef __cplusplus
extern "C" {
#endif
#define UNMAPPED 0
#define MAPPED 1
typedef struct ALdatabuffer
{
ALubyte *data;
ALuint size;
ALenum state;
ALenum usage;
/* Index to self */
ALuint databuffer;
struct ALdatabuffer *next;
} ALdatabuffer;
ALvoid ALAPIENTRY alGenDatabuffersEXT(ALsizei n,ALuint *puiBuffers);
ALvoid ALAPIENTRY alDeleteDatabuffersEXT(ALsizei n, const ALuint *puiBuffers);
ALboolean ALAPIENTRY alIsDatabufferEXT(ALuint uiBuffer);
ALvoid ALAPIENTRY alDatabufferDataEXT(ALuint buffer,const ALvoid *data,ALsizei size,ALenum usage);
ALvoid ALAPIENTRY alDatabufferSubDataEXT(ALuint buffer, ALuint start, ALsizei length, const ALvoid *data);
ALvoid ALAPIENTRY alGetDatabufferSubDataEXT(ALuint buffer, ALuint start, ALsizei length, ALvoid *data);
ALvoid ALAPIENTRY alDatabufferfEXT(ALuint buffer, ALenum eParam, ALfloat flValue);
ALvoid ALAPIENTRY alDatabufferfvEXT(ALuint buffer, ALenum eParam, const ALfloat* flValues);
ALvoid ALAPIENTRY alDatabufferiEXT(ALuint buffer, ALenum eParam, ALint lValue);
ALvoid ALAPIENTRY alDatabufferivEXT(ALuint buffer, ALenum eParam, const ALint* plValues);
ALvoid ALAPIENTRY alGetDatabufferfEXT(ALuint buffer, ALenum eParam, ALfloat *pflValue);
ALvoid ALAPIENTRY alGetDatabufferfvEXT(ALuint buffer, ALenum eParam, ALfloat* pflValues);
ALvoid ALAPIENTRY alGetDatabufferiEXT(ALuint buffer, ALenum eParam, ALint *plValue);
ALvoid ALAPIENTRY alGetDatabufferivEXT(ALuint buffer, ALenum eParam, ALint* plValues);
ALvoid ALAPIENTRY alSelectDatabufferEXT(ALenum target, ALuint uiBuffer);
ALvoid* ALAPIENTRY alMapDatabufferEXT(ALuint uiBuffer, ALuint start, ALsizei length, ALenum access);
ALvoid ALAPIENTRY alUnmapDatabufferEXT(ALuint uiBuffer);
ALvoid ReleaseALDatabuffers(ALCdevice *device);
#ifdef __cplusplus
}
#endif
#endif
+127 -204
View File
@@ -1,211 +1,69 @@
// NOTE: The effect structure is getting too large, it may be a good idea to
// start using a union or another form of unified storage.
#ifndef _AL_EFFECT_H_
#define _AL_EFFECT_H_
#include "AL/al.h"
#include "alMain.h"
#ifdef __cplusplus
extern "C" {
#endif
#define AL_EFFECT_TYPE 0x8001
#define AL_EFFECT_NULL 0x0000
#define AL_EFFECT_REVERB 0x0001
#define AL_EFFECT_CHORUS 0x0002
#define AL_EFFECT_DISTORTION 0x0003
#define AL_EFFECT_ECHO 0x0004
#define AL_EFFECT_FLANGER 0x0005
#define AL_EFFECT_FREQUENCY_SHIFTER 0x0006
#define AL_EFFECT_VOCAL_MORPHER 0x0007
#define AL_EFFECT_PITCH_SHIFTER 0x0008
#define AL_EFFECT_RING_MODULATOR 0x0009
#define AL_EFFECT_AUTOWAH 0x000A
#define AL_EFFECT_COMPRESSOR 0x000B
#define AL_EFFECT_EQUALIZER 0x000C
#define AL_EFFECT_EAXREVERB 0x8000
#define AL_REVERB_DENSITY 0x0001
#define AL_REVERB_DIFFUSION 0x0002
#define AL_REVERB_GAIN 0x0003
#define AL_REVERB_GAINHF 0x0004
#define AL_REVERB_DECAY_TIME 0x0005
#define AL_REVERB_DECAY_HFRATIO 0x0006
#define AL_REVERB_REFLECTIONS_GAIN 0x0007
#define AL_REVERB_REFLECTIONS_DELAY 0x0008
#define AL_REVERB_LATE_REVERB_GAIN 0x0009
#define AL_REVERB_LATE_REVERB_DELAY 0x000A
#define AL_REVERB_AIR_ABSORPTION_GAINHF 0x000B
#define AL_REVERB_ROOM_ROLLOFF_FACTOR 0x000C
#define AL_REVERB_DECAY_HFLIMIT 0x000D
#define AL_REVERB_MIN_DENSITY (0.0f)
#define AL_REVERB_MAX_DENSITY (1.0f)
#define AL_REVERB_DEFAULT_DENSITY (1.0f)
#define AL_REVERB_MIN_DIFFUSION (0.0f)
#define AL_REVERB_MAX_DIFFUSION (1.0f)
#define AL_REVERB_DEFAULT_DIFFUSION (1.0f)
#define AL_REVERB_MIN_GAIN (0.0f)
#define AL_REVERB_MAX_GAIN (1.0f)
#define AL_REVERB_DEFAULT_GAIN (0.32f)
#define AL_REVERB_MIN_GAINHF (0.0f)
#define AL_REVERB_MAX_GAINHF (1.0f)
#define AL_REVERB_DEFAULT_GAINHF (0.89f)
#define AL_REVERB_MIN_DECAY_TIME (0.1f)
#define AL_REVERB_MAX_DECAY_TIME (20.0f)
#define AL_REVERB_DEFAULT_DECAY_TIME (1.49f)
#define AL_REVERB_MIN_DECAY_HFRATIO (0.1f)
#define AL_REVERB_MAX_DECAY_HFRATIO (2.0f)
#define AL_REVERB_DEFAULT_DECAY_HFRATIO (0.83f)
#define AL_REVERB_MIN_REFLECTIONS_GAIN (0.0f)
#define AL_REVERB_MAX_REFLECTIONS_GAIN (3.16f)
#define AL_REVERB_DEFAULT_REFLECTIONS_GAIN (0.05f)
#define AL_REVERB_MIN_REFLECTIONS_DELAY (0.0f)
#define AL_REVERB_MAX_REFLECTIONS_DELAY (0.3f)
#define AL_REVERB_DEFAULT_REFLECTIONS_DELAY (0.007f)
#define AL_REVERB_MIN_LATE_REVERB_GAIN (0.0f)
#define AL_REVERB_MAX_LATE_REVERB_GAIN (10.0f)
#define AL_REVERB_DEFAULT_LATE_REVERB_GAIN (1.26f)
#define AL_REVERB_MIN_LATE_REVERB_DELAY (0.0f)
#define AL_REVERB_MAX_LATE_REVERB_DELAY (0.1f)
#define AL_REVERB_DEFAULT_LATE_REVERB_DELAY (0.011f)
#define AL_REVERB_MIN_AIR_ABSORPTION_GAINHF (0.892f)
#define AL_REVERB_MAX_AIR_ABSORPTION_GAINHF (1.0f)
#define AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF (0.994f)
#define AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR (0.0f)
#define AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR (10.0f)
#define AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f)
#define AL_REVERB_MIN_DECAY_HFLIMIT (AL_FALSE)
#define AL_REVERB_MAX_DECAY_HFLIMIT (AL_TRUE)
#define AL_REVERB_DEFAULT_DECAY_HFLIMIT (AL_TRUE)
#define AL_ECHO_DELAY 0x0001
#define AL_ECHO_LRDELAY 0x0002
#define AL_ECHO_DAMPING 0x0003
#define AL_ECHO_FEEDBACK 0x0004
#define AL_ECHO_SPREAD 0x0005
#define AL_ECHO_MIN_DELAY (0.0f)
#define AL_ECHO_MAX_DELAY (0.207f)
#define AL_ECHO_DEFAULT_DELAY (0.1f)
#define AL_ECHO_MIN_LRDELAY (0.0f)
#define AL_ECHO_MAX_LRDELAY (0.404f)
#define AL_ECHO_DEFAULT_LRDELAY (0.1f)
#define AL_ECHO_MIN_DAMPING (0.0f)
#define AL_ECHO_MAX_DAMPING (0.99f)
#define AL_ECHO_DEFAULT_DAMPING (0.5f)
#define AL_ECHO_MIN_FEEDBACK (0.0f)
#define AL_ECHO_MAX_FEEDBACK (1.0f)
#define AL_ECHO_DEFAULT_FEEDBACK (0.5f)
#define AL_ECHO_MIN_SPREAD (-1.0f)
#define AL_ECHO_MAX_SPREAD (1.0f)
#define AL_ECHO_DEFAULT_SPREAD (-1.0f)
#define AL_EAXREVERB_DENSITY 0x0001
#define AL_EAXREVERB_DIFFUSION 0x0002
#define AL_EAXREVERB_GAIN 0x0003
#define AL_EAXREVERB_GAINHF 0x0004
#define AL_EAXREVERB_GAINLF 0x0005
#define AL_EAXREVERB_DECAY_TIME 0x0006
#define AL_EAXREVERB_DECAY_HFRATIO 0x0007
#define AL_EAXREVERB_DECAY_LFRATIO 0x0008
#define AL_EAXREVERB_REFLECTIONS_GAIN 0x0009
#define AL_EAXREVERB_REFLECTIONS_DELAY 0x000A
#define AL_EAXREVERB_REFLECTIONS_PAN 0x000B
#define AL_EAXREVERB_LATE_REVERB_GAIN 0x000C
#define AL_EAXREVERB_LATE_REVERB_DELAY 0x000D
#define AL_EAXREVERB_LATE_REVERB_PAN 0x000E
#define AL_EAXREVERB_ECHO_TIME 0x000F
#define AL_EAXREVERB_ECHO_DEPTH 0x0010
#define AL_EAXREVERB_MODULATION_TIME 0x0011
#define AL_EAXREVERB_MODULATION_DEPTH 0x0012
#define AL_EAXREVERB_AIR_ABSORPTION_GAINHF 0x0013
#define AL_EAXREVERB_HFREFERENCE 0x0014
#define AL_EAXREVERB_LFREFERENCE 0x0015
#define AL_EAXREVERB_ROOM_ROLLOFF_FACTOR 0x0016
#define AL_EAXREVERB_DECAY_HFLIMIT 0x0017
#define AL_EAXREVERB_MIN_DENSITY (0.0f)
#define AL_EAXREVERB_MAX_DENSITY (1.0f)
#define AL_EAXREVERB_DEFAULT_DENSITY (1.0f)
#define AL_EAXREVERB_MIN_DIFFUSION (0.0f)
#define AL_EAXREVERB_MAX_DIFFUSION (1.0f)
#define AL_EAXREVERB_DEFAULT_DIFFUSION (1.0f)
#define AL_EAXREVERB_MIN_GAIN (0.0f)
#define AL_EAXREVERB_MAX_GAIN (1.0f)
#define AL_EAXREVERB_DEFAULT_GAIN (0.32f)
#define AL_EAXREVERB_MIN_GAINHF (0.0f)
#define AL_EAXREVERB_MAX_GAINHF (1.0f)
#define AL_EAXREVERB_DEFAULT_GAINHF (0.89f)
#define AL_EAXREVERB_MIN_GAINLF (0.0f)
#define AL_EAXREVERB_MAX_GAINLF (1.0f)
#define AL_EAXREVERB_DEFAULT_GAINLF (1.0f)
#define AL_EAXREVERB_MIN_DECAY_TIME (0.1f)
#define AL_EAXREVERB_MAX_DECAY_TIME (20.0f)
#define AL_EAXREVERB_DEFAULT_DECAY_TIME (1.49f)
#define AL_EAXREVERB_MIN_DECAY_HFRATIO (0.1f)
#define AL_EAXREVERB_MAX_DECAY_HFRATIO (2.0f)
#define AL_EAXREVERB_DEFAULT_DECAY_HFRATIO (0.83f)
#define AL_EAXREVERB_MIN_DECAY_LFRATIO (0.1f)
#define AL_EAXREVERB_MAX_DECAY_LFRATIO (2.0f)
#define AL_EAXREVERB_DEFAULT_DECAY_LFRATIO (1.0f)
#define AL_EAXREVERB_MIN_REFLECTIONS_GAIN (0.0f)
#define AL_EAXREVERB_MAX_REFLECTIONS_GAIN (3.16f)
#define AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN (0.05f)
#define AL_EAXREVERB_MIN_REFLECTIONS_DELAY (0.0f)
#define AL_EAXREVERB_MAX_REFLECTIONS_DELAY (0.3f)
#define AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY (0.007f)
#define AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ (0.0f)
#define AL_EAXREVERB_MIN_LATE_REVERB_GAIN (0.0f)
#define AL_EAXREVERB_MAX_LATE_REVERB_GAIN (10.0f)
#define AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN (1.26f)
#define AL_EAXREVERB_MIN_LATE_REVERB_DELAY (0.0f)
#define AL_EAXREVERB_MAX_LATE_REVERB_DELAY (0.1f)
#define AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY (0.011f)
#define AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ (0.0f)
#define AL_EAXREVERB_MIN_ECHO_TIME (0.075f)
#define AL_EAXREVERB_MAX_ECHO_TIME (0.25f)
#define AL_EAXREVERB_DEFAULT_ECHO_TIME (0.25f)
#define AL_EAXREVERB_MIN_ECHO_DEPTH (0.0f)
#define AL_EAXREVERB_MAX_ECHO_DEPTH (1.0f)
#define AL_EAXREVERB_DEFAULT_ECHO_DEPTH (0.0f)
#define AL_EAXREVERB_MIN_MODULATION_TIME (0.04f)
#define AL_EAXREVERB_MAX_MODULATION_TIME (4.0f)
#define AL_EAXREVERB_DEFAULT_MODULATION_TIME (0.25f)
#define AL_EAXREVERB_MIN_MODULATION_DEPTH (0.0f)
#define AL_EAXREVERB_MAX_MODULATION_DEPTH (1.0f)
#define AL_EAXREVERB_DEFAULT_MODULATION_DEPTH (0.0f)
#define AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF (0.892f)
#define AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF (1.0f)
#define AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF (0.994f)
#define AL_EAXREVERB_MIN_HFREFERENCE (1000.0f)
#define AL_EAXREVERB_MAX_HFREFERENCE (20000.0f)
#define AL_EAXREVERB_DEFAULT_HFREFERENCE (5000.0f)
#define AL_EAXREVERB_MIN_LFREFERENCE (20.0f)
#define AL_EAXREVERB_MAX_LFREFERENCE (1000.0f)
#define AL_EAXREVERB_DEFAULT_LFREFERENCE (250.0f)
#define AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR (0.0f)
#define AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR (10.0f)
#define AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f)
#define AL_EAXREVERB_MIN_DECAY_HFLIMIT (AL_FALSE)
#define AL_EAXREVERB_MAX_DECAY_HFLIMIT (AL_TRUE)
#define AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT (AL_TRUE)
struct ALeffect;
enum {
EAXREVERB = 0,
REVERB,
AUTOWAH,
CHORUS,
COMPRESSOR,
DISTORTION,
ECHO,
EQUALIZER,
FLANGER,
MODULATOR,
DEDICATED,
MAX_EFFECTS
};
extern ALboolean DisabledEffects[MAX_EFFECTS];
typedef struct ALeffect
{
// Effect type (AL_EFFECT_NULL, ...)
ALenum type;
extern ALfloat ReverbBoost;
extern ALboolean EmulateEAXReverb;
struct ALeffectVtable {
void (*const setParami)(struct ALeffect *effect, ALCcontext *context, ALenum param, ALint val);
void (*const setParamiv)(struct ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals);
void (*const setParamf)(struct ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val);
void (*const setParamfv)(struct ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals);
void (*const getParami)(const struct ALeffect *effect, ALCcontext *context, ALenum param, ALint *val);
void (*const getParamiv)(const struct ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals);
void (*const getParamf)(const struct ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val);
void (*const getParamfv)(const struct ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals);
};
#define DEFINE_ALEFFECT_VTABLE(T) \
const struct ALeffectVtable T##_vtable = { \
T##_setParami, T##_setParamiv, \
T##_setParamf, T##_setParamfv, \
T##_getParami, T##_getParamiv, \
T##_getParamf, T##_getParamfv, \
}
extern const struct ALeffectVtable ALeaxreverb_vtable;
extern const struct ALeffectVtable ALreverb_vtable;
extern const struct ALeffectVtable ALautowah_vtable;
extern const struct ALeffectVtable ALchorus_vtable;
extern const struct ALeffectVtable ALcompressor_vtable;
extern const struct ALeffectVtable ALdistortion_vtable;
extern const struct ALeffectVtable ALecho_vtable;
extern const struct ALeffectVtable ALequalizer_vtable;
extern const struct ALeffectVtable ALflanger_vtable;
extern const struct ALeffectVtable ALmodulator_vtable;
extern const struct ALeffectVtable ALnull_vtable;
extern const struct ALeffectVtable ALdedicated_vtable;
typedef union ALeffectProps {
struct {
// Shared Reverb Properties
ALfloat Density;
@@ -235,6 +93,34 @@ typedef struct ALeffect
ALfloat LFReference;
} Reverb;
struct {
ALfloat AttackTime;
ALfloat ReleaseTime;
ALfloat PeakGain;
ALfloat Resonance;
} Autowah;
struct {
ALint Waveform;
ALint Phase;
ALfloat Rate;
ALfloat Depth;
ALfloat Feedback;
ALfloat Delay;
} Chorus;
struct {
ALboolean OnOff;
} Compressor;
struct {
ALfloat Edge;
ALfloat Gain;
ALfloat LowpassCutoff;
ALfloat EQCenter;
ALfloat EQBandwidth;
} Distortion;
struct {
ALfloat Delay;
ALfloat LRDelay;
@@ -245,28 +131,65 @@ typedef struct ALeffect
ALfloat Spread;
} Echo;
// Index to itself
ALuint effect;
struct {
ALfloat Delay;
ALfloat LowCutoff;
ALfloat LowGain;
ALfloat Mid1Center;
ALfloat Mid1Gain;
ALfloat Mid1Width;
ALfloat Mid2Center;
ALfloat Mid2Gain;
ALfloat Mid2Width;
ALfloat HighCutoff;
ALfloat HighGain;
} Equalizer;
struct ALeffect *next;
struct {
ALint Waveform;
ALint Phase;
ALfloat Rate;
ALfloat Depth;
ALfloat Feedback;
ALfloat Delay;
} Flanger;
struct {
ALfloat Frequency;
ALfloat HighPassCutoff;
ALint Waveform;
} Modulator;
struct {
ALfloat Gain;
} Dedicated;
} ALeffectProps;
typedef struct ALeffect {
// Effect type (AL_EFFECT_NULL, ...)
ALenum type;
ALeffectProps Props;
const struct ALeffectVtable *vtbl;
/* Self ID */
ALuint id;
} ALeffect;
ALvoid AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects);
ALvoid AL_APIENTRY alDeleteEffects(ALsizei n, ALuint *effects);
ALboolean AL_APIENTRY alIsEffect(ALuint effect);
inline struct ALeffect *LookupEffect(ALCdevice *device, ALuint id)
{ return (struct ALeffect*)LookupUIntMapKey(&device->EffectMap, id); }
inline struct ALeffect *RemoveEffect(ALCdevice *device, ALuint id)
{ return (struct ALeffect*)RemoveUIntMapKey(&device->EffectMap, id); }
ALvoid AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint iValue);
ALvoid AL_APIENTRY alEffectiv(ALuint effect, ALenum param, ALint *piValues);
ALvoid AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat flValue);
ALvoid AL_APIENTRY alEffectfv(ALuint effect, ALenum param, ALfloat *pflValues);
ALvoid AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *piValue);
ALvoid AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *piValues);
ALvoid AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *pflValue);
ALvoid AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *pflValues);
inline ALboolean IsReverbEffect(ALenum type)
{ return type == AL_EFFECT_REVERB || type == AL_EFFECT_EAXREVERB; }
ALenum InitEffect(ALeffect *effect);
ALvoid ReleaseALEffects(ALCdevice *device);
ALvoid LoadReverbPreset(const char *name, ALeffect *effect);
#ifdef __cplusplus
}
#endif
+19 -2
View File
@@ -1,13 +1,30 @@
#ifndef _AL_ERROR_H_
#define _AL_ERROR_H_
#include "AL/al.h"
#include "alMain.h"
#ifdef __cplusplus
extern "C" {
#endif
ALvoid alSetError(ALenum errorCode);
extern ALboolean TrapALError;
ALvoid alSetError(ALCcontext *Context, ALenum errorCode);
#define SET_ERROR_AND_RETURN(ctx, err) do { \
alSetError((ctx), (err)); \
return; \
} while(0)
#define SET_ERROR_AND_RETURN_VALUE(ctx, err, val) do { \
alSetError((ctx), (err)); \
return (val); \
} while(0)
#define SET_ERROR_AND_GOTO(ctx, err, lbl) do { \
alSetError((ctx), (err)); \
goto lbl; \
} while(0)
#ifdef __cplusplus
}
-33
View File
@@ -1,33 +0,0 @@
#ifndef _AL_EXTENSION_H_
#define _AL_EXTENSION_H_
#include "AL/al.h"
#include "AL/alc.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct ALextension_struct
{
ALchar *extName;
ALvoid *address;
} ALextension;
typedef struct ALfunction_struct
{
ALchar *funcName;
ALvoid *address;
} ALfunction;
typedef struct ALenum_struct
{
ALchar *enumName;
ALenum value;
} ALenums;
#ifdef __cplusplus
}
#endif
#endif
+77 -63
View File
@@ -1,93 +1,107 @@
#ifndef _AL_FILTER_H_
#define _AL_FILTER_H_
#include "AL/al.h"
#include "alu.h"
#include "alMain.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
ALfloat coeff;
#ifndef _MSC_VER
ALfloat history[0];
#else
ALfloat history[1];
#endif
} FILTER;
#define LOWPASSFREQREF (5000.0f)
#define HIGHPASSFREQREF (250.0f)
static __inline ALfloat lpFilter4P(FILTER *iir, ALuint offset, ALfloat input)
/* Filters implementation is based on the "Cookbook formulae for audio *
* EQ biquad filter coefficients" by Robert Bristow-Johnson *
* http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt */
typedef enum ALfilterType {
/** EFX-style low-pass filter, specifying a gain and reference frequency. */
ALfilterType_HighShelf,
/** EFX-style high-pass filter, specifying a gain and reference frequency. */
ALfilterType_LowShelf,
/** Peaking filter, specifying a gain, reference frequency, and bandwidth. */
ALfilterType_Peaking,
/** Low-pass cut-off filter, specifying a cut-off frequency and bandwidth. */
ALfilterType_LowPass,
/** High-pass cut-off filter, specifying a cut-off frequency and bandwidth. */
ALfilterType_HighPass,
/** Band-pass filter, specifying a center frequency and bandwidth. */
ALfilterType_BandPass,
} ALfilterType;
typedef struct ALfilterState {
ALfloat x[2]; /* History of two last input samples */
ALfloat y[2]; /* History of two last output samples */
ALfloat a[3]; /* Transfer function coefficients "a" */
ALfloat b[3]; /* Transfer function coefficients "b" */
void (*process)(struct ALfilterState *self, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples);
} ALfilterState;
#define ALfilterState_process(a, ...) ((a)->process((a), __VA_ARGS__))
void ALfilterState_clear(ALfilterState *filter);
void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat gain, ALfloat freq_mult, ALfloat bandwidth);
inline ALfloat ALfilterState_processSingle(ALfilterState *filter, ALfloat sample)
{
ALfloat *history = &iir->history[offset];
ALfloat a = iir->coeff;
ALfloat output = input;
ALfloat outsmp;
output = output + (history[0]-output)*a;
history[0] = output;
output = output + (history[1]-output)*a;
history[1] = output;
output = output + (history[2]-output)*a;
history[2] = output;
output = output + (history[3]-output)*a;
history[3] = output;
outsmp = filter->b[0] * sample +
filter->b[1] * filter->x[0] +
filter->b[2] * filter->x[1] -
filter->a[1] * filter->y[0] -
filter->a[2] * filter->y[1];
filter->x[1] = filter->x[0];
filter->x[0] = sample;
filter->y[1] = filter->y[0];
filter->y[0] = outsmp;
return output;
return outsmp;
}
static __inline ALfloat lpFilter2P(FILTER *iir, ALuint offset, ALfloat input)
{
ALfloat *history = &iir->history[offset];
ALfloat a = iir->coeff;
ALfloat output = input;
output = output + (history[0]-output)*a;
history[0] = output;
output = output + (history[1]-output)*a;
history[1] = output;
return output;
}
void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples);
#define AL_FILTER_TYPE 0x8001
#define AL_FILTER_NULL 0x0000
#define AL_FILTER_LOWPASS 0x0001
#define AL_FILTER_HIGHPASS 0x0002
#define AL_FILTER_BANDPASS 0x0003
#define AL_LOWPASS_GAIN 0x0001
#define AL_LOWPASS_GAINHF 0x0002
typedef struct ALfilter
{
typedef struct ALfilter {
// Filter type (AL_FILTER_NULL, ...)
ALenum type;
ALfloat Gain;
ALfloat GainHF;
ALfloat HFReference;
ALfloat GainLF;
ALfloat LFReference;
// Index to itself
ALuint filter;
void (*SetParami)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint val);
void (*SetParamiv)(struct ALfilter *filter, ALCcontext *context, ALenum param, const ALint *vals);
void (*SetParamf)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val);
void (*SetParamfv)(struct ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals);
struct ALfilter *next;
void (*GetParami)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint *val);
void (*GetParamiv)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint *vals);
void (*GetParamf)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val);
void (*GetParamfv)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals);
/* Self ID */
ALuint id;
} ALfilter;
ALvoid AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters);
ALvoid AL_APIENTRY alDeleteFilters(ALsizei n, ALuint *filters);
ALboolean AL_APIENTRY alIsFilter(ALuint filter);
#define ALfilter_SetParami(x, c, p, v) ((x)->SetParami((x),(c),(p),(v)))
#define ALfilter_SetParamiv(x, c, p, v) ((x)->SetParamiv((x),(c),(p),(v)))
#define ALfilter_SetParamf(x, c, p, v) ((x)->SetParamf((x),(c),(p),(v)))
#define ALfilter_SetParamfv(x, c, p, v) ((x)->SetParamfv((x),(c),(p),(v)))
ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint iValue);
ALvoid AL_APIENTRY alFilteriv(ALuint filter, ALenum param, ALint *piValues);
ALvoid AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat flValue);
ALvoid AL_APIENTRY alFilterfv(ALuint filter, ALenum param, ALfloat *pflValues);
#define ALfilter_GetParami(x, c, p, v) ((x)->GetParami((x),(c),(p),(v)))
#define ALfilter_GetParamiv(x, c, p, v) ((x)->GetParamiv((x),(c),(p),(v)))
#define ALfilter_GetParamf(x, c, p, v) ((x)->GetParamf((x),(c),(p),(v)))
#define ALfilter_GetParamfv(x, c, p, v) ((x)->GetParamfv((x),(c),(p),(v)))
ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *piValue);
ALvoid AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *piValues);
ALvoid AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *pflValue);
ALvoid AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *pflValues);
inline struct ALfilter *LookupFilter(ALCdevice *device, ALuint id)
{ return (struct ALfilter*)LookupUIntMapKey(&device->FilterMap, id); }
inline struct ALfilter *RemoveFilter(ALCdevice *device, ALuint id)
{ return (struct ALfilter*)RemoveUIntMapKey(&device->FilterMap, id); }
ALvoid ReleaseALFilters(ALCdevice *device);
+12 -10
View File
@@ -1,22 +1,24 @@
#ifndef _AL_LISTENER_H_
#define _AL_LISTENER_H_
#include "AL/al.h"
#include "alMain.h"
#ifdef __cplusplus
extern "C" {
#endif
#define AL_METERS_PER_UNIT 0x20004
typedef struct ALlistener {
volatile ALfloat Position[3];
volatile ALfloat Velocity[3];
volatile ALfloat Forward[3];
volatile ALfloat Up[3];
volatile ALfloat Gain;
volatile ALfloat MetersPerUnit;
typedef struct ALlistener_struct
{
ALfloat Position[3];
ALfloat Velocity[3];
ALfloat Forward[3];
ALfloat Up[3];
ALfloat Gain;
ALfloat MetersPerUnit;
struct {
ALfloat Matrix[4][4];
ALfloat Velocity[3];
} Params;
} ALlistener;
#ifdef __cplusplus
+772 -228
View File
File diff suppressed because it is too large Load Diff
+172
View File
@@ -0,0 +1,172 @@
#ifndef ALMIDI_H
#define ALMIDI_H
#include "alMain.h"
#include "atomic.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct ALsfmodulator {
struct {
ALenum Input;
ALenum Type;
ALenum Form;
} Source[2];
ALint Amount;
ALenum TransformOp;
ALenum Dest;
} ALsfmodulator;
typedef struct ALenvelope {
ALint DelayTime;
ALint AttackTime;
ALint HoldTime;
ALint DecayTime;
ALint SustainAttn;
ALint ReleaseTime;
ALint KeyToHoldTime;
ALint KeyToDecayTime;
} ALenvelope;
typedef struct ALfontsound {
RefCount ref;
struct ALbuffer *Buffer;
ALint MinKey, MaxKey;
ALint MinVelocity, MaxVelocity;
ALint ModLfoToPitch;
ALint VibratoLfoToPitch;
ALint ModEnvToPitch;
ALint FilterCutoff;
ALint FilterQ;
ALint ModLfoToFilterCutoff;
ALint ModEnvToFilterCutoff;
ALint ModLfoToVolume;
ALint ChorusSend;
ALint ReverbSend;
ALint Pan;
struct {
ALint Delay;
ALint Frequency;
} ModLfo;
struct {
ALint Delay;
ALint Frequency;
} VibratoLfo;
ALenvelope ModEnv;
ALenvelope VolEnv;
ALint Attenuation;
ALint CoarseTuning;
ALint FineTuning;
ALenum LoopMode;
ALint TuningScale;
ALint ExclusiveClass;
ALuint Start;
ALuint End;
ALuint LoopStart;
ALuint LoopEnd;
ALuint SampleRate;
ALubyte PitchKey;
ALbyte PitchCorrection;
ALenum SampleType;
struct ALfontsound *Link;
/* NOTE: Each map entry contains *four* (4) ALsfmodulator objects. */
UIntMap ModulatorMap;
ALuint id;
} ALfontsound;
void ALfontsound_setPropi(ALfontsound *self, ALCcontext *context, ALenum param, ALint value);
void ALfontsound_setModStagei(ALfontsound *self, ALCcontext *context, ALsizei stage, ALenum param, ALint value);
ALfontsound *NewFontsound(ALCcontext *context);
void DeleteFontsound(ALCdevice *device, ALfontsound *sound);
inline struct ALfontsound *LookupFontsound(ALCdevice *device, ALuint id)
{ return (struct ALfontsound*)LookupUIntMapKey(&device->FontsoundMap, id); }
inline struct ALfontsound *RemoveFontsound(ALCdevice *device, ALuint id)
{ return (struct ALfontsound*)RemoveUIntMapKey(&device->FontsoundMap, id); }
void ReleaseALFontsounds(ALCdevice *device);
typedef struct ALsfpreset {
RefCount ref;
ALint Preset; /* a.k.a. MIDI program number */
ALint Bank; /* MIDI bank 0...127, or percussion (bank 128) */
ALfontsound **Sounds;
ALsizei NumSounds;
ALuint id;
} ALsfpreset;
ALsfpreset *NewPreset(ALCcontext *context);
void DeletePreset(ALCdevice *device, ALsfpreset *preset);
inline struct ALsfpreset *LookupPreset(ALCdevice *device, ALuint id)
{ return (struct ALsfpreset*)LookupUIntMapKey(&device->PresetMap, id); }
inline struct ALsfpreset *RemovePreset(ALCdevice *device, ALuint id)
{ return (struct ALsfpreset*)RemoveUIntMapKey(&device->PresetMap, id); }
void ReleaseALPresets(ALCdevice *device);
typedef struct ALsoundfont {
RefCount ref;
ALsfpreset **Presets;
ALsizei NumPresets;
RWLock Lock;
ALuint id;
} ALsoundfont;
ALsoundfont *ALsoundfont_getDefSoundfont(ALCcontext *context);
void ALsoundfont_deleteSoundfont(ALsoundfont *self, ALCdevice *device);
inline struct ALsoundfont *LookupSfont(ALCdevice *device, ALuint id)
{ return (struct ALsoundfont*)LookupUIntMapKey(&device->SfontMap, id); }
inline struct ALsoundfont *RemoveSfont(ALCdevice *device, ALuint id)
{ return (struct ALsoundfont*)RemoveUIntMapKey(&device->SfontMap, id); }
void ReleaseALSoundfonts(ALCdevice *device);
inline ALboolean IsValidCtrlInput(int cc)
{
/* These correspond to MIDI functions, not real controller values. */
if(cc == 0 || cc == 6 || cc == 32 || cc == 38 || (cc >= 98 && cc <= 101) || cc >= 120)
return AL_FALSE;
/* These are the LSB components of CC0...CC31, which are automatically used when
* reading the MSB controller value. */
if(cc >= 32 && cc <= 63)
return AL_FALSE;
/* All the rest are okay! */
return AL_TRUE;
}
#ifdef __cplusplus
}
#endif
#endif /* ALMIDI_H */
+116 -83
View File
@@ -1,110 +1,143 @@
#ifndef _AL_SOURCE_H_
#define _AL_SOURCE_H_
#define AL_NUM_SOURCE_PARAMS 128
#define MAX_SENDS 4
#define MAX_SENDS 2
#include "alFilter.h"
#include "alMain.h"
#include "alu.h"
#include "AL/al.h"
#define AL_DIRECT_FILTER 0x20005
#define AL_AUXILIARY_SEND_FILTER 0x20006
#define AL_AIR_ABSORPTION_FACTOR 0x20007
#define AL_ROOM_ROLLOFF_FACTOR 0x20008
#define AL_CONE_OUTER_GAINHF 0x20009
#define AL_DIRECT_FILTER_GAINHF_AUTO 0x2000A
#define AL_AUXILIARY_SEND_FILTER_GAIN_AUTO 0x2000B
#define AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO 0x2000C
#include "hrtf.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct ALbufferlistitem
{
struct ALbuffer *buffer;
struct ALbufferlistitem *next;
extern enum Resampler DefaultResampler;
extern const ALsizei ResamplerPadding[ResamplerMax];
extern const ALsizei ResamplerPrePadding[ResamplerMax];
typedef struct ALbufferlistitem {
struct ALbuffer *buffer;
struct ALbufferlistitem *volatile next;
struct ALbufferlistitem *volatile prev;
} ALbufferlistitem;
typedef struct ALsource
{
ALfloat flPitch;
ALfloat flGain;
ALfloat flOuterGain;
ALfloat flMinGain;
ALfloat flMaxGain;
ALfloat flInnerAngle;
ALfloat flOuterAngle;
ALfloat flRefDistance;
ALfloat flMaxDistance;
ALfloat flRollOffFactor;
ALfloat vPosition[3];
ALfloat vVelocity[3];
ALfloat vOrientation[3];
ALboolean bHeadRelative;
ALboolean bLooping;
ALenum DistanceModel;
ALenum state;
ALuint position;
ALuint position_fraction;
typedef struct ALactivesource {
struct ALsource *Source;
struct ALbuffer *Buffer;
/** Method to update mixing parameters. */
ALvoid (*Update)(struct ALactivesource *self, const ALCcontext *context);
struct ALbufferlistitem *queue; // Linked list of buffers in queue
ALuint BuffersInQueue; // Number of buffers in queue
ALuint BuffersPlayed; // Number of buffers played on this loop
/** Current target parameters used for mixing. */
ALint Step;
ALfilter DirectFilter;
ALboolean IsHrtf;
ALuint Offset; /* Number of output samples mixed since starting. */
DirectParams Direct;
SendParams Send[MAX_SENDS];
} ALactivesource;
typedef struct ALsource {
/** Source properties. */
volatile ALfloat Pitch;
volatile ALfloat Gain;
volatile ALfloat OuterGain;
volatile ALfloat MinGain;
volatile ALfloat MaxGain;
volatile ALfloat InnerAngle;
volatile ALfloat OuterAngle;
volatile ALfloat RefDistance;
volatile ALfloat MaxDistance;
volatile ALfloat RollOffFactor;
volatile ALfloat Position[3];
volatile ALfloat Velocity[3];
volatile ALfloat Orientation[3];
volatile ALboolean HeadRelative;
volatile ALboolean Looping;
volatile enum DistanceModel DistanceModel;
volatile ALboolean DirectChannels;
volatile ALboolean DryGainHFAuto;
volatile ALboolean WetGainAuto;
volatile ALboolean WetGainHFAuto;
volatile ALfloat OuterGainHF;
volatile ALfloat AirAbsorptionFactor;
volatile ALfloat RoomRolloffFactor;
volatile ALfloat DopplerFactor;
volatile ALfloat Radius;
enum Resampler Resampler;
/**
* Last user-specified offset, and the offset type (bytes, samples, or
* seconds).
*/
ALdouble Offset;
ALenum OffsetType;
/** Source type (static, streaming, or undetermined) */
volatile ALint SourceType;
/** Source state (initial, playing, paused, or stopped) */
volatile ALenum state;
ALenum new_state;
/**
* Source offset in samples, relative to the currently playing buffer, NOT
* the whole queue, and the fractional (fixed-point) offset to the next
* sample.
*/
ALuint position;
ALuint position_fraction;
/** Source Buffer Queue info. */
ATOMIC(ALbufferlistitem*) queue;
ATOMIC(ALbufferlistitem*) current_buffer;
RWLock queue_lock;
/** Current buffer sample info. */
ALuint NumChannels;
ALuint SampleSize;
/** Direct filter and auxiliary send info. */
struct {
ALfloat Gain;
ALfloat GainHF;
ALfloat HFReference;
ALfloat GainLF;
ALfloat LFReference;
} Direct;
struct {
struct ALeffectslot *Slot;
ALfilter WetFilter;
ALfloat Gain;
ALfloat GainHF;
ALfloat HFReference;
ALfloat GainLF;
ALfloat LFReference;
} Send[MAX_SENDS];
ALboolean DryGainHFAuto;
ALboolean WetGainAuto;
ALboolean WetGainHFAuto;
ALfloat OuterGainHF;
/** Source needs to update its mixing parameters. */
ATOMIC(ALenum) NeedsUpdate;
ALfloat AirAbsorptionFactor;
ALfloat RoomRolloffFactor;
ALfloat DopplerFactor;
ALint lOffset;
ALint lOffsetType;
// Source Type (Static, Streaming, or Undetermined)
ALint lSourceType;
// Current gains, which are ramped while mixed
ALfloat DryGains[OUTPUTCHANNELS];
ALfloat WetGains[MAX_SENDS];
ALboolean FirstStart;
// Current target parameters used for mixing
struct {
ALfloat DryGains[OUTPUTCHANNELS];
ALfloat WetGains[MAX_SENDS];
ALfloat Pitch;
struct {
FILTER iirFilter;
ALfloat history[2];
} Send[MAX_SENDS];
FILTER iirFilter;
ALfloat history[OUTPUTCHANNELS*2];
} Params;
// Index to itself
ALuint source;
struct ALsource *next;
/** Self ID */
ALuint id;
} ALsource;
inline struct ALsource *LookupSource(ALCcontext *context, ALuint id)
{ return (struct ALsource*)LookupUIntMapKey(&context->SourceMap, id); }
inline struct ALsource *RemoveSource(ALCcontext *context, ALuint id)
{ return (struct ALsource*)RemoveUIntMapKey(&context->SourceMap, id); }
ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state);
ALboolean ApplyOffset(ALsource *Source);
ALvoid ReleaseALSources(ALCcontext *Context);
#ifdef __cplusplus
-14
View File
@@ -1,14 +0,0 @@
#ifndef _AL_STATE_H_
#define _AL_STATE_H_
#include "AL/al.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif
+8 -30
View File
@@ -1,42 +1,20 @@
#ifndef _AL_THUNK_H_
#define _AL_THUNK_H_
#ifndef ALTHUNK_H
#define ALTHUNK_H
#include "config.h"
#include "AL/al.h"
#include "AL/alc.h"
#include "alMain.h"
#ifdef __cplusplus
extern "C" {
#endif
void alThunkInit(void);
void alThunkExit(void);
ALuint alThunkAddEntry(ALvoid * ptr);
void alThunkRemoveEntry(ALuint index);
ALvoid *alThunkLookupEntry(ALuint index);
#if (SIZEOF_VOIDP > SIZEOF_UINT)
#define ALTHUNK_INIT() alThunkInit()
#define ALTHUNK_EXIT() alThunkExit()
#define ALTHUNK_ADDENTRY(p) alThunkAddEntry(p)
#define ALTHUNK_REMOVEENTRY(i) alThunkRemoveEntry(i)
#define ALTHUNK_LOOKUPENTRY(i) alThunkLookupEntry(i)
#else
#define ALTHUNK_INIT()
#define ALTHUNK_EXIT()
#define ALTHUNK_ADDENTRY(p) ((ALuint)p)
#define ALTHUNK_REMOVEENTRY(i)
#define ALTHUNK_LOOKUPENTRY(i) ((ALvoid*)(i))
#endif // (SIZEOF_VOIDP > SIZEOF_INT)
void ThunkInit(void);
void ThunkExit(void);
ALenum NewThunkEntry(ALuint *index);
void FreeThunkEntry(ALuint index);
#ifdef __cplusplus
}
#endif
#endif //_AL_THUNK_H_
#endif //ALTHUNK_H
+199 -120
View File
@@ -1,154 +1,233 @@
#ifndef _ALU_H_
#define _ALU_H_
#include "AL/al.h"
#include "AL/alc.h"
#include "AL/alext.h"
#include <limits.h>
#include <math.h>
#ifdef HAVE_FLOAT_H
#include <float.h>
#endif
#ifndef M_PI
#define M_PI 3.14159265358979323846 /* pi */
#define M_PI_2 1.57079632679489661923 /* pi/2 */
#ifdef HAVE_IEEEFP_H
#include <ieeefp.h>
#endif
#ifdef HAVE_SQRTF
#define aluSqrt(x) ((ALfloat)sqrtf((float)(x)))
#else
#define aluSqrt(x) ((ALfloat)sqrt((double)(x)))
#include "alMain.h"
#include "alBuffer.h"
#include "alFilter.h"
#include "hrtf.h"
#include "align.h"
#define F_PI (3.14159265358979323846f)
#define F_PI_2 (1.57079632679489661923f)
#define F_2PI (6.28318530717958647692f)
#ifndef FLT_EPSILON
#define FLT_EPSILON (1.19209290e-07f)
#endif
#ifdef HAVE_ACOSF
#define aluAcos(x) ((ALfloat)acosf((float)(x)))
#else
#define aluAcos(x) ((ALfloat)acos((double)(x)))
#endif
#define DEG2RAD(x) ((ALfloat)(x) * (F_PI/180.0f))
#define RAD2DEG(x) ((ALfloat)(x) * (180.0f/F_PI))
#ifdef HAVE_ATANF
#define aluAtan(x) ((ALfloat)atanf((float)(x)))
#else
#define aluAtan(x) ((ALfloat)atan((double)(x)))
#endif
#ifdef HAVE_FABSF
#define aluFabs(x) ((ALfloat)fabsf((float)(x)))
#else
#define aluFabs(x) ((ALfloat)fabs((double)(x)))
#endif
#define SRC_HISTORY_BITS (6)
#define SRC_HISTORY_LENGTH (1<<SRC_HISTORY_BITS)
#define SRC_HISTORY_MASK (SRC_HISTORY_LENGTH-1)
#define MAX_PITCH (10)
// fixes for mingw32.
#if defined(max) && !defined(__max)
#define __max max
#endif
#if defined(min) && !defined(__min)
#define __min min
#endif
#ifdef __cplusplus
extern "C" {
#endif
enum {
FRONT_LEFT = 0,
FRONT_RIGHT,
FRONT_CENTER,
SIDE_LEFT,
SIDE_RIGHT,
BACK_LEFT,
BACK_RIGHT,
BACK_CENTER,
LFE,
OUTPUTCHANNELS
enum ActiveFilters {
AF_None = 0,
AF_LowPass = 1,
AF_HighPass = 2,
AF_BandPass = AF_LowPass | AF_HighPass
};
#define BUFFERSIZE 24000
extern ALboolean DuplicateStereo;
typedef struct HrtfState {
alignas(16) ALfloat History[SRC_HISTORY_LENGTH];
alignas(16) ALfloat Values[HRIR_LENGTH][2];
} HrtfState;
/* NOTE: The AL_FORMAT_REAR* enums aren't handled here be cause they're
* converted to AL_FORMAT_QUAD* when loaded */
static __inline ALuint aluBytesFromFormat(ALenum format)
typedef struct HrtfParams {
alignas(16) ALfloat Coeffs[HRIR_LENGTH][2];
alignas(16) ALfloat CoeffStep[HRIR_LENGTH][2];
ALuint Delay[2];
ALint DelayStep[2];
} HrtfParams;
typedef struct MixGains {
ALfloat Current;
ALfloat Step;
ALfloat Target;
} MixGains;
typedef struct DirectParams {
ALfloat (*OutBuffer)[BUFFERSIZE];
/* If not 'moving', gain/coefficients are set directly without fading. */
ALboolean Moving;
/* Stepping counter for gain/coefficient fading. */
ALuint Counter;
struct {
enum ActiveFilters ActiveType;
ALfilterState LowPass;
ALfilterState HighPass;
} Filters[MAX_INPUT_CHANNELS];
union {
struct {
HrtfParams Params[MAX_INPUT_CHANNELS];
HrtfState State[MAX_INPUT_CHANNELS];
ALuint IrSize;
ALfloat Gain;
ALfloat Dir[3];
} Hrtf;
MixGains Gains[MAX_INPUT_CHANNELS][MaxChannels];
} Mix;
} DirectParams;
typedef struct SendParams {
ALfloat (*OutBuffer)[BUFFERSIZE];
ALboolean Moving;
ALuint Counter;
struct {
enum ActiveFilters ActiveType;
ALfilterState LowPass;
ALfilterState HighPass;
} Filters[MAX_INPUT_CHANNELS];
/* Gain control, which applies to all input channels to a single (mono)
* output buffer. */
MixGains Gain;
} SendParams;
typedef const ALfloat* (*ResamplerFunc)(const ALfloat *src, ALuint frac, ALuint increment,
ALfloat *restrict dst, ALuint dstlen);
typedef void (*MixerFunc)(const ALfloat *data, ALuint OutChans,
ALfloat (*restrict OutBuffer)[BUFFERSIZE], struct MixGains *Gains,
ALuint Counter, ALuint OutPos, ALuint BufferSize);
typedef void (*HrtfMixerFunc)(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
ALuint Counter, ALuint Offset, ALuint OutPos,
const ALuint IrSize, const HrtfParams *hrtfparams,
HrtfState *hrtfstate, ALuint BufferSize);
#define GAIN_SILENCE_THRESHOLD (0.00001f) /* -100dB */
#define SPEEDOFSOUNDMETRESPERSEC (343.3f)
#define AIRABSORBGAINHF (0.99426f) /* -0.05dB */
#define FRACTIONBITS (14)
#define FRACTIONONE (1<<FRACTIONBITS)
#define FRACTIONMASK (FRACTIONONE-1)
inline ALfloat minf(ALfloat a, ALfloat b)
{ return ((a > b) ? b : a); }
inline ALfloat maxf(ALfloat a, ALfloat b)
{ return ((a > b) ? a : b); }
inline ALfloat clampf(ALfloat val, ALfloat min, ALfloat max)
{ return minf(max, maxf(min, val)); }
inline ALdouble mind(ALdouble a, ALdouble b)
{ return ((a > b) ? b : a); }
inline ALdouble maxd(ALdouble a, ALdouble b)
{ return ((a > b) ? a : b); }
inline ALdouble clampd(ALdouble val, ALdouble min, ALdouble max)
{ return mind(max, maxd(min, val)); }
inline ALuint minu(ALuint a, ALuint b)
{ return ((a > b) ? b : a); }
inline ALuint maxu(ALuint a, ALuint b)
{ return ((a > b) ? a : b); }
inline ALuint clampu(ALuint val, ALuint min, ALuint max)
{ return minu(max, maxu(min, val)); }
inline ALint mini(ALint a, ALint b)
{ return ((a > b) ? b : a); }
inline ALint maxi(ALint a, ALint b)
{ return ((a > b) ? a : b); }
inline ALint clampi(ALint val, ALint min, ALint max)
{ return mini(max, maxi(min, val)); }
inline ALint64 mini64(ALint64 a, ALint64 b)
{ return ((a > b) ? b : a); }
inline ALint64 maxi64(ALint64 a, ALint64 b)
{ return ((a > b) ? a : b); }
inline ALint64 clampi64(ALint64 val, ALint64 min, ALint64 max)
{ return mini64(max, maxi64(min, val)); }
inline ALuint64 minu64(ALuint64 a, ALuint64 b)
{ return ((a > b) ? b : a); }
inline ALuint64 maxu64(ALuint64 a, ALuint64 b)
{ return ((a > b) ? a : b); }
inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max)
{ return minu64(max, maxu64(min, val)); }
inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu)
{
switch(format)
{
case AL_FORMAT_MONO8:
case AL_FORMAT_STEREO8:
case AL_FORMAT_QUAD8_LOKI:
case AL_FORMAT_QUAD8:
case AL_FORMAT_51CHN8:
case AL_FORMAT_61CHN8:
case AL_FORMAT_71CHN8:
return 1;
case AL_FORMAT_MONO16:
case AL_FORMAT_STEREO16:
case AL_FORMAT_QUAD16_LOKI:
case AL_FORMAT_QUAD16:
case AL_FORMAT_51CHN16:
case AL_FORMAT_61CHN16:
case AL_FORMAT_71CHN16:
return 2;
case AL_FORMAT_MONO_FLOAT32:
case AL_FORMAT_STEREO_FLOAT32:
case AL_FORMAT_QUAD32:
case AL_FORMAT_51CHN32:
case AL_FORMAT_61CHN32:
case AL_FORMAT_71CHN32:
return 4;
default:
return 0;
}
return val1 + (val2-val1)*mu;
}
static __inline ALuint aluChannelsFromFormat(ALenum format)
inline ALfloat cubic(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALfloat mu)
{
switch(format)
{
case AL_FORMAT_MONO8:
case AL_FORMAT_MONO16:
case AL_FORMAT_MONO_FLOAT32:
return 1;
ALfloat mu2 = mu*mu;
ALfloat a0 = -0.5f*val0 + 1.5f*val1 + -1.5f*val2 + 0.5f*val3;
ALfloat a1 = val0 + -2.5f*val1 + 2.0f*val2 + -0.5f*val3;
ALfloat a2 = -0.5f*val0 + 0.5f*val2;
ALfloat a3 = val1;
case AL_FORMAT_STEREO8:
case AL_FORMAT_STEREO16:
case AL_FORMAT_STEREO_FLOAT32:
return 2;
case AL_FORMAT_QUAD8_LOKI:
case AL_FORMAT_QUAD16_LOKI:
case AL_FORMAT_QUAD8:
case AL_FORMAT_QUAD16:
case AL_FORMAT_QUAD32:
return 4;
case AL_FORMAT_51CHN8:
case AL_FORMAT_51CHN16:
case AL_FORMAT_51CHN32:
return 6;
case AL_FORMAT_61CHN8:
case AL_FORMAT_61CHN16:
case AL_FORMAT_61CHN32:
return 7;
case AL_FORMAT_71CHN8:
case AL_FORMAT_71CHN16:
case AL_FORMAT_71CHN32:
return 8;
default:
return 0;
}
return a0*mu*mu2 + a1*mu2 + a2*mu + a3;
}
ALvoid aluInitPanning(ALCcontext *Context);
ALvoid aluInitPanning(ALCdevice *Device);
/**
* ComputeAngleGains
*
* Sets channel gains based on a given source's angle and its half-width. The
* angle and hwidth parameters are in radians.
*/
void ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat hwidth, ALfloat ingain, ALfloat gains[MaxChannels]);
/**
* SetGains
*
* Helper to set the appropriate channels to the specified gain.
*/
inline void SetGains(const ALCdevice *device, ALfloat ingain, ALfloat gains[MaxChannels])
{
ComputeAngleGains(device, 0.0f, F_PI, ingain, gains);
}
ALvoid CalcSourceParams(struct ALactivesource *src, const ALCcontext *ALContext);
ALvoid CalcNonAttnSourceParams(struct ALactivesource *src, const ALCcontext *ALContext);
ALvoid MixSource(struct ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo);
ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size);
/* Caller must lock the device. */
ALvoid aluHandleDisconnect(ALCdevice *device);
extern ALfloat ConeScale;
extern ALfloat ZScale;
#ifdef __cplusplus
}
#endif
+128 -109
View File
@@ -1,109 +1,128 @@
/*-
* Copyright (c) 2005 Boris Mikhaylov
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef BS2B_H
#define BS2B_H
/* Number of crossfeed levels */
#define BS2B_CLEVELS 3
/* Normal crossfeed levels */
#define BS2B_HIGH_CLEVEL 3
#define BS2B_MIDDLE_CLEVEL 2
#define BS2B_LOW_CLEVEL 1
/* Easy crossfeed levels */
#define BS2B_HIGH_ECLEVEL BS2B_HIGH_CLEVEL + BS2B_CLEVELS
#define BS2B_MIDDLE_ECLEVEL BS2B_MIDDLE_CLEVEL + BS2B_CLEVELS
#define BS2B_LOW_ECLEVEL BS2B_LOW_CLEVEL + BS2B_CLEVELS
/* Default crossfeed levels */
#define BS2B_DEFAULT_CLEVEL BS2B_HIGH_ECLEVEL
/* Default sample rate (Hz) */
#define BS2B_DEFAULT_SRATE 44100
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
struct bs2b {
int level; /* Crossfeed level */
int srate; /* Sample rate (Hz) */
/* Lowpass IIR filter coefficients */
double a0_lo;
double b1_lo;
/* Highboost IIR filter coefficients */
double a0_hi;
double a1_hi;
double b1_hi;
/* Global gain against overloading */
double gain;
/* Buffer of last filtered sample.
* [0] - first channel, [1] - second channel
*/
struct t_last_sample {
double asis[2];
double lo[2];
double hi[2];
} last_sample;
};
/* Clear buffers and set new coefficients with new crossfeed level value.
* level - crossfeed level of *LEVEL values.
*/
void bs2b_set_level(struct bs2b *bs2b, int level);
/* Return current crossfeed level value */
int bs2b_get_level(struct bs2b *bs2b);
/* Clear buffers and set new coefficients with new sample rate value.
* srate - sample rate by Hz.
*/
void bs2b_set_srate(struct bs2b *bs2b, int srate);
/* Return current sample rate value */
int bs2b_get_srate(struct bs2b *bs2b);
/* Clear buffer */
void bs2b_clear(struct bs2b *bs2b);
/* Return 1 if buffer is clear */
int bs2b_is_clear(struct bs2b *bs2b);
/* Crossfeeds one stereo sample that are pointed by sample.
* [0] - first channel, [1] - second channel.
* Returns crossfided samle by sample pointer.
*/
/* sample poits to floats */
void bs2b_cross_feed(struct bs2b *bs2b, float *sample);
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
#endif /* BS2B_H */
/*-
* Copyright (c) 2005 Boris Mikhaylov
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef BS2B_H
#define BS2B_H
/* Number of crossfeed levels */
#define BS2B_CLEVELS 3
/* Normal crossfeed levels */
#define BS2B_HIGH_CLEVEL 3
#define BS2B_MIDDLE_CLEVEL 2
#define BS2B_LOW_CLEVEL 1
/* Easy crossfeed levels */
#define BS2B_HIGH_ECLEVEL BS2B_HIGH_CLEVEL + BS2B_CLEVELS
#define BS2B_MIDDLE_ECLEVEL BS2B_MIDDLE_CLEVEL + BS2B_CLEVELS
#define BS2B_LOW_ECLEVEL BS2B_LOW_CLEVEL + BS2B_CLEVELS
/* Default crossfeed levels */
#define BS2B_DEFAULT_CLEVEL BS2B_HIGH_ECLEVEL
/* Default sample rate (Hz) */
#define BS2B_DEFAULT_SRATE 44100
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
struct bs2b {
int level; /* Crossfeed level */
int srate; /* Sample rate (Hz) */
/* Lowpass IIR filter coefficients */
float a0_lo;
float b1_lo;
/* Highboost IIR filter coefficients */
float a0_hi;
float a1_hi;
float b1_hi;
/* Buffer of last filtered sample.
* [0] - first channel, [1] - second channel
*/
struct t_last_sample {
float asis[2];
float lo[2];
float hi[2];
} last_sample;
};
/* Clear buffers and set new coefficients with new crossfeed level value.
* level - crossfeed level of *LEVEL values.
*/
void bs2b_set_level(struct bs2b *bs2b, int level);
/* Return current crossfeed level value */
int bs2b_get_level(struct bs2b *bs2b);
/* Clear buffers and set new coefficients with new sample rate value.
* srate - sample rate by Hz.
*/
void bs2b_set_srate(struct bs2b *bs2b, int srate);
/* Return current sample rate value */
int bs2b_get_srate(struct bs2b *bs2b);
/* Clear buffer */
void bs2b_clear(struct bs2b *bs2b);
/* Crossfeeds one stereo sample that are pointed by sample.
* [0] - first channel, [1] - second channel.
* Returns crossfided sample by sample pointer.
*/
inline void bs2b_cross_feed(struct bs2b *bs2b, float *restrict sample)
{
/* Single pole IIR filter.
* O[n] = a0*I[n] + a1*I[n-1] + b1*O[n-1]
*/
/* Lowpass filter */
#define lo_filter(in, out_1) (bs2b->a0_lo*(in) + bs2b->b1_lo*(out_1))
/* Highboost filter */
#define hi_filter(in, in_1, out_1) (bs2b->a0_hi*(in) + bs2b->a1_hi*(in_1) + bs2b->b1_hi*(out_1))
/* Lowpass filter */
bs2b->last_sample.lo[0] = lo_filter(sample[0], bs2b->last_sample.lo[0]);
bs2b->last_sample.lo[1] = lo_filter(sample[1], bs2b->last_sample.lo[1]);
/* Highboost filter */
bs2b->last_sample.hi[0] = hi_filter(sample[0], bs2b->last_sample.asis[0], bs2b->last_sample.hi[0]);
bs2b->last_sample.hi[1] = hi_filter(sample[1], bs2b->last_sample.asis[1], bs2b->last_sample.hi[1]);
bs2b->last_sample.asis[0] = sample[0];
bs2b->last_sample.asis[1] = sample[1];
/* Crossfeed */
sample[0] = bs2b->last_sample.hi[0] + bs2b->last_sample.lo[1];
sample[1] = bs2b->last_sample.hi[1] + bs2b->last_sample.lo[0];
#undef hi_filter
#undef lo_filter
} /* bs2b_cross_feed */
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
#endif /* BS2B_H */
+9
View File
@@ -0,0 +1,9 @@
#ifndef SAMPLE_CVT_H
#define SAMPLE_CVT_H
#include "AL/al.h"
#include "alBuffer.h"
void ConvertData(ALvoid *dst, enum UserFmtType dstType, const ALvoid *src, enum UserFmtType srcType, ALsizei numchans, ALsizei len, ALsizei align);
#endif /* SAMPLE_CVT_H */
+485 -445
View File
@@ -29,482 +29,522 @@
#include "alAuxEffectSlot.h"
#include "alThunk.h"
#include "alError.h"
#include "alSource.h"
static ALvoid InitializeEffect(ALCcontext *Context, ALeffectslot *ALEffectSlot, ALeffect *effect);
extern inline struct ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id);
extern inline struct ALeffectslot *RemoveEffectSlot(ALCcontext *context, ALuint id);
static ALenum AddEffectSlotArray(ALCcontext *Context, ALeffectslot **start, ALsizei count);
static void RemoveEffectSlotArray(ALCcontext *Context, const ALeffectslot *slot);
ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots)
static UIntMap EffectStateFactoryMap;
static inline ALeffectStateFactory *getFactoryByType(ALenum type)
{
ALCcontext *Context;
ALsizei i, j;
Context = GetContextSuspended();
if(!Context) return;
if (n > 0)
{
ALCdevice *Device = Context->Device;
if(Context->AuxiliaryEffectSlotCount+n <= Device->AuxiliaryEffectSlotMax)
{
// Check that enough memory has been allocted in the 'effectslots' array for n Effect Slots
if (!IsBadWritePtr((void*)effectslots, n * sizeof(ALuint)))
{
ALeffectslot **list = &Context->AuxiliaryEffectSlot;
while(*list)
list = &(*list)->next;
i = 0;
while(i < n)
{
*list = calloc(1, sizeof(ALeffectslot));
if(!(*list) || !((*list)->EffectState=NoneCreate()))
{
// We must have run out or memory
free(*list); *list = NULL;
alDeleteAuxiliaryEffectSlots(i, effectslots);
alSetError(AL_OUT_OF_MEMORY);
break;
}
(*list)->Gain = 1.0;
(*list)->AuxSendAuto = AL_TRUE;
for(j = 0;j < BUFFERSIZE;j++)
(*list)->WetBuffer[j] = 0.0f;
(*list)->refcount = 0;
effectslots[i] = (ALuint)ALTHUNK_ADDENTRY(*list);
(*list)->effectslot = effectslots[i];
Context->AuxiliaryEffectSlotCount++;
i++;
list = &(*list)->next;
}
}
}
else
alSetError(AL_INVALID_OPERATION);
}
ProcessContext(Context);
ALeffectStateFactory* (*getFactory)(void) = LookupUIntMapKey(&EffectStateFactoryMap, type);
if(getFactory != NULL)
return getFactory();
return NULL;
}
ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots)
AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots)
{
ALCcontext *Context;
ALeffectslot *ALAuxiliaryEffectSlot;
ALCcontext *context;
VECTOR(ALeffectslot*) slotvec;
ALsizei cur;
ALenum err;
context = GetContextRef();
if(!context) return;
VECTOR_INIT(slotvec);
if(!(n >= 0))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
if(!VECTOR_RESERVE(slotvec, n))
SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done);
for(cur = 0;cur < n;cur++)
{
ALeffectslot *slot = al_calloc(16, sizeof(ALeffectslot));
err = AL_OUT_OF_MEMORY;
if(!slot || (err=InitEffectSlot(slot)) != AL_NO_ERROR)
{
al_free(slot);
alDeleteAuxiliaryEffectSlots(cur, effectslots);
SET_ERROR_AND_GOTO(context, err, done);
}
err = NewThunkEntry(&slot->id);
if(err == AL_NO_ERROR)
err = InsertUIntMapEntry(&context->EffectSlotMap, slot->id, slot);
if(err != AL_NO_ERROR)
{
FreeThunkEntry(slot->id);
DELETE_OBJ(slot->EffectState);
al_free(slot);
alDeleteAuxiliaryEffectSlots(cur, effectslots);
SET_ERROR_AND_GOTO(context, err, done);
}
VECTOR_PUSH_BACK(slotvec, slot);
effectslots[cur] = slot->id;
}
err = AddEffectSlotArray(context, VECTOR_ITER_BEGIN(slotvec), n);
if(err != AL_NO_ERROR)
{
alDeleteAuxiliaryEffectSlots(cur, effectslots);
SET_ERROR_AND_GOTO(context, err, done);
}
done:
VECTOR_DEINIT(slotvec);
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *effectslots)
{
ALCcontext *context;
ALeffectslot *slot;
ALsizei i;
Context = GetContextSuspended();
if(!Context) return;
context = GetContextRef();
if(!context) return;
if (n >= 0)
if(!(n >= 0))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
for(i = 0;i < n;i++)
{
// Check that all effectslots are valid
for (i = 0; i < n; i++)
{
if (!alIsAuxiliaryEffectSlot(effectslots[i]))
{
alSetError(AL_INVALID_NAME);
break;
}
else
{
ALAuxiliaryEffectSlot = (ALeffectslot*)ALTHUNK_LOOKUPENTRY(effectslots[i]);
if(ALAuxiliaryEffectSlot->refcount > 0)
{
alSetError(AL_INVALID_NAME);
break;
}
}
}
if (i == n)
{
// All effectslots are valid
for (i = 0; i < n; i++)
{
// Recheck that the effectslot is valid, because there could be duplicated names
if (alIsAuxiliaryEffectSlot(effectslots[i]))
{
ALeffectslot **list;
ALAuxiliaryEffectSlot = ((ALeffectslot*)ALTHUNK_LOOKUPENTRY(effectslots[i]));
// Remove Source from list of Sources
list = &Context->AuxiliaryEffectSlot;
while(*list && *list != ALAuxiliaryEffectSlot)
list = &(*list)->next;
if(*list)
*list = (*list)->next;
ALTHUNK_REMOVEENTRY(ALAuxiliaryEffectSlot->effectslot);
if(ALAuxiliaryEffectSlot->EffectState)
ALEffect_Destroy(ALAuxiliaryEffectSlot->EffectState);
memset(ALAuxiliaryEffectSlot, 0, sizeof(ALeffectslot));
free(ALAuxiliaryEffectSlot);
Context->AuxiliaryEffectSlotCount--;
}
}
}
}
else
alSetError(AL_INVALID_VALUE);
ProcessContext(Context);
}
ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot)
{
ALCcontext *Context;
ALeffectslot **list;
Context = GetContextSuspended();
if(!Context) return AL_FALSE;
list = &Context->AuxiliaryEffectSlot;
while(*list && (*list)->effectslot != effectslot)
list = &(*list)->next;
ProcessContext(Context);
return (*list ? AL_TRUE : AL_FALSE);
}
ALvoid AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint iValue)
{
ALCcontext *Context;
Context = GetContextSuspended();
if(!Context) return;
if (alIsAuxiliaryEffectSlot(effectslot))
{
ALeffectslot *ALEffectSlot = (ALeffectslot*)ALTHUNK_LOOKUPENTRY(effectslot);
switch(param)
{
case AL_EFFECTSLOT_EFFECT:
if(alIsEffect(iValue))
{
ALeffect *effect = (ALeffect*)ALTHUNK_LOOKUPENTRY(iValue);
InitializeEffect(Context, ALEffectSlot, effect);
}
else
alSetError(AL_INVALID_VALUE);
break;
case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
if(iValue == AL_TRUE || iValue == AL_FALSE)
ALEffectSlot->AuxSendAuto = iValue;
else
alSetError(AL_INVALID_VALUE);
break;
default:
alSetError(AL_INVALID_ENUM);
break;
}
}
else
alSetError(AL_INVALID_NAME);
ProcessContext(Context);
}
ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues)
{
ALCcontext *Context;
Context = GetContextSuspended();
if(!Context) return;
if (alIsAuxiliaryEffectSlot(effectslot))
{
switch(param)
{
case AL_EFFECTSLOT_EFFECT:
case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
alAuxiliaryEffectSloti(effectslot, param, piValues[0]);
break;
default:
alSetError(AL_INVALID_ENUM);
break;
}
}
else
alSetError(AL_INVALID_NAME);
ProcessContext(Context);
}
ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat flValue)
{
ALCcontext *Context;
Context = GetContextSuspended();
if(!Context) return;
if (alIsAuxiliaryEffectSlot(effectslot))
{
ALeffectslot *ALEffectSlot = (ALeffectslot*)ALTHUNK_LOOKUPENTRY(effectslot);
switch(param)
{
case AL_EFFECTSLOT_GAIN:
if(flValue >= 0.0f && flValue <= 1.0f)
ALEffectSlot->Gain = flValue;
else
alSetError(AL_INVALID_VALUE);
break;
default:
alSetError(AL_INVALID_ENUM);
break;
}
}
else
alSetError(AL_INVALID_NAME);
ProcessContext(Context);
}
ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues)
{
ALCcontext *Context;
Context = GetContextSuspended();
if(!Context) return;
if (alIsAuxiliaryEffectSlot(effectslot))
{
switch(param)
{
case AL_EFFECTSLOT_GAIN:
alAuxiliaryEffectSlotf(effectslot, param, pflValues[0]);
break;
default:
alSetError(AL_INVALID_ENUM);
break;
}
}
else
alSetError(AL_INVALID_NAME);
ProcessContext(Context);
}
ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *piValue)
{
ALCcontext *Context;
Context = GetContextSuspended();
if(!Context) return;
if (alIsAuxiliaryEffectSlot(effectslot))
{
ALeffectslot *ALEffectSlot = (ALeffectslot*)ALTHUNK_LOOKUPENTRY(effectslot);
switch(param)
{
case AL_EFFECTSLOT_EFFECT:
*piValue = ALEffectSlot->effect.effect;
break;
case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
*piValue = ALEffectSlot->AuxSendAuto;
break;
default:
alSetError(AL_INVALID_ENUM);
break;
}
}
else
alSetError(AL_INVALID_NAME);
ProcessContext(Context);
}
ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues)
{
ALCcontext *Context;
Context = GetContextSuspended();
if(!Context) return;
if (alIsAuxiliaryEffectSlot(effectslot))
{
switch(param)
{
case AL_EFFECTSLOT_EFFECT:
case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
alGetAuxiliaryEffectSloti(effectslot, param, piValues);
break;
default:
alSetError(AL_INVALID_ENUM);
break;
}
}
else
alSetError(AL_INVALID_NAME);
ProcessContext(Context);
}
ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *pflValue)
{
ALCcontext *Context;
Context = GetContextSuspended();
if(!Context) return;
if (alIsAuxiliaryEffectSlot(effectslot))
{
ALeffectslot *ALEffectSlot = (ALeffectslot*)ALTHUNK_LOOKUPENTRY(effectslot);
switch(param)
{
case AL_EFFECTSLOT_GAIN:
*pflValue = ALEffectSlot->Gain;
break;
default:
alSetError(AL_INVALID_ENUM);
break;
}
}
else
alSetError(AL_INVALID_NAME);
ProcessContext(Context);
}
ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues)
{
ALCcontext *Context;
Context = GetContextSuspended();
if(!Context) return;
if (alIsAuxiliaryEffectSlot(effectslot))
{
switch(param)
{
case AL_EFFECTSLOT_GAIN:
alGetAuxiliaryEffectSlotf(effectslot, param, pflValues);
break;
default:
alSetError(AL_INVALID_ENUM);
break;
}
}
else
alSetError(AL_INVALID_NAME);
ProcessContext(Context);
}
static ALvoid NoneDestroy(ALeffectState *State)
{ free(State); }
static ALboolean NoneDeviceUpdate(ALeffectState *State, ALCdevice *Device)
{
return AL_TRUE;
(void)State;
(void)Device;
}
static ALvoid NoneUpdate(ALeffectState *State, ALCcontext *Context, const ALeffect *Effect)
{
(void)State;
(void)Context;
(void)Effect;
}
static ALvoid NoneProcess(ALeffectState *State, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[OUTPUTCHANNELS])
{
(void)State;
(void)Slot;
(void)SamplesToDo;
(void)SamplesIn;
(void)SamplesOut;
}
ALeffectState *NoneCreate(void)
{
ALeffectState *state;
state = calloc(1, sizeof(*state));
if(!state)
{
alSetError(AL_OUT_OF_MEMORY);
return NULL;
if((slot=LookupEffectSlot(context, effectslots[i])) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
if(ReadRef(&slot->ref) != 0)
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
}
state->Destroy = NoneDestroy;
state->DeviceUpdate = NoneDeviceUpdate;
state->Update = NoneUpdate;
state->Process = NoneProcess;
// All effectslots are valid
for(i = 0;i < n;i++)
{
if((slot=RemoveEffectSlot(context, effectslots[i])) == NULL)
continue;
FreeThunkEntry(slot->id);
return state;
RemoveEffectSlotArray(context, slot);
DELETE_OBJ(slot->EffectState);
memset(slot, 0, sizeof(*slot));
al_free(slot);
}
done:
ALCcontext_DecRef(context);
}
static ALvoid InitializeEffect(ALCcontext *Context, ALeffectslot *ALEffectSlot, ALeffect *effect)
AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot)
{
if(ALEffectSlot->effect.type != (effect?effect->type:AL_EFFECT_NULL))
ALCcontext *context;
ALboolean ret;
context = GetContextRef();
if(!context) return AL_FALSE;
ret = (LookupEffectSlot(context, effectslot) ? AL_TRUE : AL_FALSE);
ALCcontext_DecRef(context);
return ret;
}
AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint value)
{
ALCdevice *device;
ALCcontext *context;
ALeffectslot *slot;
ALeffect *effect = NULL;
ALenum err;
context = GetContextRef();
if(!context) return;
device = context->Device;
if((slot=LookupEffectSlot(context, effectslot)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
{
ALeffectState *NewState = NULL;
if(!effect || effect->type == AL_EFFECT_NULL)
NewState = NoneCreate();
else if(effect->type == AL_EFFECT_EAXREVERB)
NewState = EAXVerbCreate();
else if(effect->type == AL_EFFECT_REVERB)
NewState = VerbCreate();
else if(effect->type == AL_EFFECT_ECHO)
NewState = EchoCreate();
/* No new state? An error occured.. */
if(NewState == NULL ||
ALEffect_DeviceUpdate(NewState, Context->Device) == AL_FALSE)
{
if(NewState)
ALEffect_Destroy(NewState);
return;
}
if(ALEffectSlot->EffectState)
ALEffect_Destroy(ALEffectSlot->EffectState);
ALEffectSlot->EffectState = NewState;
case AL_EFFECTSLOT_EFFECT:
effect = (value ? LookupEffect(device, value) : NULL);
if(!(value == 0 || effect != NULL))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
err = InitializeEffect(device, slot, effect);
if(err != AL_NO_ERROR)
SET_ERROR_AND_GOTO(context, err, done);
ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
break;
case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
if(!(value == AL_TRUE || value == AL_FALSE))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
slot->AuxSendAuto = value;
ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
if(!effect)
done:
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *values)
{
ALCcontext *context;
switch(param)
{
memset(&ALEffectSlot->effect, 0, sizeof(ALEffectSlot->effect));
case AL_EFFECTSLOT_EFFECT:
case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
alAuxiliaryEffectSloti(effectslot, param, values[0]);
return;
}
memcpy(&ALEffectSlot->effect, effect, sizeof(*effect));
ALEffect_Update(ALEffectSlot->EffectState, Context, effect);
context = GetContextRef();
if(!context) return;
if(LookupEffectSlot(context, effectslot) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
{
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat value)
{
ALCcontext *context;
ALeffectslot *slot;
context = GetContextRef();
if(!context) return;
if((slot=LookupEffectSlot(context, effectslot)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
{
case AL_EFFECTSLOT_GAIN:
if(!(value >= 0.0f && value <= 1.0f))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
slot->Gain = value;
ATOMIC_STORE(&slot->NeedsUpdate, AL_TRUE);
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *values)
{
ALCcontext *context;
switch(param)
{
case AL_EFFECTSLOT_GAIN:
alAuxiliaryEffectSlotf(effectslot, param, values[0]);
return;
}
context = GetContextRef();
if(!context) return;
if(LookupEffectSlot(context, effectslot) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
{
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *value)
{
ALCcontext *context;
ALeffectslot *slot;
context = GetContextRef();
if(!context) return;
if((slot=LookupEffectSlot(context, effectslot)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
{
case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
*value = slot->AuxSendAuto;
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *values)
{
ALCcontext *context;
switch(param)
{
case AL_EFFECTSLOT_EFFECT:
case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
alGetAuxiliaryEffectSloti(effectslot, param, values);
return;
}
context = GetContextRef();
if(!context) return;
if(LookupEffectSlot(context, effectslot) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
{
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *value)
{
ALCcontext *context;
ALeffectslot *slot;
context = GetContextRef();
if(!context) return;
if((slot=LookupEffectSlot(context, effectslot)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
{
case AL_EFFECTSLOT_GAIN:
*value = slot->Gain;
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *values)
{
ALCcontext *context;
switch(param)
{
case AL_EFFECTSLOT_GAIN:
alGetAuxiliaryEffectSlotf(effectslot, param, values);
return;
}
context = GetContextRef();
if(!context) return;
if(LookupEffectSlot(context, effectslot) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
{
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
static ALenum AddEffectSlotArray(ALCcontext *context, ALeffectslot **start, ALsizei count)
{
ALenum err = AL_NO_ERROR;
LockContext(context);
if(!VECTOR_INSERT(context->ActiveAuxSlots, VECTOR_ITER_END(context->ActiveAuxSlots), start, start+count))
err = AL_OUT_OF_MEMORY;
UnlockContext(context);
return err;
}
static void RemoveEffectSlotArray(ALCcontext *context, const ALeffectslot *slot)
{
ALeffectslot **iter;
LockContext(context);
#define MATCH_SLOT(_i) (slot == *(_i))
VECTOR_FIND_IF(iter, ALeffectslot*, context->ActiveAuxSlots, MATCH_SLOT);
if(iter != VECTOR_ITER_END(context->ActiveAuxSlots))
{
*iter = VECTOR_BACK(context->ActiveAuxSlots);
VECTOR_POP_BACK(context->ActiveAuxSlots);
}
#undef MATCH_SLOT
UnlockContext(context);
}
void InitEffectFactoryMap(void)
{
InitUIntMap(&EffectStateFactoryMap, ~0);
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_NULL, ALnullStateFactory_getFactory);
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_EAXREVERB, ALreverbStateFactory_getFactory);
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_REVERB, ALreverbStateFactory_getFactory);
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_AUTOWAH, ALautowahStateFactory_getFactory);
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_CHORUS, ALchorusStateFactory_getFactory);
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_COMPRESSOR, ALcompressorStateFactory_getFactory);
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DISTORTION, ALdistortionStateFactory_getFactory);
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_ECHO, ALechoStateFactory_getFactory);
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_EQUALIZER, ALequalizerStateFactory_getFactory);
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_FLANGER, ALflangerStateFactory_getFactory);
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_RING_MODULATOR, ALmodulatorStateFactory_getFactory);
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DEDICATED_DIALOGUE, ALdedicatedStateFactory_getFactory);
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT, ALdedicatedStateFactory_getFactory);
}
void DeinitEffectFactoryMap(void)
{
ResetUIntMap(&EffectStateFactoryMap);
}
ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *effect)
{
ALenum newtype = (effect ? effect->type : AL_EFFECT_NULL);
ALeffectStateFactory *factory;
if(newtype != EffectSlot->EffectType)
{
ALeffectState *State;
FPUCtl oldMode;
factory = getFactoryByType(newtype);
if(!factory)
{
ERR("Failed to find factory for effect type 0x%04x\n", newtype);
return AL_INVALID_ENUM;
}
State = V0(factory,create)();
if(!State)
return AL_OUT_OF_MEMORY;
SetMixerFPUMode(&oldMode);
ALCdevice_Lock(Device);
if(V(State,deviceUpdate)(Device) == AL_FALSE)
{
ALCdevice_Unlock(Device);
RestoreFPUMode(&oldMode);
DELETE_OBJ(State);
return AL_OUT_OF_MEMORY;
}
State = ExchangePtr((XchgPtr*)&EffectSlot->EffectState, State);
if(!effect)
{
memset(&EffectSlot->EffectProps, 0, sizeof(EffectSlot->EffectProps));
EffectSlot->EffectType = AL_EFFECT_NULL;
}
else
{
memcpy(&EffectSlot->EffectProps, &effect->Props, sizeof(effect->Props));
EffectSlot->EffectType = effect->type;
}
/* FIXME: This should be done asynchronously, but since the EffectState
* object was changed, it needs an update before its Process method can
* be called. */
ATOMIC_STORE(&EffectSlot->NeedsUpdate, AL_FALSE);
V(EffectSlot->EffectState,update)(Device, EffectSlot);
ALCdevice_Unlock(Device);
RestoreFPUMode(&oldMode);
DELETE_OBJ(State);
State = NULL;
}
else
{
if(effect)
{
ALCdevice_Lock(Device);
memcpy(&EffectSlot->EffectProps, &effect->Props, sizeof(effect->Props));
ALCdevice_Unlock(Device);
ATOMIC_STORE(&EffectSlot->NeedsUpdate, AL_TRUE);
}
}
return AL_NO_ERROR;
}
ALenum InitEffectSlot(ALeffectslot *slot)
{
ALeffectStateFactory *factory;
ALuint i, c;
slot->EffectType = AL_EFFECT_NULL;
factory = getFactoryByType(AL_EFFECT_NULL);
if(!(slot->EffectState=V0(factory,create)()))
return AL_OUT_OF_MEMORY;
slot->Gain = 1.0;
slot->AuxSendAuto = AL_TRUE;
ATOMIC_INIT(&slot->NeedsUpdate, AL_FALSE);
for(c = 0;c < 1;c++)
{
for(i = 0;i < BUFFERSIZE;i++)
slot->WetBuffer[c][i] = 0.0f;
}
InitRef(&slot->ref, 0);
return AL_NO_ERROR;
}
ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context)
{
while(Context->AuxiliaryEffectSlot)
ALsizei pos;
for(pos = 0;pos < Context->EffectSlotMap.size;pos++)
{
ALeffectslot *temp = Context->AuxiliaryEffectSlot;
Context->AuxiliaryEffectSlot = Context->AuxiliaryEffectSlot->next;
ALeffectslot *temp = Context->EffectSlotMap.array[pos].value;
Context->EffectSlotMap.array[pos].value = NULL;
// Release effectslot structure
if(temp->EffectState)
ALEffect_Destroy(temp->EffectState);
ALTHUNK_REMOVEENTRY(temp->effectslot);
DELETE_OBJ(temp->EffectState);
FreeThunkEntry(temp->id);
memset(temp, 0, sizeof(ALeffectslot));
free(temp);
al_free(temp);
}
Context->AuxiliaryEffectSlotCount = 0;
}
+1155 -949
View File
File diff suppressed because it is too large Load Diff
-639
View File
@@ -1,639 +0,0 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include "alMain.h"
#include "AL/al.h"
#include "AL/alc.h"
#include "AL/alext.h"
#include "alError.h"
#include "alDatabuffer.h"
#include "alThunk.h"
/*
* alGenDatabuffersEXT(ALsizei n, ALuint *puiBuffers)
*
* Generates n AL Databuffers, and stores the Databuffers Names in the array pointed to by puiBuffers
*/
ALvoid ALAPIENTRY alGenDatabuffersEXT(ALsizei n,ALuint *puiBuffers)
{
ALCcontext *Context;
ALsizei i=0;
Context = GetContextSuspended();
if(!Context) return;
/* Check that we are actually generation some Databuffers */
if(n > 0)
{
ALCdevice *device = Context->Device;
/* Check the pointer is valid (and points to enough memory to store
* Databuffer Names) */
if(!IsBadWritePtr((void*)puiBuffers, n * sizeof(ALuint)))
{
ALdatabuffer **list = &device->Databuffers;
while(*list)
list = &(*list)->next;
/* Create all the new Databuffers */
while(i < n)
{
*list = calloc(1, sizeof(ALdatabuffer));
if(!(*list))
{
alDeleteDatabuffersEXT(i, puiBuffers);
alSetError(AL_OUT_OF_MEMORY);
break;
}
puiBuffers[i] = (ALuint)ALTHUNK_ADDENTRY(*list);
(*list)->databuffer = puiBuffers[i];
(*list)->state = UNMAPPED;
device->DatabufferCount++;
i++;
list = &(*list)->next;
}
}
else
alSetError(AL_INVALID_VALUE);
}
ProcessContext(Context);
}
/*
* alDatabeleteBuffersEXT(ALsizei n, ALuint *puiBuffers)
*
* Deletes the n AL Databuffers pointed to by puiBuffers
*/
ALvoid ALAPIENTRY alDeleteDatabuffersEXT(ALsizei n, const ALuint *puiBuffers)
{
ALCcontext *Context;
ALdatabuffer *ALBuf;
ALsizei i;
ALboolean bFailed = AL_FALSE;
Context = GetContextSuspended();
if(!Context) return;
/* Check we are actually Deleting some Databuffers */
if(n >= 0)
{
ALCdevice *device = Context->Device;
/* Check that all the databuffers are valid and can actually be
* deleted */
for(i = 0;i < n;i++)
{
/* Check for valid Buffer ID (can be NULL buffer) */
if(alIsDatabufferEXT(puiBuffers[i]))
{
/* If not the NULL buffer, check that it's unmapped */
ALBuf = ((ALdatabuffer *)ALTHUNK_LOOKUPENTRY(puiBuffers[i]));
if(ALBuf)
{
if(ALBuf->state != UNMAPPED)
{
/* Databuffer still in use, cannot be deleted */
alSetError(AL_INVALID_OPERATION);
bFailed = AL_TRUE;
}
}
}
else
{
/* Invalid Databuffer */
alSetError(AL_INVALID_NAME);
bFailed = AL_TRUE;
}
}
/* If all the Databuffers were valid (and unmapped), then we can
* delete them */
if(!bFailed)
{
for(i = 0;i < n;i++)
{
if(puiBuffers[i] && alIsDatabufferEXT(puiBuffers[i]))
{
ALdatabuffer **list = &device->Databuffers;
ALBuf = (ALdatabuffer*)ALTHUNK_LOOKUPENTRY(puiBuffers[i]);
while(*list && *list != ALBuf)
list = &(*list)->next;
if(*list)
*list = (*list)->next;
if(ALBuf == Context->SampleSource)
Context->SampleSource = NULL;
if(ALBuf == Context->SampleSink)
Context->SampleSink = NULL;
// Release the memory used to store audio data
free(ALBuf->data);
// Release buffer structure
ALTHUNK_REMOVEENTRY(puiBuffers[i]);
memset(ALBuf, 0, sizeof(ALdatabuffer));
device->DatabufferCount--;
free(ALBuf);
}
}
}
}
else
alSetError(AL_INVALID_VALUE);
ProcessContext(Context);
return;
}
/*
* alIsDatabufferEXT(ALuint uiBuffer)
*
* Checks if ulBuffer is a valid Databuffer Name
*/
ALboolean ALAPIENTRY alIsDatabufferEXT(ALuint uiBuffer)
{
ALCcontext *Context;
ALdatabuffer *ALBuf;
Context = GetContextSuspended();
if(!Context) return AL_FALSE;
/* Check through list of generated databuffers for uiBuffer */
ALBuf = Context->Device->Databuffers;
while(ALBuf && ALBuf->databuffer != uiBuffer)
ALBuf = ALBuf->next;
ProcessContext(Context);
return ((ALBuf || !uiBuffer) ? AL_TRUE : AL_FALSE);
}
/*
* alDatabufferDataEXT(ALuint buffer,ALvoid *data,ALsizei size,ALenum usage)
*
* Fill databuffer with data
*/
ALvoid ALAPIENTRY alDatabufferDataEXT(ALuint buffer,const ALvoid *data,ALsizei size,ALenum usage)
{
ALCcontext *Context;
ALdatabuffer *ALBuf;
ALvoid *temp;
Context = GetContextSuspended();
if(!Context) return;
if(alIsDatabufferEXT(buffer) && buffer != 0)
{
ALBuf = (ALdatabuffer*)ALTHUNK_LOOKUPENTRY(buffer);
if(ALBuf->state == UNMAPPED)
{
if(usage == AL_STREAM_WRITE_EXT || usage == AL_STREAM_READ_EXT ||
usage == AL_STREAM_COPY_EXT || usage == AL_STATIC_WRITE_EXT ||
usage == AL_STATIC_READ_EXT || usage == AL_STATIC_COPY_EXT ||
usage == AL_DYNAMIC_WRITE_EXT || usage == AL_DYNAMIC_READ_EXT ||
usage == AL_DYNAMIC_COPY_EXT)
{
/* (Re)allocate data */
temp = realloc(ALBuf->data, size);
if(temp)
{
ALBuf->data = temp;
ALBuf->size = size;
ALBuf->usage = usage;
if(data)
memcpy(ALBuf->data, data, size);
}
else
alSetError(AL_OUT_OF_MEMORY);
}
else
alSetError(AL_INVALID_ENUM);
}
else
alSetError(AL_INVALID_OPERATION);
}
else
alSetError(AL_INVALID_NAME);
ProcessContext(Context);
}
ALvoid ALAPIENTRY alDatabufferSubDataEXT(ALuint uiBuffer, ALuint start, ALsizei length, const ALvoid *data)
{
ALCcontext *pContext;
ALdatabuffer *pBuffer;
pContext = GetContextSuspended();
if(!pContext) return;
if(alIsDatabufferEXT(uiBuffer) && uiBuffer != 0)
{
pBuffer = (ALdatabuffer*)ALTHUNK_LOOKUPENTRY(uiBuffer);
if(length >= 0 && start+length <= pBuffer->size)
{
if(pBuffer->state == UNMAPPED)
memcpy(pBuffer->data+start, data, length);
else
alSetError(AL_INVALID_OPERATION);
}
else
alSetError(AL_INVALID_VALUE);
}
else
alSetError(AL_INVALID_NAME);
ProcessContext(pContext);
}
ALvoid ALAPIENTRY alGetDatabufferSubDataEXT(ALuint uiBuffer, ALuint start, ALsizei length, ALvoid *data)
{
ALCcontext *pContext;
ALdatabuffer *pBuffer;
pContext = GetContextSuspended();
if(!pContext) return;
if(alIsDatabufferEXT(uiBuffer) && uiBuffer != 0)
{
pBuffer = (ALdatabuffer*)ALTHUNK_LOOKUPENTRY(uiBuffer);
if(length >= 0 && start+length <= pBuffer->size)
{
if(pBuffer->state == UNMAPPED)
memcpy(data, pBuffer->data+start, length);
else
alSetError(AL_INVALID_OPERATION);
}
else
alSetError(AL_INVALID_VALUE);
}
else
alSetError(AL_INVALID_NAME);
ProcessContext(pContext);
}
ALvoid ALAPIENTRY alDatabufferfEXT(ALuint buffer, ALenum eParam, ALfloat flValue)
{
ALCcontext *pContext;
(void)flValue;
pContext = GetContextSuspended();
if(!pContext) return;
if(alIsDatabufferEXT(buffer) && buffer != 0)
{
switch(eParam)
{
default:
alSetError(AL_INVALID_ENUM);
break;
}
}
else
alSetError(AL_INVALID_NAME);
ProcessContext(pContext);
}
ALvoid ALAPIENTRY alDatabufferfvEXT(ALuint buffer, ALenum eParam, const ALfloat* flValues)
{
ALCcontext *pContext;
(void)flValues;
pContext = GetContextSuspended();
if(!pContext) return;
if(alIsDatabufferEXT(buffer) && buffer != 0)
{
switch(eParam)
{
default:
alSetError(AL_INVALID_ENUM);
break;
}
}
else
alSetError(AL_INVALID_NAME);
ProcessContext(pContext);
}
ALvoid ALAPIENTRY alDatabufferiEXT(ALuint buffer, ALenum eParam, ALint lValue)
{
ALCcontext *pContext;
(void)lValue;
pContext = GetContextSuspended();
if(!pContext) return;
if(alIsDatabufferEXT(buffer) && buffer != 0)
{
switch(eParam)
{
default:
alSetError(AL_INVALID_ENUM);
break;
}
}
else
alSetError(AL_INVALID_NAME);
ProcessContext(pContext);
}
ALvoid ALAPIENTRY alDatabufferivEXT(ALuint buffer, ALenum eParam, const ALint* plValues)
{
ALCcontext *pContext;
(void)plValues;
pContext = GetContextSuspended();
if(!pContext) return;
if(alIsDatabufferEXT(buffer) && buffer != 0)
{
switch(eParam)
{
default:
alSetError(AL_INVALID_ENUM);
break;
}
}
else
alSetError(AL_INVALID_NAME);
ProcessContext(pContext);
}
ALvoid ALAPIENTRY alGetDatabufferfEXT(ALuint buffer, ALenum eParam, ALfloat *pflValue)
{
ALCcontext *pContext;
pContext = GetContextSuspended();
if(!pContext) return;
if(pflValue)
{
if(alIsDatabufferEXT(buffer) && buffer != 0)
{
switch(eParam)
{
default:
alSetError(AL_INVALID_ENUM);
break;
}
}
else
alSetError(AL_INVALID_NAME);
}
else
alSetError(AL_INVALID_VALUE);
ProcessContext(pContext);
}
ALvoid ALAPIENTRY alGetDatabufferfvEXT(ALuint buffer, ALenum eParam, ALfloat* pflValues)
{
ALCcontext *pContext;
pContext = GetContextSuspended();
if(!pContext) return;
if(pflValues)
{
if(alIsDatabufferEXT(buffer) && buffer != 0)
{
switch(eParam)
{
default:
alSetError(AL_INVALID_ENUM);
break;
}
}
else
alSetError(AL_INVALID_NAME);
}
else
alSetError(AL_INVALID_VALUE);
ProcessContext(pContext);
}
ALvoid ALAPIENTRY alGetDatabufferiEXT(ALuint buffer, ALenum eParam, ALint *plValue)
{
ALCcontext *pContext;
ALdatabuffer *pBuffer;
pContext = GetContextSuspended();
if(!pContext) return;
if(plValue)
{
if(alIsDatabufferEXT(buffer) && buffer != 0)
{
pBuffer = (ALdatabuffer*)ALTHUNK_LOOKUPENTRY(buffer);
switch(eParam)
{
case AL_SIZE:
*plValue = pBuffer->size;
break;
default:
alSetError(AL_INVALID_ENUM);
break;
}
}
else
alSetError(AL_INVALID_NAME);
}
else
alSetError(AL_INVALID_VALUE);
ProcessContext(pContext);
}
ALvoid ALAPIENTRY alGetDatabufferivEXT(ALuint buffer, ALenum eParam, ALint* plValues)
{
ALCcontext *pContext;
pContext = GetContextSuspended();
if(!pContext) return;
if(plValues)
{
if(alIsDatabufferEXT(buffer) && buffer != 0)
{
switch (eParam)
{
case AL_SIZE:
alGetBufferi(buffer, eParam, plValues);
break;
default:
alSetError(AL_INVALID_ENUM);
break;
}
}
else
alSetError(AL_INVALID_NAME);
}
else
alSetError(AL_INVALID_VALUE);
ProcessContext(pContext);
}
ALvoid ALAPIENTRY alSelectDatabufferEXT(ALenum target, ALuint uiBuffer)
{
ALCcontext *pContext;
ALdatabuffer *pBuffer;
pContext = GetContextSuspended();
if(!pContext) return;
if(alIsDatabufferEXT(uiBuffer))
{
pBuffer = (ALdatabuffer*)(uiBuffer ? ALTHUNK_LOOKUPENTRY(uiBuffer) : NULL);
if(target == AL_SAMPLE_SOURCE_EXT)
pContext->SampleSource = pBuffer;
else if(target == AL_SAMPLE_SINK_EXT)
pContext->SampleSink = pBuffer;
else
alSetError(AL_INVALID_VALUE);
}
else
alSetError(AL_INVALID_NAME);
ProcessContext(pContext);
}
ALvoid* ALAPIENTRY alMapDatabufferEXT(ALuint uiBuffer, ALuint start, ALsizei length, ALenum access)
{
ALCcontext *pContext;
ALdatabuffer *pBuffer;
ALvoid *ret = NULL;
pContext = GetContextSuspended();
if(!pContext) return NULL;
if(alIsDatabufferEXT(uiBuffer) && uiBuffer != 0)
{
pBuffer = (ALdatabuffer*)ALTHUNK_LOOKUPENTRY(uiBuffer);
if(length >= 0 && start+length <= pBuffer->size)
{
if(access == AL_READ_ONLY_EXT || access == AL_WRITE_ONLY_EXT ||
access == AL_READ_WRITE_EXT)
{
if(pBuffer->state == UNMAPPED)
{
ret = pBuffer->data + start;
pBuffer->state = MAPPED;
}
else
alSetError(AL_INVALID_OPERATION);
}
else
alSetError(AL_INVALID_ENUM);
}
else
alSetError(AL_INVALID_VALUE);
}
else
alSetError(AL_INVALID_NAME);
ProcessContext(pContext);
return ret;
}
ALvoid ALAPIENTRY alUnmapDatabufferEXT(ALuint uiBuffer)
{
ALCcontext *pContext;
ALdatabuffer *pBuffer;
pContext = GetContextSuspended();
if(!pContext) return;
if(alIsDatabufferEXT(uiBuffer) && uiBuffer != 0)
{
pBuffer = (ALdatabuffer*)ALTHUNK_LOOKUPENTRY(uiBuffer);
if(pBuffer->state == MAPPED)
pBuffer->state = UNMAPPED;
else
alSetError(AL_INVALID_OPERATION);
}
else
alSetError(AL_INVALID_NAME);
ProcessContext(pContext);
}
/*
* ReleaseALDatabuffers()
*
* INTERNAL FN : Called by DLLMain on exit to destroy any buffers that still exist
*/
ALvoid ReleaseALDatabuffers(ALCdevice *device)
{
ALdatabuffer *ALBuffer;
ALdatabuffer *ALBufferTemp;
ALBuffer = device->Databuffers;
while(ALBuffer)
{
// Release sample data
free(ALBuffer->data);
// Release Buffer structure
ALBufferTemp = ALBuffer;
ALBuffer = ALBuffer->next;
memset(ALBufferTemp, 0, sizeof(ALdatabuffer));
free(ALBufferTemp);
}
device->Databuffers = NULL;
device->DatabufferCount = 0;
}
+537 -1079
View File
File diff suppressed because it is too large Load Diff
+42 -19
View File
@@ -20,35 +20,58 @@
#include "config.h"
#include <signal.h>
#ifdef HAVE_WINDOWS_H
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include "alMain.h"
#include "AL/alc.h"
#include "alError.h"
ALAPI ALenum ALAPIENTRY alGetError(ALvoid)
ALboolean TrapALError = AL_FALSE;
ALvoid alSetError(ALCcontext *Context, ALenum errorCode)
{
ALenum curerr = AL_NO_ERROR;
if(TrapALError)
{
#ifdef _WIN32
/* DebugBreak will cause an exception if there is no debugger */
if(IsDebuggerPresent())
DebugBreak();
#elif defined(SIGTRAP)
raise(SIGTRAP);
#endif
}
ATOMIC_COMPARE_EXCHANGE_STRONG(ALenum, &Context->LastError, &curerr, errorCode);
}
AL_API ALenum AL_APIENTRY alGetError(void)
{
ALCcontext *Context;
ALenum errorCode;
Context = GetContextSuspended();
if(!Context) return AL_INVALID_OPERATION;
Context = GetContextRef();
if(!Context)
{
if(TrapALError)
{
#ifdef _WIN32
if(IsDebuggerPresent())
DebugBreak();
#elif defined(SIGTRAP)
raise(SIGTRAP);
#endif
}
return AL_INVALID_OPERATION;
}
errorCode = Context->LastError;
Context->LastError = AL_NO_ERROR;
errorCode = ATOMIC_EXCHANGE(ALenum, &Context->LastError, AL_NO_ERROR);
ProcessContext(Context);
ALCcontext_DecRef(Context);
return errorCode;
}
ALvoid alSetError(ALenum errorCode)
{
ALCcontext *Context;
Context = GetContextSuspended();
if(Context)
{
if(Context->LastError == AL_NO_ERROR)
Context->LastError = errorCode;
ProcessContext(Context);
}
}
+37 -334
View File
@@ -23,346 +23,58 @@
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "alExtension.h"
#include "alError.h"
#include "alMain.h"
#include "alFilter.h"
#include "alEffect.h"
#include "alAuxEffectSlot.h"
#include "alDatabuffer.h"
#include "alSource.h"
#include "alBuffer.h"
#include "AL/al.h"
#include "AL/alc.h"
static ALfunction function[]= {
{ "alEnable", (ALvoid *) alEnable },
{ "alDisable", (ALvoid *) alDisable },
{ "alIsEnabled", (ALvoid *) alIsEnabled },
{ "alGetString", (ALvoid *) alGetString },
{ "alGetBooleanv", (ALvoid *) alGetBooleanv },
{ "alGetIntegerv", (ALvoid *) alGetIntegerv },
{ "alGetFloatv", (ALvoid *) alGetFloatv },
{ "alGetDoublev", (ALvoid *) alGetDoublev },
{ "alGetBoolean", (ALvoid *) alGetBoolean },
{ "alGetInteger", (ALvoid *) alGetInteger },
{ "alGetFloat", (ALvoid *) alGetFloat },
{ "alGetDouble", (ALvoid *) alGetDouble },
{ "alGetError", (ALvoid *) alGetError },
{ "alIsExtensionPresent", (ALvoid *) alIsExtensionPresent },
{ "alGetProcAddress", (ALvoid *) alGetProcAddress },
{ "alGetEnumValue", (ALvoid *) alGetEnumValue },
{ "alListenerf", (ALvoid *) alListenerf },
{ "alListener3f", (ALvoid *) alListener3f },
{ "alListenerfv", (ALvoid *) alListenerfv },
{ "alListeneri", (ALvoid *) alListeneri },
{ "alListener3i", (ALvoid *) alListener3i },
{ "alListeneriv", (ALvoid *) alListeneriv },
{ "alGetListenerf", (ALvoid *) alGetListenerf },
{ "alGetListener3f", (ALvoid *) alGetListener3f },
{ "alGetListenerfv", (ALvoid *) alGetListenerfv },
{ "alGetListeneri", (ALvoid *) alGetListeneri },
{ "alGetListener3i", (ALvoid *) alGetListener3i },
{ "alGetListeneriv", (ALvoid *) alGetListeneriv },
{ "alGenSources", (ALvoid *) alGenSources },
{ "alDeleteSources", (ALvoid *) alDeleteSources },
{ "alIsSource", (ALvoid *) alIsSource },
{ "alSourcef", (ALvoid *) alSourcef },
{ "alSource3f", (ALvoid *) alSource3f },
{ "alSourcefv", (ALvoid *) alSourcefv },
{ "alSourcei", (ALvoid *) alSourcei },
{ "alSource3i", (ALvoid *) alSource3i },
{ "alSourceiv", (ALvoid *) alSourceiv },
{ "alGetSourcef", (ALvoid *) alGetSourcef },
{ "alGetSource3f", (ALvoid *) alGetSource3f },
{ "alGetSourcefv", (ALvoid *) alGetSourcefv },
{ "alGetSourcei", (ALvoid *) alGetSourcei },
{ "alGetSource3i", (ALvoid *) alGetSource3i },
{ "alGetSourceiv", (ALvoid *) alGetSourceiv },
{ "alSourcePlayv", (ALvoid *) alSourcePlayv },
{ "alSourceStopv", (ALvoid *) alSourceStopv },
{ "alSourceRewindv", (ALvoid *) alSourceRewindv },
{ "alSourcePausev", (ALvoid *) alSourcePausev },
{ "alSourcePlay", (ALvoid *) alSourcePlay },
{ "alSourceStop", (ALvoid *) alSourceStop },
{ "alSourceRewind", (ALvoid *) alSourceRewind },
{ "alSourcePause", (ALvoid *) alSourcePause },
{ "alSourceQueueBuffers", (ALvoid *) alSourceQueueBuffers },
{ "alSourceUnqueueBuffers", (ALvoid *) alSourceUnqueueBuffers },
{ "alGenBuffers", (ALvoid *) alGenBuffers },
{ "alDeleteBuffers", (ALvoid *) alDeleteBuffers },
{ "alIsBuffer", (ALvoid *) alIsBuffer },
{ "alBufferData", (ALvoid *) alBufferData },
{ "alBufferf", (ALvoid *) alBufferf },
{ "alBuffer3f", (ALvoid *) alBuffer3f },
{ "alBufferfv", (ALvoid *) alBufferfv },
{ "alBufferi", (ALvoid *) alBufferi },
{ "alBuffer3i", (ALvoid *) alBuffer3i },
{ "alBufferiv", (ALvoid *) alBufferiv },
{ "alGetBufferf", (ALvoid *) alGetBufferf },
{ "alGetBuffer3f", (ALvoid *) alGetBuffer3f },
{ "alGetBufferfv", (ALvoid *) alGetBufferfv },
{ "alGetBufferi", (ALvoid *) alGetBufferi },
{ "alGetBuffer3i", (ALvoid *) alGetBuffer3i },
{ "alGetBufferiv", (ALvoid *) alGetBufferiv },
{ "alDopplerFactor", (ALvoid *) alDopplerFactor },
{ "alDopplerVelocity", (ALvoid *) alDopplerVelocity },
{ "alSpeedOfSound", (ALvoid *) alSpeedOfSound },
{ "alDistanceModel", (ALvoid *) alDistanceModel },
{ "alGenFilters", (ALvoid *) alGenFilters },
{ "alDeleteFilters", (ALvoid *) alDeleteFilters },
{ "alIsFilter", (ALvoid *) alIsFilter },
{ "alFilteri", (ALvoid *) alFilteri },
{ "alFilteriv", (ALvoid *) alFilteriv },
{ "alFilterf", (ALvoid *) alFilterf },
{ "alFilterfv", (ALvoid *) alFilterfv },
{ "alGetFilteri", (ALvoid *) alGetFilteri },
{ "alGetFilteriv", (ALvoid *) alGetFilteriv },
{ "alGetFilterf", (ALvoid *) alGetFilterf },
{ "alGetFilterfv", (ALvoid *) alGetFilterfv },
{ "alGenEffects", (ALvoid *) alGenEffects },
{ "alDeleteEffects", (ALvoid *) alDeleteEffects },
{ "alIsEffect", (ALvoid *) alIsEffect },
{ "alEffecti", (ALvoid *) alEffecti },
{ "alEffectiv", (ALvoid *) alEffectiv },
{ "alEffectf", (ALvoid *) alEffectf },
{ "alEffectfv", (ALvoid *) alEffectfv },
{ "alGetEffecti", (ALvoid *) alGetEffecti },
{ "alGetEffectiv", (ALvoid *) alGetEffectiv },
{ "alGetEffectf", (ALvoid *) alGetEffectf },
{ "alGetEffectfv", (ALvoid *) alGetEffectfv },
{ "alGenAuxiliaryEffectSlots", (ALvoid *) alGenAuxiliaryEffectSlots },
{ "alDeleteAuxiliaryEffectSlots",(ALvoid *) alDeleteAuxiliaryEffectSlots},
{ "alIsAuxiliaryEffectSlot", (ALvoid *) alIsAuxiliaryEffectSlot },
{ "alAuxiliaryEffectSloti", (ALvoid *) alAuxiliaryEffectSloti },
{ "alAuxiliaryEffectSlotiv", (ALvoid *) alAuxiliaryEffectSlotiv },
{ "alAuxiliaryEffectSlotf", (ALvoid *) alAuxiliaryEffectSlotf },
{ "alAuxiliaryEffectSlotfv", (ALvoid *) alAuxiliaryEffectSlotfv },
{ "alGetAuxiliaryEffectSloti", (ALvoid *) alGetAuxiliaryEffectSloti },
{ "alGetAuxiliaryEffectSlotiv", (ALvoid *) alGetAuxiliaryEffectSlotiv},
{ "alGetAuxiliaryEffectSlotf", (ALvoid *) alGetAuxiliaryEffectSlotf },
{ "alGetAuxiliaryEffectSlotfv", (ALvoid *) alGetAuxiliaryEffectSlotfv},
{ "alBufferSubDataEXT", (ALvoid *) alBufferSubDataEXT },
{ "alGenDatabuffersEXT", (ALvoid *) alGenDatabuffersEXT },
{ "alDeleteDatabuffersEXT", (ALvoid *) alDeleteDatabuffersEXT },
{ "alIsDatabufferEXT", (ALvoid *) alIsDatabufferEXT },
{ "alDatabufferDataEXT", (ALvoid *) alDatabufferDataEXT },
{ "alDatabufferSubDataEXT", (ALvoid *) alDatabufferSubDataEXT },
{ "alGetDatabufferSubDataEXT", (ALvoid *) alGetDatabufferSubDataEXT },
{ "alDatabufferfEXT", (ALvoid *) alDatabufferfEXT },
{ "alDatabufferfvEXT", (ALvoid *) alDatabufferfvEXT },
{ "alDatabufferiEXT", (ALvoid *) alDatabufferiEXT },
{ "alDatabufferivEXT", (ALvoid *) alDatabufferivEXT },
{ "alGetDatabufferfEXT", (ALvoid *) alGetDatabufferfEXT },
{ "alGetDatabufferfvEXT", (ALvoid *) alGetDatabufferfvEXT },
{ "alGetDatabufferiEXT", (ALvoid *) alGetDatabufferiEXT },
{ "alGetDatabufferivEXT", (ALvoid *) alGetDatabufferivEXT },
{ "alSelectDatabufferEXT", (ALvoid *) alSelectDatabufferEXT },
{ "alMapDatabufferEXT", (ALvoid *) alMapDatabufferEXT },
{ "alUnmapDatabufferEXT", (ALvoid *) alUnmapDatabufferEXT },
{ NULL, (ALvoid *) NULL } };
static ALenums enumeration[]={
// Types
{ (ALchar *)"AL_INVALID", AL_INVALID },
{ (ALchar *)"AL_NONE", AL_NONE },
{ (ALchar *)"AL_FALSE", AL_FALSE },
{ (ALchar *)"AL_TRUE", AL_TRUE },
// Source and Listener Properties
{ (ALchar *)"AL_SOURCE_RELATIVE", AL_SOURCE_RELATIVE },
{ (ALchar *)"AL_CONE_INNER_ANGLE", AL_CONE_INNER_ANGLE },
{ (ALchar *)"AL_CONE_OUTER_ANGLE", AL_CONE_OUTER_ANGLE },
{ (ALchar *)"AL_PITCH", AL_PITCH },
{ (ALchar *)"AL_POSITION", AL_POSITION },
{ (ALchar *)"AL_DIRECTION", AL_DIRECTION },
{ (ALchar *)"AL_VELOCITY", AL_VELOCITY },
{ (ALchar *)"AL_LOOPING", AL_LOOPING },
{ (ALchar *)"AL_BUFFER", AL_BUFFER },
{ (ALchar *)"AL_GAIN", AL_GAIN },
{ (ALchar *)"AL_MIN_GAIN", AL_MIN_GAIN },
{ (ALchar *)"AL_MAX_GAIN", AL_MAX_GAIN },
{ (ALchar *)"AL_ORIENTATION", AL_ORIENTATION },
{ (ALchar *)"AL_REFERENCE_DISTANCE", AL_REFERENCE_DISTANCE },
{ (ALchar *)"AL_ROLLOFF_FACTOR", AL_ROLLOFF_FACTOR },
{ (ALchar *)"AL_CONE_OUTER_GAIN", AL_CONE_OUTER_GAIN },
{ (ALchar *)"AL_MAX_DISTANCE", AL_MAX_DISTANCE },
{ (ALchar *)"AL_SEC_OFFSET", AL_SEC_OFFSET },
{ (ALchar *)"AL_SAMPLE_OFFSET", AL_SAMPLE_OFFSET },
{ (ALchar *)"AL_BYTE_OFFSET", AL_BYTE_OFFSET },
{ (ALchar *)"AL_SOURCE_TYPE", AL_SOURCE_TYPE },
{ (ALchar *)"AL_STATIC", AL_STATIC },
{ (ALchar *)"AL_STREAMING", AL_STREAMING },
{ (ALchar *)"AL_UNDETERMINED", AL_UNDETERMINED },
{ (ALchar *)"AL_METERS_PER_UNIT", AL_METERS_PER_UNIT },
// Source EFX Properties
{ (ALchar *)"AL_DIRECT_FILTER", AL_DIRECT_FILTER },
{ (ALchar *)"AL_AUXILIARY_SEND_FILTER", AL_AUXILIARY_SEND_FILTER },
{ (ALchar *)"AL_AIR_ABSORPTION_FACTOR", AL_AIR_ABSORPTION_FACTOR },
{ (ALchar *)"AL_ROOM_ROLLOFF_FACTOR", AL_ROOM_ROLLOFF_FACTOR },
{ (ALchar *)"AL_CONE_OUTER_GAINHF", AL_CONE_OUTER_GAINHF },
{ (ALchar *)"AL_DIRECT_FILTER_GAINHF_AUTO", AL_DIRECT_FILTER_GAINHF_AUTO },
{ (ALchar *)"AL_AUXILIARY_SEND_FILTER_GAIN_AUTO", AL_AUXILIARY_SEND_FILTER_GAIN_AUTO },
{ (ALchar *)"AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO", AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO},
// Source State information
{ (ALchar *)"AL_SOURCE_STATE", AL_SOURCE_STATE },
{ (ALchar *)"AL_INITIAL", AL_INITIAL },
{ (ALchar *)"AL_PLAYING", AL_PLAYING },
{ (ALchar *)"AL_PAUSED", AL_PAUSED },
{ (ALchar *)"AL_STOPPED", AL_STOPPED },
// Queue information
{ (ALchar *)"AL_BUFFERS_QUEUED", AL_BUFFERS_QUEUED },
{ (ALchar *)"AL_BUFFERS_PROCESSED", AL_BUFFERS_PROCESSED },
// Buffer Formats
{ (ALchar *)"AL_FORMAT_MONO8", AL_FORMAT_MONO8 },
{ (ALchar *)"AL_FORMAT_MONO16", AL_FORMAT_MONO16 },
{ (ALchar *)"AL_FORMAT_MONO_FLOAT32", AL_FORMAT_MONO_FLOAT32 },
{ (ALchar *)"AL_FORMAT_STEREO8", AL_FORMAT_STEREO8 },
{ (ALchar *)"AL_FORMAT_STEREO16", AL_FORMAT_STEREO16 },
{ (ALchar *)"AL_FORMAT_STEREO_FLOAT32", AL_FORMAT_STEREO_FLOAT32 },
{ (ALchar *)"AL_FORMAT_MONO_IMA4", AL_FORMAT_MONO_IMA4 },
{ (ALchar *)"AL_FORMAT_STEREO_IMA4", AL_FORMAT_STEREO_IMA4 },
{ (ALchar *)"AL_FORMAT_QUAD8_LOKI", AL_FORMAT_QUAD8_LOKI },
{ (ALchar *)"AL_FORMAT_QUAD16_LOKI", AL_FORMAT_QUAD16_LOKI },
{ (ALchar *)"AL_FORMAT_QUAD8", AL_FORMAT_QUAD8 },
{ (ALchar *)"AL_FORMAT_QUAD16", AL_FORMAT_QUAD16 },
{ (ALchar *)"AL_FORMAT_QUAD32", AL_FORMAT_QUAD32 },
{ (ALchar *)"AL_FORMAT_51CHN8", AL_FORMAT_51CHN8 },
{ (ALchar *)"AL_FORMAT_51CHN16", AL_FORMAT_51CHN16 },
{ (ALchar *)"AL_FORMAT_51CHN32", AL_FORMAT_51CHN32 },
{ (ALchar *)"AL_FORMAT_61CHN8", AL_FORMAT_61CHN8 },
{ (ALchar *)"AL_FORMAT_61CHN16", AL_FORMAT_61CHN16 },
{ (ALchar *)"AL_FORMAT_61CHN32", AL_FORMAT_61CHN32 },
{ (ALchar *)"AL_FORMAT_71CHN8", AL_FORMAT_71CHN8 },
{ (ALchar *)"AL_FORMAT_71CHN16", AL_FORMAT_71CHN16 },
{ (ALchar *)"AL_FORMAT_71CHN32", AL_FORMAT_71CHN32 },
{ (ALchar *)"AL_FORMAT_REAR8", AL_FORMAT_REAR8 },
{ (ALchar *)"AL_FORMAT_REAR16", AL_FORMAT_REAR16 },
{ (ALchar *)"AL_FORMAT_REAR32", AL_FORMAT_REAR32 },
// Buffer attributes
{ (ALchar *)"AL_FREQUENCY", AL_FREQUENCY },
{ (ALchar *)"AL_BITS", AL_BITS },
{ (ALchar *)"AL_CHANNELS", AL_CHANNELS },
{ (ALchar *)"AL_SIZE", AL_SIZE },
// Buffer States (not supported yet)
{ (ALchar *)"AL_UNUSED", AL_UNUSED },
{ (ALchar *)"AL_PENDING", AL_PENDING },
{ (ALchar *)"AL_PROCESSED", AL_PROCESSED },
// AL Error Messages
{ (ALchar *)"AL_NO_ERROR", AL_NO_ERROR },
{ (ALchar *)"AL_INVALID_NAME", AL_INVALID_NAME },
{ (ALchar *)"AL_INVALID_ENUM", AL_INVALID_ENUM },
{ (ALchar *)"AL_INVALID_VALUE", AL_INVALID_VALUE },
{ (ALchar *)"AL_INVALID_OPERATION", AL_INVALID_OPERATION },
{ (ALchar *)"AL_OUT_OF_MEMORY", AL_OUT_OF_MEMORY },
// Context strings
{ (ALchar *)"AL_VENDOR", AL_VENDOR },
{ (ALchar *)"AL_VERSION", AL_VERSION },
{ (ALchar *)"AL_RENDERER", AL_RENDERER },
{ (ALchar *)"AL_EXTENSIONS", AL_EXTENSIONS },
// Global states
{ (ALchar *)"AL_DOPPLER_FACTOR", AL_DOPPLER_FACTOR },
{ (ALchar *)"AL_DOPPLER_VELOCITY", AL_DOPPLER_VELOCITY },
{ (ALchar *)"AL_DISTANCE_MODEL", AL_DISTANCE_MODEL },
{ (ALchar *)"AL_SPEED_OF_SOUND", AL_SPEED_OF_SOUND },
// Distance Models
{ (ALchar *)"AL_INVERSE_DISTANCE", AL_INVERSE_DISTANCE },
{ (ALchar *)"AL_INVERSE_DISTANCE_CLAMPED", AL_INVERSE_DISTANCE_CLAMPED },
{ (ALchar *)"AL_LINEAR_DISTANCE", AL_LINEAR_DISTANCE },
{ (ALchar *)"AL_LINEAR_DISTANCE_CLAMPED", AL_LINEAR_DISTANCE_CLAMPED },
{ (ALchar *)"AL_EXPONENT_DISTANCE", AL_EXPONENT_DISTANCE },
{ (ALchar *)"AL_EXPONENT_DISTANCE_CLAMPED", AL_EXPONENT_DISTANCE_CLAMPED },
// Filter types
{ (ALchar *)"AL_FILTER_TYPE", AL_FILTER_TYPE },
{ (ALchar *)"AL_FILTER_NULL", AL_FILTER_NULL },
{ (ALchar *)"AL_FILTER_LOWPASS", AL_FILTER_LOWPASS },
{ (ALchar *)"AL_FILTER_HIGHPASS", AL_FILTER_HIGHPASS },
{ (ALchar *)"AL_FILTER_BANDPASS", AL_FILTER_BANDPASS },
// Filter params
{ (ALchar *)"AL_LOWPASS_GAIN", AL_LOWPASS_GAIN },
{ (ALchar *)"AL_LOWPASS_GAINHF", AL_LOWPASS_GAINHF },
// Effect types
{ (ALchar *)"AL_EFFECT_TYPE", AL_EFFECT_TYPE },
{ (ALchar *)"AL_EFFECT_NULL", AL_EFFECT_NULL },
{ (ALchar *)"AL_EFFECT_REVERB", AL_EFFECT_REVERB },
{ (ALchar *)"AL_EFFECT_CHORUS", AL_EFFECT_CHORUS },
{ (ALchar *)"AL_EFFECT_DISTORTION", AL_EFFECT_DISTORTION },
{ (ALchar *)"AL_EFFECT_ECHO", AL_EFFECT_ECHO },
{ (ALchar *)"AL_EFFECT_FLANGER", AL_EFFECT_FLANGER },
{ (ALchar *)"AL_EFFECT_FREQUENCY_SHIFTER", AL_EFFECT_FREQUENCY_SHIFTER },
{ (ALchar *)"AL_EFFECT_VOCAL_MORPHER", AL_EFFECT_VOCAL_MORPHER },
{ (ALchar *)"AL_EFFECT_PITCH_SHIFTER", AL_EFFECT_PITCH_SHIFTER },
{ (ALchar *)"AL_EFFECT_RING_MODULATOR", AL_EFFECT_RING_MODULATOR },
{ (ALchar *)"AL_EFFECT_AUTOWAH", AL_EFFECT_AUTOWAH },
{ (ALchar *)"AL_EFFECT_COMPRESSOR", AL_EFFECT_COMPRESSOR },
{ (ALchar *)"AL_EFFECT_EQUALIZER", AL_EFFECT_EQUALIZER },
// Reverb params
{ (ALchar *)"AL_REVERB_DENSITY", AL_REVERB_DENSITY },
{ (ALchar *)"AL_REVERB_DIFFUSION", AL_REVERB_DIFFUSION },
{ (ALchar *)"AL_REVERB_GAIN", AL_REVERB_GAIN },
{ (ALchar *)"AL_REVERB_GAINHF", AL_REVERB_GAINHF },
{ (ALchar *)"AL_REVERB_DECAY_TIME", AL_REVERB_DECAY_TIME },
{ (ALchar *)"AL_REVERB_DECAY_HFRATIO", AL_REVERB_DECAY_HFRATIO },
{ (ALchar *)"AL_REVERB_REFLECTIONS_GAIN", AL_REVERB_REFLECTIONS_GAIN },
{ (ALchar *)"AL_REVERB_REFLECTIONS_DELAY", AL_REVERB_REFLECTIONS_DELAY },
{ (ALchar *)"AL_REVERB_LATE_REVERB_GAIN", AL_REVERB_LATE_REVERB_GAIN },
{ (ALchar *)"AL_REVERB_LATE_REVERB_DELAY", AL_REVERB_LATE_REVERB_DELAY },
{ (ALchar *)"AL_REVERB_AIR_ABSORPTION_GAINHF", AL_REVERB_AIR_ABSORPTION_GAINHF },
{ (ALchar *)"AL_REVERB_ROOM_ROLLOFF_FACTOR", AL_REVERB_ROOM_ROLLOFF_FACTOR },
{ (ALchar *)"AL_REVERB_DECAY_HFLIMIT", AL_REVERB_DECAY_HFLIMIT },
// Default
{ (ALchar *)NULL, (ALenum)0 }
const struct EffectList EffectList[] = {
{ "eaxreverb", EAXREVERB, "AL_EFFECT_EAXREVERB", AL_EFFECT_EAXREVERB },
{ "reverb", REVERB, "AL_EFFECT_REVERB", AL_EFFECT_REVERB },
#if 0
{ "autowah", AUTOWAH, "AL_EFFECT_AUTOWAH", AL_EFFECT_AUTOWAH },
#endif
{ "chorus", CHORUS, "AL_EFFECT_CHORUS", AL_EFFECT_CHORUS },
{ "compressor", COMPRESSOR, "AL_EFFECT_COMPRESSOR", AL_EFFECT_COMPRESSOR },
{ "distortion", DISTORTION, "AL_EFFECT_DISTORTION", AL_EFFECT_DISTORTION },
{ "echo", ECHO, "AL_EFFECT_ECHO", AL_EFFECT_ECHO },
{ "equalizer", EQUALIZER, "AL_EFFECT_EQUALIZER", AL_EFFECT_EQUALIZER },
{ "flanger", FLANGER, "AL_EFFECT_FLANGER", AL_EFFECT_FLANGER },
{ "modulator", MODULATOR, "AL_EFFECT_RING_MODULATOR", AL_EFFECT_RING_MODULATOR },
{ "dedicated", DEDICATED, "AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT", AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT },
{ "dedicated", DEDICATED, "AL_EFFECT_DEDICATED_DIALOGUE", AL_EFFECT_DEDICATED_DIALOGUE },
{ NULL, 0, NULL, (ALenum)0 }
};
ALAPI ALboolean ALAPIENTRY alIsExtensionPresent(const ALchar *extName)
AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extName)
{
ALboolean bIsSupported = AL_FALSE;
ALCcontext *pContext;
ALboolean ret = AL_FALSE;
ALCcontext *context;
const char *ptr;
size_t len;
if (!extName)
{
alSetError(AL_INVALID_VALUE);
return AL_FALSE;
}
context = GetContextRef();
if(!context) return AL_FALSE;
pContext = GetContextSuspended();
if(!pContext) return AL_FALSE;
if(!(extName))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
len = strlen(extName);
ptr = pContext->ExtensionList;
ptr = context->ExtensionList;
while(ptr && *ptr)
{
if(strncasecmp(ptr, extName, len) == 0 &&
(ptr[len] == '\0' || isspace(ptr[len])))
{
bIsSupported = AL_TRUE;
ret = AL_TRUE;
break;
}
if((ptr=strchr(ptr, ' ')) != NULL)
@@ -373,31 +85,22 @@ ALAPI ALboolean ALAPIENTRY alIsExtensionPresent(const ALchar *extName)
}
}
ProcessContext(pContext);
return bIsSupported;
done:
ALCcontext_DecRef(context);
return ret;
}
ALAPI ALvoid * ALAPIENTRY alGetProcAddress(const ALchar *funcName)
AL_API ALvoid* AL_APIENTRY alGetProcAddress(const ALchar *funcName)
{
ALsizei i = 0;
while(function[i].funcName &&
strcmp((char*)function[i].funcName, (char*)funcName) != 0)
i++;
return function[i].address;
if(!funcName)
return NULL;
return alcGetProcAddress(NULL, funcName);
}
/* NOTE: This function must be able to run without a context! */
ALAPI ALenum ALAPIENTRY alGetEnumValue(const ALchar *enumName)
AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *enumName)
{
ALsizei i = 0;
while(enumeration[i].enumName &&
strcmp(enumeration[i].enumName, enumName) != 0)
i++;
return enumeration[i].value;
if(!enumName)
return (ALenum)0;
return alcGetEnumValue(NULL, enumName);
}
+554 -278
View File
@@ -22,404 +22,680 @@
#include <stdlib.h>
#include "AL/al.h"
#include "AL/alc.h"
#include "alMain.h"
#include "alu.h"
#include "alFilter.h"
#include "alThunk.h"
#include "alError.h"
extern inline struct ALfilter *LookupFilter(ALCdevice *device, ALuint id);
extern inline struct ALfilter *RemoveFilter(ALCdevice *device, ALuint id);
extern inline ALfloat ALfilterState_processSingle(ALfilterState *filter, ALfloat sample);
static void InitFilterParams(ALfilter *filter, ALenum type);
ALvoid AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters)
AL_API ALvoid AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters)
{
ALCcontext *Context;
ALsizei i;
ALCdevice *device;
ALCcontext *context;
ALsizei cur = 0;
ALenum err;
Context = GetContextSuspended();
if(!Context) return;
context = GetContextRef();
if(!context) return;
if (n > 0)
if(!(n >= 0))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
device = context->Device;
for(cur = 0;cur < n;cur++)
{
ALCdevice *device = Context->Device;
// Check that enough memory has been allocted in the 'filters' array for n Filters
if (!IsBadWritePtr((void*)filters, n * sizeof(ALuint)))
ALfilter *filter = calloc(1, sizeof(ALfilter));
if(!filter)
{
ALfilter **list = &device->FilterList;
while(*list)
list = &(*list)->next;
i = 0;
while(i < n)
{
*list = calloc(1, sizeof(ALfilter));
if(!(*list))
{
// We must have run out or memory
alDeleteFilters(i, filters);
alSetError(AL_OUT_OF_MEMORY);
break;
}
filters[i] = (ALuint)ALTHUNK_ADDENTRY(*list);
(*list)->filter = filters[i];
InitFilterParams(*list, AL_FILTER_NULL);
device->FilterCount++;
i++;
list = &(*list)->next;
}
alDeleteFilters(cur, filters);
SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done);
}
InitFilterParams(filter, AL_FILTER_NULL);
err = NewThunkEntry(&filter->id);
if(err == AL_NO_ERROR)
err = InsertUIntMapEntry(&device->FilterMap, filter->id, filter);
if(err != AL_NO_ERROR)
{
FreeThunkEntry(filter->id);
memset(filter, 0, sizeof(ALfilter));
free(filter);
alDeleteFilters(cur, filters);
SET_ERROR_AND_GOTO(context, err, done);
}
filters[cur] = filter->id;
}
ProcessContext(Context);
done:
ALCcontext_DecRef(context);
}
ALvoid AL_APIENTRY alDeleteFilters(ALsizei n, ALuint *filters)
AL_API ALvoid AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters)
{
ALCcontext *Context;
ALfilter *ALFilter;
ALCdevice *device;
ALCcontext *context;
ALfilter *filter;
ALsizei i;
Context = GetContextSuspended();
if(!Context) return;
context = GetContextRef();
if(!context) return;
if (n >= 0)
if(!(n >= 0))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
device = context->Device;
for(i = 0;i < n;i++)
{
ALCdevice *device = Context->Device;
// Check that all filters are valid
for (i = 0; i < n; i++)
{
if (!alIsFilter(filters[i]))
{
alSetError(AL_INVALID_NAME);
break;
}
}
if (i == n)
{
// All filters are valid
for (i = 0; i < n; i++)
{
// Recheck that the filter is valid, because there could be duplicated names
if (filters[i] && alIsFilter(filters[i]))
{
ALfilter **list;
ALFilter = ((ALfilter*)ALTHUNK_LOOKUPENTRY(filters[i]));
// Remove Source from list of Sources
list = &device->FilterList;
while(*list && *list != ALFilter)
list = &(*list)->next;
if(*list)
*list = (*list)->next;
ALTHUNK_REMOVEENTRY(ALFilter->filter);
memset(ALFilter, 0, sizeof(ALfilter));
free(ALFilter);
device->FilterCount--;
}
}
}
if(filters[i] && LookupFilter(device, filters[i]) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
}
else
alSetError(AL_INVALID_VALUE);
for(i = 0;i < n;i++)
{
if((filter=RemoveFilter(device, filters[i])) == NULL)
continue;
FreeThunkEntry(filter->id);
ProcessContext(Context);
memset(filter, 0, sizeof(*filter));
free(filter);
}
done:
ALCcontext_DecRef(context);
}
ALboolean AL_APIENTRY alIsFilter(ALuint filter)
AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter)
{
ALCcontext *Context;
ALfilter *list;
ALboolean result;
Context = GetContextSuspended();
Context = GetContextRef();
if(!Context) return AL_FALSE;
list = Context->Device->FilterList;
while(list && list->filter != filter)
list = list->next;
result = ((!filter || LookupFilter(Context->Device, filter)) ?
AL_TRUE : AL_FALSE);
ProcessContext(Context);
ALCcontext_DecRef(Context);
return ((list || !filter) ? AL_TRUE : AL_FALSE);
return result;
}
ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint iValue)
AL_API ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint value)
{
ALCcontext *Context;
ALCdevice *Device;
ALfilter *ALFilter;
Context = GetContextSuspended();
Context = GetContextRef();
if(!Context) return;
if (filter && alIsFilter(filter))
Device = Context->Device;
if((ALFilter=LookupFilter(Device, filter)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
{
ALfilter *ALFilter = (ALfilter*)ALTHUNK_LOOKUPENTRY(filter);
switch(param)
if(param == AL_FILTER_TYPE)
{
case AL_FILTER_TYPE:
if(iValue == AL_FILTER_NULL ||
iValue == AL_FILTER_LOWPASS)
InitFilterParams(ALFilter, iValue);
if(value == AL_FILTER_NULL || value == AL_FILTER_LOWPASS ||
value == AL_FILTER_HIGHPASS || value == AL_FILTER_BANDPASS)
InitFilterParams(ALFilter, value);
else
alSetError(AL_INVALID_VALUE);
break;
default:
alSetError(AL_INVALID_ENUM);
break;
alSetError(Context, AL_INVALID_VALUE);
}
else
{
/* Call the appropriate handler */
ALfilter_SetParami(ALFilter, Context, param, value);
}
}
else
alSetError(AL_INVALID_NAME);
ProcessContext(Context);
ALCcontext_DecRef(Context);
}
ALvoid AL_APIENTRY alFilteriv(ALuint filter, ALenum param, ALint *piValues)
AL_API ALvoid AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *values)
{
ALCcontext *Context;
ALCdevice *Device;
ALfilter *ALFilter;
Context = GetContextSuspended();
if(!Context) return;
if (filter && alIsFilter(filter))
switch(param)
{
switch(param)
{
case AL_FILTER_TYPE:
alFilteri(filter, param, piValues[0]);
break;
default:
alSetError(AL_INVALID_ENUM);
break;
}
alFilteri(filter, param, values[0]);
return;
}
else
alSetError(AL_INVALID_NAME);
ProcessContext(Context);
}
ALvoid AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat flValue)
{
ALCcontext *Context;
Context = GetContextSuspended();
Context = GetContextRef();
if(!Context) return;
if (filter && alIsFilter(filter))
{
ALfilter *ALFilter = (ALfilter*)ALTHUNK_LOOKUPENTRY(filter);
switch(ALFilter->type)
{
case AL_FILTER_LOWPASS:
switch(param)
{
case AL_LOWPASS_GAIN:
if(flValue >= 0.0f && flValue <= 1.0f)
ALFilter->Gain = flValue;
else
alSetError(AL_INVALID_VALUE);
break;
case AL_LOWPASS_GAINHF:
if(flValue >= 0.0f && flValue <= 1.0f)
ALFilter->GainHF = flValue;
else
alSetError(AL_INVALID_VALUE);
break;
default:
alSetError(AL_INVALID_ENUM);
break;
}
break;
default:
alSetError(AL_INVALID_ENUM);
break;
}
}
Device = Context->Device;
if((ALFilter=LookupFilter(Device, filter)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
alSetError(AL_INVALID_NAME);
{
/* Call the appropriate handler */
ALfilter_SetParamiv(ALFilter, Context, param, values);
}
ProcessContext(Context);
ALCcontext_DecRef(Context);
}
ALvoid AL_APIENTRY alFilterfv(ALuint filter, ALenum param, ALfloat *pflValues)
AL_API ALvoid AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat value)
{
ALCcontext *Context;
ALCdevice *Device;
ALfilter *ALFilter;
Context = GetContextSuspended();
Context = GetContextRef();
if(!Context) return;
if (filter && alIsFilter(filter))
{
switch(param)
{
default:
alFilterf(filter, param, pflValues[0]);
break;
}
}
Device = Context->Device;
if((ALFilter=LookupFilter(Device, filter)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
alSetError(AL_INVALID_NAME);
{
/* Call the appropriate handler */
ALfilter_SetParamf(ALFilter, Context, param, value);
}
ProcessContext(Context);
ALCcontext_DecRef(Context);
}
ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *piValue)
AL_API ALvoid AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat *values)
{
ALCcontext *Context;
ALCdevice *Device;
ALfilter *ALFilter;
Context = GetContextSuspended();
Context = GetContextRef();
if(!Context) return;
if (filter && alIsFilter(filter))
Device = Context->Device;
if((ALFilter=LookupFilter(Device, filter)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
{
ALfilter *ALFilter = (ALfilter*)ALTHUNK_LOOKUPENTRY(filter);
/* Call the appropriate handler */
ALfilter_SetParamfv(ALFilter, Context, param, values);
}
switch(param)
ALCcontext_DecRef(Context);
}
AL_API ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *value)
{
ALCcontext *Context;
ALCdevice *Device;
ALfilter *ALFilter;
Context = GetContextRef();
if(!Context) return;
Device = Context->Device;
if((ALFilter=LookupFilter(Device, filter)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
{
if(param == AL_FILTER_TYPE)
*value = ALFilter->type;
else
{
/* Call the appropriate handler */
ALfilter_GetParami(ALFilter, Context, param, value);
}
}
ALCcontext_DecRef(Context);
}
AL_API ALvoid AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *values)
{
ALCcontext *Context;
ALCdevice *Device;
ALfilter *ALFilter;
switch(param)
{
case AL_FILTER_TYPE:
*piValue = ALFilter->type;
break;
default:
alSetError(AL_INVALID_ENUM);
break;
}
alGetFilteri(filter, param, values);
return;
}
else
alSetError(AL_INVALID_NAME);
ProcessContext(Context);
}
ALvoid AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *piValues)
{
ALCcontext *Context;
Context = GetContextSuspended();
Context = GetContextRef();
if(!Context) return;
if (filter && alIsFilter(filter))
{
switch(param)
{
case AL_FILTER_TYPE:
alGetFilteri(filter, param, piValues);
break;
default:
alSetError(AL_INVALID_ENUM);
break;
}
}
Device = Context->Device;
if((ALFilter=LookupFilter(Device, filter)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
alSetError(AL_INVALID_NAME);
{
/* Call the appropriate handler */
ALfilter_GetParamiv(ALFilter, Context, param, values);
}
ProcessContext(Context);
ALCcontext_DecRef(Context);
}
ALvoid AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *pflValue)
AL_API ALvoid AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *value)
{
ALCcontext *Context;
ALCdevice *Device;
ALfilter *ALFilter;
Context = GetContextSuspended();
Context = GetContextRef();
if(!Context) return;
if (filter && alIsFilter(filter))
{
ALfilter *ALFilter = (ALfilter*)ALTHUNK_LOOKUPENTRY(filter);
switch(ALFilter->type)
{
case AL_FILTER_LOWPASS:
switch(param)
{
case AL_LOWPASS_GAIN:
*pflValue = ALFilter->Gain;
break;
case AL_LOWPASS_GAINHF:
*pflValue = ALFilter->GainHF;
break;
default:
alSetError(AL_INVALID_ENUM);
break;
}
break;
default:
alSetError(AL_INVALID_ENUM);
break;
}
}
Device = Context->Device;
if((ALFilter=LookupFilter(Device, filter)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
alSetError(AL_INVALID_NAME);
{
/* Call the appropriate handler */
ALfilter_GetParamf(ALFilter, Context, param, value);
}
ProcessContext(Context);
ALCcontext_DecRef(Context);
}
ALvoid AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *pflValues)
AL_API ALvoid AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *values)
{
ALCcontext *Context;
ALCdevice *Device;
ALfilter *ALFilter;
Context = GetContextSuspended();
Context = GetContextRef();
if(!Context) return;
if (filter && alIsFilter(filter))
{
switch(param)
{
default:
alGetFilterf(filter, param, pflValues);
break;
}
}
Device = Context->Device;
if((ALFilter=LookupFilter(Device, filter)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
alSetError(AL_INVALID_NAME);
{
/* Call the appropriate handler */
ALfilter_GetParamfv(ALFilter, Context, param, values);
}
ProcessContext(Context);
ALCcontext_DecRef(Context);
}
void ALfilterState_clear(ALfilterState *filter)
{
filter->x[0] = 0.0f;
filter->x[1] = 0.0f;
filter->y[0] = 0.0f;
filter->y[1] = 0.0f;
}
void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat gain, ALfloat freq_mult, ALfloat bandwidth)
{
ALfloat alpha;
ALfloat w0;
// Limit gain to -100dB
gain = maxf(gain, 0.00001f);
w0 = F_2PI * freq_mult;
/* Calculate filter coefficients depending on filter type */
switch(type)
{
case ALfilterType_HighShelf:
alpha = sinf(w0)/2.0f*sqrtf((gain + 1.0f/gain)*(1.0f/0.75f - 1.0f) + 2.0f);
filter->b[0] = gain*((gain+1.0f) + (gain-1.0f)*cosf(w0) + 2.0f*sqrtf(gain)*alpha);
filter->b[1] = -2.0f*gain*((gain-1.0f) + (gain+1.0f)*cosf(w0) );
filter->b[2] = gain*((gain+1.0f) + (gain-1.0f)*cosf(w0) - 2.0f*sqrtf(gain)*alpha);
filter->a[0] = (gain+1.0f) - (gain-1.0f)*cosf(w0) + 2.0f*sqrtf(gain)*alpha;
filter->a[1] = 2.0f* ((gain-1.0f) - (gain+1.0f)*cosf(w0) );
filter->a[2] = (gain+1.0f) - (gain-1.0f)*cosf(w0) - 2.0f*sqrtf(gain)*alpha;
break;
case ALfilterType_LowShelf:
alpha = sinf(w0)/2.0f*sqrtf((gain + 1.0f/gain)*(1.0f/0.75f - 1.0f) + 2.0f);
filter->b[0] = gain*((gain+1.0f) - (gain-1.0f)*cosf(w0) + 2.0f*sqrtf(gain)*alpha);
filter->b[1] = 2.0f*gain*((gain-1.0f) - (gain+1.0f)*cosf(w0) );
filter->b[2] = gain*((gain+1.0f) - (gain-1.0f)*cosf(w0) - 2.0f*sqrtf(gain)*alpha);
filter->a[0] = (gain+1.0f) + (gain-1.0f)*cosf(w0) + 2.0f*sqrtf(gain)*alpha;
filter->a[1] = -2.0f* ((gain-1.0f) + (gain+1.0f)*cosf(w0) );
filter->a[2] = (gain+1.0f) + (gain-1.0f)*cosf(w0) - 2.0f*sqrtf(gain)*alpha;
break;
case ALfilterType_Peaking:
alpha = sinf(w0) * sinhf(logf(2.0f) / 2.0f * bandwidth * w0 / sinf(w0));
filter->b[0] = 1.0f + alpha * gain;
filter->b[1] = -2.0f * cosf(w0);
filter->b[2] = 1.0f - alpha * gain;
filter->a[0] = 1.0f + alpha / gain;
filter->a[1] = -2.0f * cosf(w0);
filter->a[2] = 1.0f - alpha / gain;
break;
case ALfilterType_LowPass:
alpha = sinf(w0) * sinhf(logf(2.0f) / 2.0f * bandwidth * w0 / sinf(w0));
filter->b[0] = (1.0f - cosf(w0)) / 2.0f;
filter->b[1] = 1.0f - cosf(w0);
filter->b[2] = (1.0f - cosf(w0)) / 2.0f;
filter->a[0] = 1.0f + alpha;
filter->a[1] = -2.0f * cosf(w0);
filter->a[2] = 1.0f - alpha;
break;
case ALfilterType_HighPass:
alpha = sinf(w0) * sinhf(logf(2.0f) / 2.0f * bandwidth * w0 / sinf(w0));
filter->b[0] = (1.0f + cosf(w0)) / 2.0f;
filter->b[1] = 1.0f + cosf(w0);
filter->b[2] = (1.0f + cosf(w0)) / 2.0f;
filter->a[0] = 1.0f + alpha;
filter->a[1] = -2.0f * cosf(w0);
filter->a[2] = 1.0f - alpha;
break;
case ALfilterType_BandPass:
alpha = sinf(w0) * sinhf(logf(2.0f) / 2.0f * bandwidth * w0 / sinf(w0));
filter->b[0] = alpha;
filter->b[1] = 0;
filter->b[2] = -alpha;
filter->a[0] = 1.0f + alpha;
filter->a[1] = -2.0f * cosf(w0);
filter->a[2] = 1.0f - alpha;
break;
}
filter->b[2] /= filter->a[0];
filter->b[1] /= filter->a[0];
filter->b[0] /= filter->a[0];
filter->a[2] /= filter->a[0];
filter->a[1] /= filter->a[0];
filter->a[0] /= filter->a[0];
filter->process = ALfilterState_processC;
}
static void lp_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void lp_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void lp_SetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val)
{
switch(param)
{
case AL_LOWPASS_GAIN:
if(!(val >= AL_LOWPASS_MIN_GAIN && val <= AL_LOWPASS_MAX_GAIN))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
filter->Gain = val;
break;
case AL_LOWPASS_GAINHF:
if(!(val >= AL_LOWPASS_MIN_GAINHF && val <= AL_LOWPASS_MAX_GAINHF))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
filter->GainHF = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
static void lp_SetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals)
{
lp_SetParamf(filter, context, param, vals[0]);
}
static void lp_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void lp_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void lp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val)
{
switch(param)
{
case AL_LOWPASS_GAIN:
*val = filter->Gain;
break;
case AL_LOWPASS_GAINHF:
*val = filter->GainHF;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
static void lp_GetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals)
{
lp_GetParamf(filter, context, param, vals);
}
static void hp_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void hp_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void hp_SetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val)
{
switch(param)
{
case AL_HIGHPASS_GAIN:
if(!(val >= AL_HIGHPASS_MIN_GAIN && val <= AL_HIGHPASS_MAX_GAIN))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
filter->Gain = val;
break;
case AL_HIGHPASS_GAINLF:
if(!(val >= AL_HIGHPASS_MIN_GAINLF && val <= AL_HIGHPASS_MAX_GAINLF))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
filter->GainLF = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
static void hp_SetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals)
{
hp_SetParamf(filter, context, param, vals[0]);
}
static void hp_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void hp_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void hp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val)
{
switch(param)
{
case AL_HIGHPASS_GAIN:
*val = filter->Gain;
break;
case AL_HIGHPASS_GAINLF:
*val = filter->GainLF;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
static void hp_GetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals)
{
hp_GetParamf(filter, context, param, vals);
}
static void bp_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void bp_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void bp_SetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val)
{
switch(param)
{
case AL_BANDPASS_GAIN:
if(!(val >= AL_BANDPASS_MIN_GAIN && val <= AL_BANDPASS_MAX_GAIN))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
filter->Gain = val;
break;
case AL_BANDPASS_GAINHF:
if(!(val >= AL_BANDPASS_MIN_GAINHF && val <= AL_BANDPASS_MAX_GAINHF))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
filter->GainHF = val;
break;
case AL_BANDPASS_GAINLF:
if(!(val >= AL_BANDPASS_MIN_GAINLF && val <= AL_BANDPASS_MAX_GAINLF))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
filter->GainLF = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
static void bp_SetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals)
{
bp_SetParamf(filter, context, param, vals[0]);
}
static void bp_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void bp_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void bp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val)
{
switch(param)
{
case AL_BANDPASS_GAIN:
*val = filter->Gain;
break;
case AL_BANDPASS_GAINHF:
*val = filter->GainHF;
break;
case AL_BANDPASS_GAINLF:
*val = filter->GainLF;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
static void bp_GetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals)
{
bp_GetParamf(filter, context, param, vals);
}
static void null_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void null_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void null_SetParamf(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALfloat UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void null_SetParamfv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALfloat *UNUSED(vals))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void null_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void null_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void null_GetParamf(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALfloat *UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void null_GetParamfv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALfloat *UNUSED(vals))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
ALvoid ReleaseALFilters(ALCdevice *device)
{
ALfilter *list = device->FilterList;
while(list)
ALsizei i;
for(i = 0;i < device->FilterMap.size;i++)
{
ALfilter *temp = list;
list = list->next;
ALfilter *temp = device->FilterMap.array[i].value;
device->FilterMap.array[i].value = NULL;
// Release filter structure
FreeThunkEntry(temp->id);
memset(temp, 0, sizeof(ALfilter));
free(temp);
}
device->FilterList = NULL;
device->FilterCount = 0;
}
static void InitFilterParams(ALfilter *filter, ALenum type)
{
filter->type = type;
if(type == AL_FILTER_LOWPASS)
{
filter->Gain = AL_LOWPASS_DEFAULT_GAIN;
filter->GainHF = AL_LOWPASS_DEFAULT_GAINHF;
filter->HFReference = LOWPASSFREQREF;
filter->GainLF = 1.0f;
filter->LFReference = HIGHPASSFREQREF;
filter->Gain = 1.0;
filter->GainHF = 1.0;
filter->SetParami = lp_SetParami;
filter->SetParamiv = lp_SetParamiv;
filter->SetParamf = lp_SetParamf;
filter->SetParamfv = lp_SetParamfv;
filter->GetParami = lp_GetParami;
filter->GetParamiv = lp_GetParamiv;
filter->GetParamf = lp_GetParamf;
filter->GetParamfv = lp_GetParamfv;
}
else if(type == AL_FILTER_HIGHPASS)
{
filter->Gain = AL_HIGHPASS_DEFAULT_GAIN;
filter->GainHF = 1.0f;
filter->HFReference = LOWPASSFREQREF;
filter->GainLF = AL_HIGHPASS_DEFAULT_GAINLF;
filter->LFReference = HIGHPASSFREQREF;
filter->SetParami = hp_SetParami;
filter->SetParamiv = hp_SetParamiv;
filter->SetParamf = hp_SetParamf;
filter->SetParamfv = hp_SetParamfv;
filter->GetParami = hp_GetParami;
filter->GetParamiv = hp_GetParamiv;
filter->GetParamf = hp_GetParamf;
filter->GetParamfv = hp_GetParamfv;
}
else if(type == AL_FILTER_BANDPASS)
{
filter->Gain = AL_BANDPASS_DEFAULT_GAIN;
filter->GainHF = AL_BANDPASS_DEFAULT_GAINHF;
filter->HFReference = LOWPASSFREQREF;
filter->GainLF = AL_BANDPASS_DEFAULT_GAINLF;
filter->LFReference = HIGHPASSFREQREF;
filter->SetParami = bp_SetParami;
filter->SetParamiv = bp_SetParamiv;
filter->SetParamf = bp_SetParamf;
filter->SetParamfv = bp_SetParamfv;
filter->GetParami = bp_GetParami;
filter->GetParamiv = bp_GetParamiv;
filter->GetParamf = bp_GetParamf;
filter->GetParamfv = bp_GetParamfv;
}
else
{
filter->Gain = 1.0f;
filter->GainHF = 1.0f;
filter->HFReference = LOWPASSFREQREF;
filter->GainLF = 1.0f;
filter->LFReference = HIGHPASSFREQREF;
filter->SetParami = null_SetParami;
filter->SetParamiv = null_SetParamiv;
filter->SetParamf = null_SetParamf;
filter->SetParamfv = null_SetParamfv;
filter->GetParami = null_GetParami;
filter->GetParamiv = null_GetParamiv;
filter->GetParamf = null_GetParamf;
filter->GetParamfv = null_GetParamfv;
}
filter->type = type;
}
File diff suppressed because it is too large Load Diff
+353 -360
View File
@@ -24,430 +24,423 @@
#include "AL/alc.h"
#include "alError.h"
#include "alListener.h"
#include "alSource.h"
ALAPI ALvoid ALAPIENTRY alListenerf(ALenum eParam, ALfloat flValue)
AL_API ALvoid AL_APIENTRY alListenerf(ALenum param, ALfloat value)
{
ALCcontext *pContext;
ALCcontext *context;
pContext = GetContextSuspended();
if(!pContext) return;
context = GetContextRef();
if(!context) return;
switch(eParam)
switch(param)
{
case AL_GAIN:
if(!(value >= 0.0f && isfinite(value)))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
context->Listener->Gain = value;
ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
break;
case AL_METERS_PER_UNIT:
if(!(value >= 0.0f && isfinite(value)))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
context->Listener->MetersPerUnit = value;
ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3)
{
ALCcontext *context;
context = GetContextRef();
if(!context) return;
switch(param)
{
case AL_POSITION:
if(!(isfinite(value1) && isfinite(value2) && isfinite(value3)))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
LockContext(context);
context->Listener->Position[0] = value1;
context->Listener->Position[1] = value2;
context->Listener->Position[2] = value3;
ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
UnlockContext(context);
break;
case AL_VELOCITY:
if(!(isfinite(value1) && isfinite(value2) && isfinite(value3)))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
LockContext(context);
context->Listener->Velocity[0] = value1;
context->Listener->Velocity[1] = value2;
context->Listener->Velocity[2] = value3;
ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
UnlockContext(context);
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values)
{
ALCcontext *context;
if(values)
{
switch(param)
{
case AL_GAIN:
if(flValue >= 0.0f)
pContext->Listener.Gain = flValue;
else
alSetError(AL_INVALID_VALUE);
break;
case AL_METERS_PER_UNIT:
if(flValue > 0.0f)
pContext->Listener.MetersPerUnit = flValue;
else
alSetError(AL_INVALID_VALUE);
break;
alListenerf(param, values[0]);
return;
default:
alSetError(AL_INVALID_ENUM);
break;
}
ProcessContext(pContext);
}
ALAPI ALvoid ALAPIENTRY alListener3f(ALenum eParam, ALfloat flValue1, ALfloat flValue2, ALfloat flValue3)
{
ALCcontext *pContext;
pContext = GetContextSuspended();
if(!pContext) return;
switch(eParam)
{
case AL_POSITION:
pContext->Listener.Position[0] = flValue1;
pContext->Listener.Position[1] = flValue2;
pContext->Listener.Position[2] = flValue3;
break;
case AL_VELOCITY:
pContext->Listener.Velocity[0] = flValue1;
pContext->Listener.Velocity[1] = flValue2;
pContext->Listener.Velocity[2] = flValue3;
break;
default:
alSetError(AL_INVALID_ENUM);
break;
}
ProcessContext(pContext);
}
ALAPI ALvoid ALAPIENTRY alListenerfv(ALenum eParam, const ALfloat *pflValues)
{
ALCcontext *pContext;
pContext = GetContextSuspended();
if(!pContext) return;
if(pflValues)
{
switch(eParam)
{
case AL_GAIN:
if(pflValues[0] >= 0.0f)
pContext->Listener.Gain = pflValues[0];
else
alSetError(AL_INVALID_VALUE);
break;
case AL_METERS_PER_UNIT:
if(pflValues[0] > 0.0f)
pContext->Listener.MetersPerUnit = pflValues[0];
else
alSetError(AL_INVALID_VALUE);
break;
case AL_POSITION:
pContext->Listener.Position[0] = pflValues[0];
pContext->Listener.Position[1] = pflValues[1];
pContext->Listener.Position[2] = pflValues[2];
break;
case AL_VELOCITY:
pContext->Listener.Velocity[0] = pflValues[0];
pContext->Listener.Velocity[1] = pflValues[1];
pContext->Listener.Velocity[2] = pflValues[2];
break;
case AL_ORIENTATION:
// AT then UP
pContext->Listener.Forward[0] = pflValues[0];
pContext->Listener.Forward[1] = pflValues[1];
pContext->Listener.Forward[2] = pflValues[2];
pContext->Listener.Up[0] = pflValues[3];
pContext->Listener.Up[1] = pflValues[4];
pContext->Listener.Up[2] = pflValues[5];
break;
default:
alSetError(AL_INVALID_ENUM);
break;
}
}
else
alSetError(AL_INVALID_VALUE);
ProcessContext(pContext);
}
ALAPI ALvoid ALAPIENTRY alListeneri(ALenum eParam, ALint lValue)
{
ALCcontext *pContext;
(void)lValue;
pContext = GetContextSuspended();
if(!pContext) return;
switch(eParam)
{
default:
alSetError(AL_INVALID_ENUM);
break;
}
ProcessContext(pContext);
}
ALAPI void ALAPIENTRY alListener3i(ALenum eParam, ALint lValue1, ALint lValue2, ALint lValue3)
{
ALCcontext *pContext;
pContext = GetContextSuspended();
if(!pContext) return;
switch(eParam)
{
case AL_POSITION:
case AL_VELOCITY:
alListener3f(eParam, (ALfloat)lValue1, (ALfloat)lValue2, (ALfloat)lValue3);
break;
default:
alSetError(AL_INVALID_ENUM);
break;
}
ProcessContext(pContext);
}
ALAPI void ALAPIENTRY alListeneriv( ALenum eParam, const ALint* plValues )
{
ALCcontext *pContext;
ALfloat flValues[6];
pContext = GetContextSuspended();
if(!pContext) return;
if(plValues)
{
switch(eParam)
{
case AL_POSITION:
case AL_VELOCITY:
flValues[0] = (ALfloat)plValues[0];
flValues[1] = (ALfloat)plValues[1];
flValues[2] = (ALfloat)plValues[2];
alListenerfv(eParam, flValues);
break;
case AL_ORIENTATION:
flValues[0] = (ALfloat)plValues[0];
flValues[1] = (ALfloat)plValues[1];
flValues[2] = (ALfloat)plValues[2];
flValues[3] = (ALfloat)plValues[3];
flValues[4] = (ALfloat)plValues[4];
flValues[5] = (ALfloat)plValues[5];
alListenerfv(eParam, flValues);
break;
default:
alSetError(AL_INVALID_ENUM);
break;
alListener3f(param, values[0], values[1], values[2]);
return;
}
}
else
alSetError(AL_INVALID_VALUE);
ProcessContext(pContext);
context = GetContextRef();
if(!context) return;
if(!(values))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
switch(param)
{
case AL_ORIENTATION:
if(!(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2]) &&
isfinite(values[3]) && isfinite(values[4]) && isfinite(values[5])))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
LockContext(context);
/* AT then UP */
context->Listener->Forward[0] = values[0];
context->Listener->Forward[1] = values[1];
context->Listener->Forward[2] = values[2];
context->Listener->Up[0] = values[3];
context->Listener->Up[1] = values[4];
context->Listener->Up[2] = values[5];
ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
UnlockContext(context);
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
ALAPI ALvoid ALAPIENTRY alGetListenerf(ALenum eParam, ALfloat *pflValue)
AL_API ALvoid AL_APIENTRY alListeneri(ALenum param, ALint UNUSED(value))
{
ALCcontext *pContext;
ALCcontext *context;
pContext = GetContextSuspended();
if(!pContext) return;
context = GetContextRef();
if(!context) return;
if(pflValue)
switch(param)
{
switch(eParam)
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3)
{
ALCcontext *context;
switch(param)
{
case AL_POSITION:
case AL_VELOCITY:
alListener3f(param, (ALfloat)value1, (ALfloat)value2, (ALfloat)value3);
return;
}
context = GetContextRef();
if(!context) return;
switch(param)
{
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values)
{
ALCcontext *context;
if(values)
{
ALfloat fvals[6];
switch(param)
{
case AL_GAIN:
*pflValue = pContext->Listener.Gain;
break;
case AL_POSITION:
case AL_VELOCITY:
alListener3f(param, (ALfloat)values[0], (ALfloat)values[1], (ALfloat)values[2]);
return;
case AL_METERS_PER_UNIT:
*pflValue = pContext->Listener.MetersPerUnit;
break;
default:
alSetError(AL_INVALID_ENUM);
break;
case AL_ORIENTATION:
fvals[0] = (ALfloat)values[0];
fvals[1] = (ALfloat)values[1];
fvals[2] = (ALfloat)values[2];
fvals[3] = (ALfloat)values[3];
fvals[4] = (ALfloat)values[4];
fvals[5] = (ALfloat)values[5];
alListenerfv(param, fvals);
return;
}
}
else
alSetError(AL_INVALID_VALUE);
ProcessContext(pContext);
}
context = GetContextRef();
if(!context) return;
ALAPI ALvoid ALAPIENTRY alGetListener3f(ALenum eParam, ALfloat *pflValue1, ALfloat *pflValue2, ALfloat *pflValue3)
{
ALCcontext *pContext;
pContext = GetContextSuspended();
if(!pContext) return;
if(pflValue1 && pflValue2 && pflValue3)
if(!(values))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
switch(param)
{
switch(eParam)
{
case AL_POSITION:
*pflValue1 = pContext->Listener.Position[0];
*pflValue2 = pContext->Listener.Position[1];
*pflValue3 = pContext->Listener.Position[2];
break;
case AL_VELOCITY:
*pflValue1 = pContext->Listener.Velocity[0];
*pflValue2 = pContext->Listener.Velocity[1];
*pflValue3 = pContext->Listener.Velocity[2];
break;
default:
alSetError(AL_INVALID_ENUM);
break;
}
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
else
alSetError(AL_INVALID_VALUE);
ProcessContext(pContext);
done:
ALCcontext_DecRef(context);
}
ALAPI ALvoid ALAPIENTRY alGetListenerfv(ALenum eParam, ALfloat *pflValues)
AL_API ALvoid AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value)
{
ALCcontext *pContext;
ALCcontext *context;
pContext = GetContextSuspended();
if(!pContext) return;
context = GetContextRef();
if(!context) return;
if(pflValues)
if(!(value))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
switch(param)
{
switch(eParam)
{
case AL_GAIN:
pflValues[0] = pContext->Listener.Gain;
break;
case AL_GAIN:
*value = context->Listener->Gain;
break;
case AL_METERS_PER_UNIT:
pflValues[0] = pContext->Listener.MetersPerUnit;
break;
case AL_METERS_PER_UNIT:
*value = context->Listener->MetersPerUnit;
break;
case AL_POSITION:
pflValues[0] = pContext->Listener.Position[0];
pflValues[1] = pContext->Listener.Position[1];
pflValues[2] = pContext->Listener.Position[2];
break;
case AL_VELOCITY:
pflValues[0] = pContext->Listener.Velocity[0];
pflValues[1] = pContext->Listener.Velocity[1];
pflValues[2] = pContext->Listener.Velocity[2];
break;
case AL_ORIENTATION:
// AT then UP
pflValues[0] = pContext->Listener.Forward[0];
pflValues[1] = pContext->Listener.Forward[1];
pflValues[2] = pContext->Listener.Forward[2];
pflValues[3] = pContext->Listener.Up[0];
pflValues[4] = pContext->Listener.Up[1];
pflValues[5] = pContext->Listener.Up[2];
break;
default:
alSetError(AL_INVALID_ENUM);
break;
}
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
else
alSetError(AL_INVALID_VALUE);
ProcessContext(pContext);
done:
ALCcontext_DecRef(context);
}
ALAPI ALvoid ALAPIENTRY alGetListeneri(ALenum eParam, ALint *plValue)
AL_API ALvoid AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
{
ALCcontext *pContext;
ALCcontext *context;
pContext = GetContextSuspended();
if(!pContext) return;
context = GetContextRef();
if(!context) return;
if(plValue)
if(!(value1 && value2 && value3))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
switch(param)
{
switch(eParam)
{
default:
alSetError(AL_INVALID_ENUM);
break;
}
}
else
alSetError(AL_INVALID_VALUE);
case AL_POSITION:
LockContext(context);
*value1 = context->Listener->Position[0];
*value2 = context->Listener->Position[1];
*value3 = context->Listener->Position[2];
UnlockContext(context);
break;
ProcessContext(pContext);
case AL_VELOCITY:
LockContext(context);
*value1 = context->Listener->Velocity[0];
*value2 = context->Listener->Velocity[1];
*value3 = context->Listener->Velocity[2];
UnlockContext(context);
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
ALAPI void ALAPIENTRY alGetListener3i(ALenum eParam, ALint *plValue1, ALint *plValue2, ALint *plValue3)
AL_API ALvoid AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values)
{
ALCcontext *pContext;
ALCcontext *context;
pContext = GetContextSuspended();
if(!pContext) return;
if(plValue1 && plValue2 && plValue3)
switch(param)
{
switch (eParam)
{
case AL_POSITION:
*plValue1 = (ALint)pContext->Listener.Position[0];
*plValue2 = (ALint)pContext->Listener.Position[1];
*plValue3 = (ALint)pContext->Listener.Position[2];
break;
case AL_GAIN:
case AL_METERS_PER_UNIT:
alGetListenerf(param, values);
return;
case AL_VELOCITY:
*plValue1 = (ALint)pContext->Listener.Velocity[0];
*plValue2 = (ALint)pContext->Listener.Velocity[1];
*plValue3 = (ALint)pContext->Listener.Velocity[2];
break;
default:
alSetError(AL_INVALID_ENUM);
break;
}
case AL_POSITION:
case AL_VELOCITY:
alGetListener3f(param, values+0, values+1, values+2);
return;
}
else
alSetError(AL_INVALID_VALUE);
ProcessContext(pContext);
context = GetContextRef();
if(!context) return;
if(!(values))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
switch(param)
{
case AL_ORIENTATION:
LockContext(context);
// AT then UP
values[0] = context->Listener->Forward[0];
values[1] = context->Listener->Forward[1];
values[2] = context->Listener->Forward[2];
values[3] = context->Listener->Up[0];
values[4] = context->Listener->Up[1];
values[5] = context->Listener->Up[2];
UnlockContext(context);
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
ALAPI void ALAPIENTRY alGetListeneriv(ALenum eParam, ALint* plValues)
AL_API ALvoid AL_APIENTRY alGetListeneri(ALenum param, ALint *value)
{
ALCcontext *pContext;
ALCcontext *context;
pContext = GetContextSuspended();
if(!pContext) return;
context = GetContextRef();
if(!context) return;
if(plValues)
if(!(value))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
switch(param)
{
switch(eParam)
{
case AL_POSITION:
plValues[0] = (ALint)pContext->Listener.Position[0];
plValues[1] = (ALint)pContext->Listener.Position[1];
plValues[2] = (ALint)pContext->Listener.Position[2];
break;
case AL_VELOCITY:
plValues[0] = (ALint)pContext->Listener.Velocity[0];
plValues[1] = (ALint)pContext->Listener.Velocity[1];
plValues[2] = (ALint)pContext->Listener.Velocity[2];
break;
case AL_ORIENTATION:
// AT then UP
plValues[0] = (ALint)pContext->Listener.Forward[0];
plValues[1] = (ALint)pContext->Listener.Forward[1];
plValues[2] = (ALint)pContext->Listener.Forward[2];
plValues[3] = (ALint)pContext->Listener.Up[0];
plValues[4] = (ALint)pContext->Listener.Up[1];
plValues[5] = (ALint)pContext->Listener.Up[2];
break;
default:
alSetError(AL_INVALID_ENUM);
break;
}
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
else
alSetError(AL_INVALID_VALUE);
ProcessContext(pContext);
done:
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3)
{
ALCcontext *context;
context = GetContextRef();
if(!context) return;
if(!(value1 && value2 && value3))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
switch (param)
{
case AL_POSITION:
LockContext(context);
*value1 = (ALint)context->Listener->Position[0];
*value2 = (ALint)context->Listener->Position[1];
*value3 = (ALint)context->Listener->Position[2];
UnlockContext(context);
break;
case AL_VELOCITY:
LockContext(context);
*value1 = (ALint)context->Listener->Velocity[0];
*value2 = (ALint)context->Listener->Velocity[1];
*value3 = (ALint)context->Listener->Velocity[2];
UnlockContext(context);
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint* values)
{
ALCcontext *context;
switch(param)
{
case AL_POSITION:
case AL_VELOCITY:
alGetListener3i(param, values+0, values+1, values+2);
return;
}
context = GetContextRef();
if(!context) return;
if(!(values))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
switch(param)
{
case AL_ORIENTATION:
LockContext(context);
// AT then UP
values[0] = (ALint)context->Listener->Forward[0];
values[1] = (ALint)context->Listener->Forward[1];
values[2] = (ALint)context->Listener->Forward[2];
values[3] = (ALint)context->Listener->Up[0];
values[4] = (ALint)context->Listener->Up[1];
values[5] = (ALint)context->Listener->Up[2];
UnlockContext(context);
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
+217
View File
@@ -0,0 +1,217 @@
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "alMain.h"
#include "alMidi.h"
#include "alError.h"
#include "alThunk.h"
#include "evtqueue.h"
#include "rwlock.h"
#include "alu.h"
#include "midi/base.h"
MidiSynth *SynthCreate(ALCdevice *device)
{
MidiSynth *synth = NULL;
if(!synth) synth = SSynth_create(device);
if(!synth) synth = FSynth_create(device);
if(!synth) synth = DSynth_create(device);
return synth;
}
AL_API void AL_APIENTRY alMidiSoundfontSOFT(ALuint id)
{
alMidiSoundfontvSOFT(1, &id);
}
AL_API void AL_APIENTRY alMidiSoundfontvSOFT(ALsizei count, const ALuint *ids)
{
ALCdevice *device;
ALCcontext *context;
MidiSynth *synth;
ALenum err;
context = GetContextRef();
if(!context) return;
if(count < 0)
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
device = context->Device;
synth = device->Synth;
WriteLock(&synth->Lock);
if(synth->State == AL_PLAYING || synth->State == AL_PAUSED)
alSetError(context, AL_INVALID_OPERATION);
else
{
err = V(synth,selectSoundfonts)(context, count, ids);
if(err != AL_NO_ERROR)
alSetError(context, err);
}
WriteUnlock(&synth->Lock);
done:
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alMidiEventSOFT(ALuint64SOFT time, ALenum event, ALsizei channel, ALsizei param1, ALsizei param2)
{
ALCdevice *device;
ALCcontext *context;
ALenum err;
context = GetContextRef();
if(!context) return;
if(!(event == AL_NOTEOFF_SOFT || event == AL_NOTEON_SOFT ||
event == AL_KEYPRESSURE_SOFT || event == AL_CONTROLLERCHANGE_SOFT ||
event == AL_PROGRAMCHANGE_SOFT || event == AL_CHANNELPRESSURE_SOFT ||
event == AL_PITCHBEND_SOFT))
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
if(!(channel >= 0 && channel <= 15))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
if(!(param1 >= 0 && param1 <= 127))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
if(!(param2 >= 0 && param2 <= 127))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
device = context->Device;
ALCdevice_Lock(device);
err = MidiSynth_insertEvent(device->Synth, time, event|channel, param1, param2);
ALCdevice_Unlock(device);
if(err != AL_NO_ERROR)
alSetError(context, err);
done:
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alMidiSysExSOFT(ALuint64SOFT time, const ALbyte *data, ALsizei size)
{
ALCdevice *device;
ALCcontext *context;
ALenum err;
context = GetContextRef();
if(!context) return;
if(!data || size < 0)
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
device = context->Device;
ALCdevice_Lock(device);
err = MidiSynth_insertSysExEvent(device->Synth, time, data, size);
ALCdevice_Unlock(device);
if(err != AL_NO_ERROR)
alSetError(context, err);
done:
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alMidiPlaySOFT(void)
{
ALCcontext *context;
MidiSynth *synth;
context = GetContextRef();
if(!context) return;
synth = context->Device->Synth;
WriteLock(&synth->Lock);
MidiSynth_setState(synth, AL_PLAYING);
WriteUnlock(&synth->Lock);
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alMidiPauseSOFT(void)
{
ALCcontext *context;
MidiSynth *synth;
context = GetContextRef();
if(!context) return;
synth = context->Device->Synth;
WriteLock(&synth->Lock);
MidiSynth_setState(synth, AL_PAUSED);
WriteUnlock(&synth->Lock);
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alMidiStopSOFT(void)
{
ALCdevice *device;
ALCcontext *context;
MidiSynth *synth;
context = GetContextRef();
if(!context) return;
device = context->Device;
synth = device->Synth;
WriteLock(&synth->Lock);
MidiSynth_setState(synth, AL_STOPPED);
ALCdevice_Lock(device);
V0(synth,stop)();
ALCdevice_Unlock(device);
WriteUnlock(&synth->Lock);
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alMidiResetSOFT(void)
{
ALCdevice *device;
ALCcontext *context;
MidiSynth *synth;
context = GetContextRef();
if(!context) return;
device = context->Device;
synth = device->Synth;
WriteLock(&synth->Lock);
MidiSynth_setState(synth, AL_INITIAL);
ALCdevice_Lock(device);
V0(synth,reset)();
ALCdevice_Unlock(device);
WriteUnlock(&synth->Lock);
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alMidiGainSOFT(ALfloat value)
{
ALCdevice *device;
ALCcontext *context;
context = GetContextRef();
if(!context) return;
if(!(value >= 0.0f && isfinite(value)))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
device = context->Device;
V(device->Synth,setGain)(value);
done:
ALCcontext_DecRef(context);
}
+339
View File
@@ -0,0 +1,339 @@
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include "alMain.h"
#include "alMidi.h"
#include "alError.h"
#include "alThunk.h"
#include "midi/base.h"
extern inline struct ALsfpreset *LookupPreset(ALCdevice *device, ALuint id);
extern inline struct ALsfpreset *RemovePreset(ALCdevice *device, ALuint id);
static void ALsfpreset_Construct(ALsfpreset *self);
static void ALsfpreset_Destruct(ALsfpreset *self);
AL_API void AL_APIENTRY alGenPresetsSOFT(ALsizei n, ALuint *ids)
{
ALCcontext *context;
ALsizei cur = 0;
context = GetContextRef();
if(!context) return;
if(!(n >= 0))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
for(cur = 0;cur < n;cur++)
{
ALsfpreset *preset = NewPreset(context);
if(!preset)
{
alDeletePresetsSOFT(cur, ids);
break;
}
ids[cur] = preset->id;
}
done:
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alDeletePresetsSOFT(ALsizei n, const ALuint *ids)
{
ALCdevice *device;
ALCcontext *context;
ALsfpreset *preset;
ALsizei i;
context = GetContextRef();
if(!context) return;
if(!(n >= 0))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
device = context->Device;
for(i = 0;i < n;i++)
{
/* Check for valid ID */
if((preset=LookupPreset(device, ids[i])) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
if(ReadRef(&preset->ref) != 0)
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
}
for(i = 0;i < n;i++)
{
if((preset=LookupPreset(device, ids[i])) != NULL)
DeletePreset(device, preset);
}
done:
ALCcontext_DecRef(context);
}
AL_API ALboolean AL_APIENTRY alIsPresetSOFT(ALuint id)
{
ALCcontext *context;
ALboolean ret;
context = GetContextRef();
if(!context) return AL_FALSE;
ret = LookupPreset(context->Device, id) ? AL_TRUE : AL_FALSE;
ALCcontext_DecRef(context);
return ret;
}
AL_API void AL_APIENTRY alPresetiSOFT(ALuint id, ALenum param, ALint value)
{
ALCdevice *device;
ALCcontext *context;
ALsfpreset *preset;
context = GetContextRef();
if(!context) return;
device = context->Device;
if((preset=LookupPreset(device, id)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
if(ReadRef(&preset->ref) != 0)
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
switch(param)
{
case AL_MIDI_PRESET_SOFT:
if(!(value >= 0 && value <= 127))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
preset->Preset = value;
break;
case AL_MIDI_BANK_SOFT:
if(!(value >= 0 && value <= 128))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
preset->Bank = value;
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alPresetivSOFT(ALuint id, ALenum param, const ALint *values)
{
ALCdevice *device;
ALCcontext *context;
ALsfpreset *preset;
switch(param)
{
case AL_MIDI_PRESET_SOFT:
case AL_MIDI_BANK_SOFT:
alPresetiSOFT(id, param, values[0]);
return;
}
context = GetContextRef();
if(!context) return;
device = context->Device;
if((preset=LookupPreset(device, id)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
if(ReadRef(&preset->ref) != 0)
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
switch(param)
{
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alGetPresetivSOFT(ALuint id, ALenum param, ALint *values)
{
ALCdevice *device;
ALCcontext *context;
ALsfpreset *preset;
ALsizei i;
context = GetContextRef();
if(!context) return;
device = context->Device;
if((preset=LookupPreset(device, id)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
{
case AL_MIDI_PRESET_SOFT:
values[0] = preset->Preset;
break;
case AL_MIDI_BANK_SOFT:
values[0] = preset->Bank;
break;
case AL_FONTSOUNDS_SIZE_SOFT:
values[0] = preset->NumSounds;
break;
case AL_FONTSOUNDS_SOFT:
for(i = 0;i < preset->NumSounds;i++)
values[i] = preset->Sounds[i]->id;
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alPresetFontsoundsSOFT(ALuint id, ALsizei count, const ALuint *fsids)
{
ALCdevice *device;
ALCcontext *context;
ALsfpreset *preset;
ALfontsound **sounds;
ALsizei i;
context = GetContextRef();
if(!context) return;
device = context->Device;
if(!(preset=LookupPreset(device, id)))
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
if(count < 0)
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
if(ReadRef(&preset->ref) != 0)
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
if(count == 0)
sounds = NULL;
else
{
sounds = calloc(count, sizeof(sounds[0]));
if(!sounds)
SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done);
for(i = 0;i < count;i++)
{
if(!(sounds[i]=LookupFontsound(device, fsids[i])))
{
free(sounds);
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
}
}
}
for(i = 0;i < count;i++)
IncrementRef(&sounds[i]->ref);
sounds = ExchangePtr((XchgPtr*)&preset->Sounds, sounds);
count = ExchangeInt(&preset->NumSounds, count);
for(i = 0;i < count;i++)
DecrementRef(&sounds[i]->ref);
free(sounds);
done:
ALCcontext_DecRef(context);
}
ALsfpreset *NewPreset(ALCcontext *context)
{
ALCdevice *device = context->Device;
ALsfpreset *preset;
ALenum err;
preset = calloc(1, sizeof(*preset));
if(!preset)
SET_ERROR_AND_RETURN_VALUE(context, AL_OUT_OF_MEMORY, NULL);
ALsfpreset_Construct(preset);
err = NewThunkEntry(&preset->id);
if(err == AL_NO_ERROR)
err = InsertUIntMapEntry(&device->PresetMap, preset->id, preset);
if(err != AL_NO_ERROR)
{
ALsfpreset_Destruct(preset);
memset(preset, 0, sizeof(*preset));
free(preset);
SET_ERROR_AND_RETURN_VALUE(context, err, NULL);
}
return preset;
}
void DeletePreset(ALCdevice *device, ALsfpreset *preset)
{
RemovePreset(device, preset->id);
ALsfpreset_Destruct(preset);
memset(preset, 0, sizeof(*preset));
free(preset);
}
static void ALsfpreset_Construct(ALsfpreset *self)
{
InitRef(&self->ref, 0);
self->Preset = 0;
self->Bank = 0;
self->Sounds = NULL;
self->NumSounds = 0;
self->id = 0;
}
static void ALsfpreset_Destruct(ALsfpreset *self)
{
ALsizei i;
FreeThunkEntry(self->id);
self->id = 0;
for(i = 0;i < self->NumSounds;i++)
DecrementRef(&self->Sounds[i]->ref);
free(self->Sounds);
self->Sounds = NULL;
self->NumSounds = 0;
}
/* ReleaseALPresets
*
* Called to destroy any presets that still exist on the device
*/
void ReleaseALPresets(ALCdevice *device)
{
ALsizei i;
for(i = 0;i < device->PresetMap.size;i++)
{
ALsfpreset *temp = device->PresetMap.array[i].value;
device->PresetMap.array[i].value = NULL;
ALsfpreset_Destruct(temp);
memset(temp, 0, sizeof(*temp));
free(temp);
}
}
+454
View File
@@ -0,0 +1,454 @@
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "alMain.h"
#include "alMidi.h"
#include "alThunk.h"
#include "alError.h"
#include <alBuffer.h>
#include "midi/base.h"
extern inline struct ALsoundfont *LookupSfont(ALCdevice *device, ALuint id);
extern inline struct ALsoundfont *RemoveSfont(ALCdevice *device, ALuint id);
static void ALsoundfont_Construct(ALsoundfont *self);
static void ALsoundfont_Destruct(ALsoundfont *self);
void ALsoundfont_deleteSoundfont(ALsoundfont *self, ALCdevice *device);
ALsoundfont *ALsoundfont_getDefSoundfont(ALCcontext *context);
static size_t ALsoundfont_read(ALvoid *buf, size_t bytes, ALvoid *ptr);
AL_API void AL_APIENTRY alGenSoundfontsSOFT(ALsizei n, ALuint *ids)
{
ALCdevice *device;
ALCcontext *context;
ALsizei cur = 0;
ALenum err;
context = GetContextRef();
if(!context) return;
if(!(n >= 0))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
device = context->Device;
for(cur = 0;cur < n;cur++)
{
ALsoundfont *sfont = calloc(1, sizeof(ALsoundfont));
if(!sfont)
{
alDeleteSoundfontsSOFT(cur, ids);
SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done);
}
ALsoundfont_Construct(sfont);
err = NewThunkEntry(&sfont->id);
if(err == AL_NO_ERROR)
err = InsertUIntMapEntry(&device->SfontMap, sfont->id, sfont);
if(err != AL_NO_ERROR)
{
ALsoundfont_Destruct(sfont);
memset(sfont, 0, sizeof(ALsoundfont));
free(sfont);
alDeleteSoundfontsSOFT(cur, ids);
SET_ERROR_AND_GOTO(context, err, done);
}
ids[cur] = sfont->id;
}
done:
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alDeleteSoundfontsSOFT(ALsizei n, const ALuint *ids)
{
ALCdevice *device;
ALCcontext *context;
ALsoundfont *sfont;
ALsizei i;
context = GetContextRef();
if(!context) return;
if(!(n >= 0))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
device = context->Device;
for(i = 0;i < n;i++)
{
/* Check for valid soundfont ID */
if(ids[i] == 0)
{
if(!(sfont=device->DefaultSfont))
continue;
}
else if((sfont=LookupSfont(device, ids[i])) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
if(ReadRef(&sfont->ref) != 0)
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
}
for(i = 0;i < n;i++)
{
if(ids[i] == 0)
{
MidiSynth *synth = device->Synth;
WriteLock(&synth->Lock);
if(device->DefaultSfont != NULL)
ALsoundfont_deleteSoundfont(device->DefaultSfont, device);
device->DefaultSfont = NULL;
WriteUnlock(&synth->Lock);
continue;
}
else if((sfont=RemoveSfont(device, ids[i])) == NULL)
continue;
ALsoundfont_Destruct(sfont);
memset(sfont, 0, sizeof(*sfont));
free(sfont);
}
done:
ALCcontext_DecRef(context);
}
AL_API ALboolean AL_APIENTRY alIsSoundfontSOFT(ALuint id)
{
ALCcontext *context;
ALboolean ret;
context = GetContextRef();
if(!context) return AL_FALSE;
ret = ((!id || LookupSfont(context->Device, id)) ?
AL_TRUE : AL_FALSE);
ALCcontext_DecRef(context);
return ret;
}
AL_API void AL_APIENTRY alGetSoundfontivSOFT(ALuint id, ALenum param, ALint *values)
{
ALCdevice *device;
ALCcontext *context;
ALsoundfont *sfont;
ALsizei i;
context = GetContextRef();
if(!context) return;
device = context->Device;
if(id == 0)
sfont = ALsoundfont_getDefSoundfont(context);
else if(!(sfont=LookupSfont(device, id)))
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
{
case AL_PRESETS_SIZE_SOFT:
values[0] = sfont->NumPresets;
break;
case AL_PRESETS_SOFT:
for(i = 0;i < sfont->NumPresets;i++)
values[i] = sfont->Presets[i]->id;
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alSoundfontPresetsSOFT(ALuint id, ALsizei count, const ALuint *pids)
{
ALCdevice *device;
ALCcontext *context;
ALsoundfont *sfont;
ALsfpreset **presets;
ALsizei i;
context = GetContextRef();
if(!context) return;
device = context->Device;
if(id == 0)
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
if(!(sfont=LookupSfont(device, id)))
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
if(count < 0)
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
WriteLock(&sfont->Lock);
if(ReadRef(&sfont->ref) != 0)
{
WriteUnlock(&sfont->Lock);
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
}
if(count == 0)
presets = NULL;
else
{
presets = calloc(count, sizeof(presets[0]));
if(!presets)
{
WriteUnlock(&sfont->Lock);
SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done);
}
for(i = 0;i < count;i++)
{
if(!(presets[i]=LookupPreset(device, pids[i])))
{
free(presets);
WriteUnlock(&sfont->Lock);
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
}
}
}
for(i = 0;i < count;i++)
IncrementRef(&presets[i]->ref);
presets = ExchangePtr((XchgPtr*)&sfont->Presets, presets);
count = ExchangeInt(&sfont->NumPresets, count);
WriteUnlock(&sfont->Lock);
for(i = 0;i < count;i++)
DecrementRef(&presets[i]->ref);
free(presets);
done:
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alLoadSoundfontSOFT(ALuint id, size_t(*cb)(ALvoid*,size_t,ALvoid*), ALvoid *user)
{
ALCdevice *device;
ALCcontext *context;
ALsoundfont *sfont;
Reader reader;
context = GetContextRef();
if(!context) return;
device = context->Device;
if(id == 0)
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
if(!(sfont=LookupSfont(device, id)))
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
WriteLock(&sfont->Lock);
if(ReadRef(&sfont->ref) != 0)
{
WriteUnlock(&sfont->Lock);
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
}
if(sfont->NumPresets > 0)
{
WriteUnlock(&sfont->Lock);
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
}
reader.cb = cb;
reader.ptr = user;
reader.error = 0;
loadSf2(&reader, sfont, context);
WriteUnlock(&sfont->Lock);
done:
ALCcontext_DecRef(context);
}
static void ALsoundfont_Construct(ALsoundfont *self)
{
InitRef(&self->ref, 0);
self->Presets = NULL;
self->NumPresets = 0;
RWLockInit(&self->Lock);
self->id = 0;
}
static void ALsoundfont_Destruct(ALsoundfont *self)
{
ALsizei i;
FreeThunkEntry(self->id);
self->id = 0;
for(i = 0;i < self->NumPresets;i++)
{
DecrementRef(&self->Presets[i]->ref);
self->Presets[i] = NULL;
}
free(self->Presets);
self->Presets = NULL;
self->NumPresets = 0;
}
ALsoundfont *ALsoundfont_getDefSoundfont(ALCcontext *context)
{
ALCdevice *device = context->Device;
al_string fname = AL_STRING_INIT_STATIC();
const char *namelist;
if(device->DefaultSfont)
return device->DefaultSfont;
device->DefaultSfont = calloc(1, sizeof(device->DefaultSfont[0]));
ALsoundfont_Construct(device->DefaultSfont);
namelist = getenv("ALSOFT_SOUNDFONT");
if(!namelist || !namelist[0])
ConfigValueStr("midi", "soundfont", &namelist);
while(namelist && namelist[0])
{
const char *next, *end;
FILE *f;
while(*namelist && (isspace(*namelist) || *namelist == ','))
namelist++;
if(!*namelist)
break;
next = strchr(namelist, ',');
end = next ? next++ : (namelist+strlen(namelist));
while(--end != namelist && isspace(*end)) {
}
if(end == namelist)
continue;
al_string_append_range(&fname, namelist, end+1);
namelist = next;
f = OpenDataFile(al_string_get_cstr(fname), "openal/soundfonts");
if(f == NULL)
ERR("Failed to open %s\n", al_string_get_cstr(fname));
else
{
Reader reader;
reader.cb = ALsoundfont_read;
reader.ptr = f;
reader.error = 0;
TRACE("Loading %s\n", al_string_get_cstr(fname));
loadSf2(&reader, device->DefaultSfont, context);
fclose(f);
}
al_string_clear(&fname);
}
AL_STRING_DEINIT(fname);
return device->DefaultSfont;
}
void ALsoundfont_deleteSoundfont(ALsoundfont *self, ALCdevice *device)
{
ALsfpreset **presets;
ALsizei num_presets;
VECTOR(ALbuffer*) buffers;
ALsizei i;
VECTOR_INIT(buffers);
presets = ExchangePtr((XchgPtr*)&self->Presets, NULL);
num_presets = ExchangeInt(&self->NumPresets, 0);
for(i = 0;i < num_presets;i++)
{
ALsfpreset *preset = presets[i];
ALfontsound **sounds;
ALsizei num_sounds;
ALboolean deleting;
ALsizei j;
sounds = ExchangePtr((XchgPtr*)&preset->Sounds, NULL);
num_sounds = ExchangeInt(&preset->NumSounds, 0);
DeletePreset(device, preset);
preset = NULL;
for(j = 0;j < num_sounds;j++)
DecrementRef(&sounds[j]->ref);
/* Some fontsounds may not be immediately deletable because they're
* linked to another fontsound. When those fontsounds are deleted
* they should become deletable, so use a loop until all fontsounds
* are deleted. */
do {
deleting = AL_FALSE;
for(j = 0;j < num_sounds;j++)
{
if(sounds[j] && ReadRef(&sounds[j]->ref) == 0)
{
deleting = AL_TRUE;
if(sounds[j]->Buffer)
{
ALbuffer *buffer = sounds[j]->Buffer;
ALbuffer **iter;
#define MATCH_BUFFER(_i) (buffer == *(_i))
VECTOR_FIND_IF(iter, ALbuffer*, buffers, MATCH_BUFFER);
if(iter == VECTOR_ITER_END(buffers))
VECTOR_PUSH_BACK(buffers, buffer);
#undef MATCH_BUFFER
}
DeleteFontsound(device, sounds[j]);
sounds[j] = NULL;
}
}
} while(deleting);
free(sounds);
}
ALsoundfont_Destruct(self);
free(self);
#define DELETE_BUFFER(iter) do { \
assert(ReadRef(&(*(iter))->ref) == 0); \
DeleteBuffer(device, *(iter)); \
} while(0)
VECTOR_FOR_EACH(ALbuffer*, buffers, DELETE_BUFFER);
VECTOR_DEINIT(buffers);
#undef DELETE_BUFFER
}
static size_t ALsoundfont_read(ALvoid *buf, size_t bytes, ALvoid *ptr)
{
return fread(buf, 1, bytes, (FILE*)ptr);
}
/* ReleaseALSoundfonts
*
* Called to destroy any soundfonts that still exist on the device
*/
void ReleaseALSoundfonts(ALCdevice *device)
{
ALsizei i;
for(i = 0;i < device->SfontMap.size;i++)
{
ALsoundfont *temp = device->SfontMap.array[i].value;
device->SfontMap.array[i].value = NULL;
ALsoundfont_Destruct(temp);
memset(temp, 0, sizeof(*temp));
free(temp);
}
}
+2647 -1833
View File
File diff suppressed because it is too large Load Diff
+640 -410
View File
File diff suppressed because it is too large Load Diff
+41 -62
View File
@@ -25,86 +25,65 @@
#include "alMain.h"
#include "alThunk.h"
typedef struct {
ALvoid *ptr;
ALboolean InUse;
} ThunkEntry;
static ThunkEntry *g_ThunkArray;
static ALuint g_ThunkArraySize;
static ALenum *ThunkArray;
static ALuint ThunkArraySize;
static RWLock ThunkLock;
static CRITICAL_SECTION g_ThunkLock;
void alThunkInit(void)
void ThunkInit(void)
{
InitializeCriticalSection(&g_ThunkLock);
g_ThunkArraySize = 1;
g_ThunkArray = calloc(1, g_ThunkArraySize * sizeof(ThunkEntry));
RWLockInit(&ThunkLock);
ThunkArraySize = 1;
ThunkArray = calloc(1, ThunkArraySize * sizeof(*ThunkArray));
}
void alThunkExit(void)
void ThunkExit(void)
{
free(g_ThunkArray);
g_ThunkArray = NULL;
g_ThunkArraySize = 0;
DeleteCriticalSection(&g_ThunkLock);
free(ThunkArray);
ThunkArray = NULL;
ThunkArraySize = 0;
}
ALuint alThunkAddEntry(ALvoid *ptr)
ALenum NewThunkEntry(ALuint *index)
{
ALuint index;
ALenum *NewList;
ALuint i;
EnterCriticalSection(&g_ThunkLock);
for(index = 0;index < g_ThunkArraySize;index++)
ReadLock(&ThunkLock);
for(i = 0;i < ThunkArraySize;i++)
{
if(g_ThunkArray[index].InUse == AL_FALSE)
break;
}
if(index == g_ThunkArraySize)
{
ThunkEntry *NewList;
NewList = realloc(g_ThunkArray, g_ThunkArraySize*2 * sizeof(ThunkEntry));
if(!NewList)
if(ExchangeInt(&ThunkArray[i], AL_TRUE) == AL_FALSE)
{
LeaveCriticalSection(&g_ThunkLock);
return 0;
ReadUnlock(&ThunkLock);
*index = i+1;
return AL_NO_ERROR;
}
memset(&NewList[g_ThunkArraySize], 0, g_ThunkArraySize*sizeof(ThunkEntry));
g_ThunkArraySize *= 2;
g_ThunkArray = NewList;
}
ReadUnlock(&ThunkLock);
g_ThunkArray[index].ptr = ptr;
g_ThunkArray[index].InUse = AL_TRUE;
WriteLock(&ThunkLock);
NewList = realloc(ThunkArray, ThunkArraySize*2 * sizeof(*ThunkArray));
if(!NewList)
{
WriteUnlock(&ThunkLock);
ERR("Realloc failed to increase to %u entries!\n", ThunkArraySize*2);
return AL_OUT_OF_MEMORY;
}
memset(&NewList[ThunkArraySize], 0, ThunkArraySize*sizeof(*ThunkArray));
ThunkArraySize *= 2;
ThunkArray = NewList;
LeaveCriticalSection(&g_ThunkLock);
ThunkArray[i] = AL_TRUE;
WriteUnlock(&ThunkLock);
return index+1;
*index = i+1;
return AL_NO_ERROR;
}
void alThunkRemoveEntry(ALuint index)
void FreeThunkEntry(ALuint index)
{
EnterCriticalSection(&g_ThunkLock);
if(index > 0 && index <= g_ThunkArraySize)
g_ThunkArray[index-1].InUse = AL_FALSE;
LeaveCriticalSection(&g_ThunkLock);
}
ALvoid *alThunkLookupEntry(ALuint index)
{
ALvoid *ptr = NULL;
EnterCriticalSection(&g_ThunkLock);
if(index > 0 && index <= g_ThunkArraySize)
ptr = g_ThunkArray[index-1].ptr;
LeaveCriticalSection(&g_ThunkLock);
return ptr;
ReadLock(&ThunkLock);
if(index > 0 && index <= ThunkArraySize)
ExchangeInt(&ThunkArray[index-1], AL_FALSE);
ReadUnlock(&ThunkLock);
}
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More