Compare commits

...

884 Commits

Author SHA1 Message Date
Chris Robinson ce6076091b Release 1.18.2 2017-09-24 06:41:50 -07:00
Chris Robinson dba8166b67 Update ChangeLog with JACK fix 2017-09-23 15:12:50 -07:00
Chris Robinson 4fa9c8e4b4 Restore the original JACK message callback when possible 2017-09-23 15:12:38 -07:00
Chris Robinson 912c0cc9eb Update ChangeLog 2017-09-22 07:18:31 -07:00
Daniel Scharrer 9a67aa8530 Fix build on Gentoo FreeBSD with freebsd-lib 9.1 2017-09-22 07:18:14 -07:00
Chris Robinson 45e757ea12 Update ChangeLog 2017-09-19 10:16:53 -07:00
Chris Robinson 026f96c2a3 Manually save and restore the FPU rounding mode on Windows
Apparently there is a bug with at least MinGW-W64 where fegetenv and fesetenv
do not properly save and restore the FPU rounding mode, resulting in the
rounding mode remaining as round-to-zero after certain function calls. I do not
know if this also affects MSVC, but better safe than sorry for now.
2017-09-19 10:05:41 -07:00
Chris Robinson 631624fa58 Always link to ossaudio when found 2017-09-19 10:05:41 -07:00
Chris Robinson 51e11fdb9c Handle libossaudio as an optional OSS library 2017-09-19 10:05:41 -07:00
Chris Robinson eaf3b94143 Add a check for pthread_setname_np with three parameters
As found in NetBSD.
2017-09-19 10:05:41 -07:00
Chris Robinson 84eca96dad Don't hide -msse and -mfpu=neon checks behind a not-msvc check
Apparently Clang gets reported as being MSVC on Windows, but still needs the
GCC switches to enable SSE code generation.
2017-09-19 10:05:41 -07:00
Chris Robinson bf9c36408a Release 1.18.1 2017-07-29 22:09:21 -07:00
Chris Robinson 12db67f548 Cleanup output write functions 2017-07-27 19:07:02 -07:00
Chris Robinson e25ba747e6 Remove unused macros 2017-07-25 17:42:16 -07:00
Chris Robinson 11b44d29e6 Update default HRTFs 2017-07-25 17:11:44 -07:00
Chris Robinson 75841642bf Update makehrtf to use a larger FFT by default
Also fixes DC offset removal and increases the max IR size.
2017-07-25 16:17:46 -07:00
Chris Robinson 1ab082caaf Update ChangeLog with OSS enumeration fix 2017-07-23 22:10:13 -07:00
Chris Robinson 82a990e08e Downgrade some ERRs to TRACEs
These don't exist outside OSSv4, e.g. with OSS/Free, padsp, or aoss, so no need
to be concerned.
2017-07-23 16:43:39 -07:00
Chris Robinson 5ab4e584ee Make sure OSS device files exist before adding them 2017-07-23 16:38:54 -07:00
Chris Robinson 4ec67e6226 Add missing include for std::array 2017-07-23 00:18:32 -07:00
Chris Robinson 21b71b2fd2 Update ChangeLog 2017-07-19 18:54:12 -07:00
Chris Robinson f91445029d Fix default effect initialization 2017-07-19 18:26:46 -07:00
Chris Robinson fea74124c8 Add an all-pass filter that replicates the band splitter's phase shift 2017-07-19 02:48:01 -07:00
Chris Robinson 0135ddc2e5 Scale the source volume by +3dB for a full spread
This effectively turns a full spread source into an ambient response,
preventing such sources from being unexpectedly quiet.
2017-07-18 22:15:32 -07:00
Chris Robinson 5f7268c0cc Add a 5.1 preset that excludes the front-center speaker
On some speaker setups, the front-center speaker is not designed for full-range
content and should be used exclusively for dialog. Consequently it should not
be used for positional sounds, only the dedicated dialog effect.
2017-07-18 20:30:45 -07:00
Chris Robinson c484935542 Apply the output buffer offset before writing to it 2017-07-15 23:13:08 -07:00
Chris Robinson ff696bc1fe Set the float PCM GUID for wave files only when outputting float 2017-07-15 01:45:48 -07:00
Chris Robinson 8fa3f6da64 Add the default auxiliary slot to the active slot array 2017-07-13 23:13:02 -07:00
Chris Robinson 249afde5f9 Initialize the default effect after device update 2017-07-13 22:35:37 -07:00
Chris Robinson a535169bbd Use macros to set and restore the mixer FPU mode 2017-07-13 22:30:39 -07:00
Chris Robinson 22d77b87a3 Store the default effect slot in the context 2017-07-13 21:44:25 -07:00
Chris Robinson 67ab9ec466 Don't trace for every GetDriverIndexForName call 2017-07-11 22:43:22 -07:00
Chris Robinson 0da55fd912 Trace a version for the router 2017-07-11 00:43:15 -07:00
Chris Robinson 5048322fff Update alffplay's command line message 2017-07-11 00:38:52 -07:00
Chris Robinson f313f9c117 Rename the OpenAL target if also building the router
This is rather ugly, but it's necessary to get a proper export configuration.
The issue was that the main OpenAL target library name is set to soft_oal when
the router is being built, which is incorrect for the exported config library.
Exporting the router would have the incorrect name of OpenAL::Router.

So this change has the router use the OpenAL target name when it's built, which
is good since it will have the standard OpenAL lib name for apps to link to and
get the OpenAL::OpenAL export name. The main library's target name is changed
in this case to avoid conflicts.
2017-07-10 01:57:22 -07:00
Chris Robinson d6326c1791 Generate the def and lib files from the router when built 2017-07-09 23:14:31 -07:00
Chris Robinson d050af7eeb Reorganize some Windows-only CMake commands 2017-07-09 22:19:34 -07:00
Chris Robinson 2be4c93f9f Use a macro to add backend include dirs 2017-07-08 22:58:16 -07:00
Chris Robinson 09826cc684 Set the proper ldflags for the router 2017-07-07 18:48:19 -07:00
Chris Robinson e4e240fa9b Support ALC_EXT_thread_local_context in the router
Note that a given context's device must also support the extension to work. The
router's support simply lets a driver's capabilities through.
2017-07-07 18:41:03 -07:00
Chris Robinson 6be752a9b1 Add methods for thread-local contexts to the router 2017-07-07 18:33:54 -07:00
Chris Robinson faefa1d554 Revert "Try all drivers for an unknown device name"
This reverts commit dadf7a4cf2.
2017-07-05 14:18:16 -07:00
Chris Robinson 56a33ef955 Print the opened device in alffplay 2017-07-05 12:32:19 -07:00
Chris Robinson dadf7a4cf2 Try all drivers for an unknown device name 2017-07-05 12:23:08 -07:00
Chris Robinson 3af2ff7b25 Trace if a driver was found for a device name 2017-07-05 12:16:28 -07:00
Chris Robinson 72ce0d1e9c Open a device only when a driver index is found 2017-07-03 22:14:15 -07:00
Chris Robinson 3cd4cfe73d Don't add --output-def to EXTRA_LDFLAGS 2017-07-01 20:48:17 -07:00
Chris Robinson 74139c914d Skip past the -device switch even if the device doesn't open 2017-07-01 20:44:56 -07:00
Chris Robinson cb83f48105 Add an option to enable direct channels for alffplay 2017-07-01 19:18:57 -07:00
Chris Robinson a14f651034 Flush the log file after writing 2017-07-01 15:34:42 -07:00
Chris Robinson 958fa34272 Use a weaker memory order for the current context iface 2017-07-01 15:25:39 -07:00
Chris Robinson af626fdded Initialize ALC resources in the file they're used in 2017-07-01 15:25:11 -07:00
Chris Robinson 7daefd4e77 Use the al alloc functions instead of standard 2017-07-01 12:22:25 -07:00
Chris Robinson 32bda7b94c Add tracing capabilities to the router 2017-06-30 17:22:15 -07:00
Chris Robinson 77e317609b Add special handling for alGerError in the router 2017-06-30 17:21:26 -07:00
Chris Robinson ce9222b686 Clear initial ALC version vars before querying it 2017-06-29 23:21:07 -07:00
Chris Robinson 8a0d1e5191 Store the QSA backend's ExtraData in the wrapper struct 2017-06-29 17:38:38 -07:00
Chris Robinson d874b6bb27 Don't assume the first driver has the default device 2017-06-29 15:59:16 -07:00
Chris Robinson 00694826ef Protect context switches with a lock in the router 2017-06-29 10:56:32 -07:00
Chris Robinson 058d57ef03 Protect device enumeration in the router with a mutex 2017-06-29 10:39:27 -07:00
Chris Robinson e8ce8924d1 Use sqrtf for single-precision square roots 2017-06-29 10:28:37 -07:00
Chris Robinson ec13cf6c9c Add casts to silence MSVC 2017-06-29 10:28:22 -07:00
Chris Robinson a69d608a1e Define a backup log2f if the compiler doesn't have it 2017-06-29 10:11:31 -07:00
Chris Robinson aefa11b6ad Workaround for MSVC not liking 1.0f/0.0f for float infinity 2017-06-29 09:57:19 -07:00
Chris Robinson cee2d226d2 Return the extension list in the router 2017-06-29 08:55:44 -07:00
Chris Robinson f08a7b341f Prepare the new driver in a local variable 2017-06-29 08:46:06 -07:00
Chris Robinson 15e6821147 Avoid unnecessary reenumeration in the router 2017-06-29 08:35:21 -07:00
Chris Robinson 3a16fed279 Handle the ALC version for some extension capabilities
Also fix some improper parenthesis.
2017-06-28 23:18:39 -07:00
Chris Robinson ef7eced7a7 Properly clean up allocated memory at exit 2017-06-28 22:39:11 -07:00
Chris Robinson a729007887 Implement setting a context current in the router 2017-06-28 21:54:44 -07:00
Chris Robinson 323162c49f Implement creating and destroying contexts 2017-06-28 21:15:30 -07:00
Chris Robinson ea4379c5b7 Implement opening and closing devices in the router 2017-06-28 21:10:02 -07:00
Chris Robinson 6124f447cd Implement enumeration in the router 2017-06-28 20:45:23 -07:00
Chris Robinson 47f1db36a7 Partially implement ALC functions in the router 2017-06-28 20:03:36 -07:00
Chris Robinson b88b57868a Add a ptr-to-int map 2017-06-28 19:09:38 -07:00
Chris Robinson 9fd7349220 Add forwarding for the AL functions 2017-06-28 17:02:43 -07:00
Chris Robinson cfec20830b Load driver dlls in the router 2017-06-28 16:41:38 -07:00
Chris Robinson ebee8da05c Start a router DLL
Experimental, Windows only. This is intended as an alternative to Creative's
router DLL, fixing a few issues with it (falsely reporting extensions that
aren't supported, not being able to query the ALC version without a device, and
not being able to use ALC extension functions).

When enabled OpenAL Soft's DLL is built as soft_oal.dll, while the router is
OpenAL32.dll.
2017-06-28 12:42:20 -07:00
Chris Robinson 7cadbebe9f Calculate the converter stepping value using floating point 2017-06-28 10:17:36 -07:00
Chris Robinson e9a7218a06 Remove the fastf2u conversion function 2017-06-27 07:25:08 -07:00
Chris Robinson 8f2bbc434c Use a macro to apply NFC filtered mixes instead of a loop 2017-06-26 11:19:27 -07:00
Chris Robinson b13fead555 Round the converter's stepping value 2017-06-26 11:04:23 -07:00
Chris Robinson 2f2d941edb Trace the message name in the message handler loop 2017-06-26 08:57:29 -07:00
Chris Robinson 740e7d979c Convert all input samples in the loop
Instead of potentially leaving 1 sample that requires another loop iteration.
2017-06-26 08:55:22 -07:00
Chris Robinson 464a7c0545 Update ChangeLog for mmdevapi fix 2017-06-26 07:09:17 -07:00
Chris Robinson 55c329b462 Clean up some messy rounding code 2017-06-26 06:54:45 -07:00
Chris Robinson 5d5eff7502 Ensure the mmdevapi capture buffer is at least 100ms 2017-06-26 06:14:11 -07:00
Chris Robinson 4b7cbb50ab Add a whitenoise generator to altonegen 2017-06-25 08:00:55 -07:00
Chris Robinson be552a35e6 Support 32-bit float with the recording example 2017-06-25 06:10:20 -07:00
Chris Robinson 1deb8b6160 Clean up some loop variables 2017-06-25 05:42:35 -07:00
Chris Robinson c465718ddd Use the bsinc resampler for the converter 2017-06-25 04:07:06 -07:00
Chris Robinson d1bb04d588 Improve traces for the mmdevapi capture conversions 2017-06-23 09:54:26 -07:00
Chris Robinson d70a98fe03 Use the correct destination channel offset 2017-06-23 08:25:47 -07:00
Chris Robinson 9d01ac440d Don't report any output samples for no input samples 2017-06-23 08:13:42 -07:00
Chris Robinson 44a940d8d6 Forward Sample_ALuint to Sample_ALint 2017-06-23 05:34:43 -07:00
Chris Robinson e07166e93c Add a recording example app 2017-06-23 05:19:24 -07:00
Chris Robinson d1077795de Stop conversion when no more source samples are available 2017-06-22 15:07:24 -07:00
Chris Robinson 31b02e044f Trace the capture converter formats for mmdevapi 2017-06-22 13:58:32 -07:00
Chris Robinson 552d3a85af Workaround log2f missing on Android 2017-06-21 23:05:11 -07:00
Chris Robinson 6fcbb7c738 Remove an unnecessary variable 2017-06-21 11:34:26 -07:00
Chris Robinson e09468cdca Trace if dithering is enabled 2017-06-19 00:17:11 -07:00
Chris Robinson 36edd80073 Update ChangeLog for the dither-depth config option 2017-06-18 04:14:56 -07:00
Chris Robinson 0a361fa9e2 "Convert" the QSA backend to the new API
I say "convert" because it takes the lazy way and essentially just embeds the
wrappers into the backend. It's done this way because I lack the means to check
any changes, even syntactically. This also means the device's ExtraData field
is still needed.

However, this does mean all the backends are now using the new API. Code
related to the old interface can now be removed.
2017-06-18 03:07:02 -07:00
Chris Robinson 2b013fc54e Make the dithering depth configurable 2017-06-17 23:09:51 -07:00
Chris Robinson e3a825b37c Apply dither separately from output 2017-06-17 02:42:01 -07:00
Chris Robinson 9fc01934c2 Use helpers to get data from byte streams 2017-06-16 22:58:13 -07:00
Chris Robinson 879b79740f Round the B-Format HRTF response where the multiple is defined 2017-06-16 19:00:00 -07:00
Chris Robinson e18f7ca3e4 Update ChangeLog with the PulseAudio fix 2017-06-15 23:13:25 -07:00
Chris Robinson d4f3490a88 Limit device buffer based on PulseAudio's tlength
Unfortunately PulseAudio has a habit of limiting tlength, and trying to
calculate the device's buffer length to write regardless of tlength could
result in some amount always being writable.
2017-06-15 21:39:09 -07:00
Chris Robinson 1e8feeff03 Update ChangeLog with recent fixes 2017-06-11 16:38:51 -07:00
Chris Robinson a35b9bbd3e Don't force a fade-in when resuming a paused source
This needs to be handled more automatically by the mixer to work correctly.
Otherwise, requiring a property update on resume can put the source into a
playing state with the mixer never playing it, due to not having valid mixing
parameters and the mixing parameters not getting calculated because no updates
are specified by the app (and forcing an update can break deferred updates).
2017-06-09 13:32:34 -07:00
kcat ba0644254c Merge pull request #123 from rdb/master
Implement GetProcPath for FreeBSD
2017-06-09 12:14:56 -07:00
rdb 39e4756b37 Implement GetProcPath for FreeBSD 2017-06-09 15:19:03 +02:00
Chris Robinson b4aea294c3 Calculate chorus and flanger mod delays separately from feedback 2017-06-07 12:42:54 -07:00
Chris Robinson 10ff6cba9c Make the late lines' delay the delay average for modulation
Similar to the recent chorus and flanger changes, the modulation delay now
swings between -n to +n, where n is less than the delay length. This brings up
a slight issue with the linear interpolation, as modff doesn't produce the
correct fraction value for interpolation (it's inverted, with 0 being closer to
the next sample and 1 being closer to the base). So it's using nearest
interpolation for now.
2017-06-07 10:39:19 -07:00
Chris Robinson 61e43d4039 Release 1.18.0 2017-06-04 07:31:22 -07:00
Chris Robinson 9222d89de2 Update ChangeLog about OpenSL capture support 2017-06-04 05:44:08 -07:00
Chris Robinson e1dcfa4b9b Update default 48khz HRTF dataset 2017-06-01 04:09:08 -07:00
Chris Robinson 1c04cbad04 Resample HRIRs prior to minimum phase reconstruction 2017-06-01 04:06:08 -07:00
Chris Robinson f54946f9cb Remove unused HIDDEN_DECL macro 2017-05-30 09:58:06 -07:00
Chris Robinson e893211b65 Restrict ClampedDist to RefDistance for invalid distance attenuation 2017-05-30 05:13:54 -07:00
Chris Robinson 6d4adc6ad6 Use an RMS limit of -3dB for the output limiter 2017-05-29 03:38:27 -07:00
Chris Robinson 07bb5f1322 Add a missing include 2017-05-28 00:53:33 -07:00
Chris Robinson a79e8f3d95 Use peak limiting rather than RMS detection 2017-05-27 22:47:40 -07:00
Chris Robinson e9505b164e Fix source sends' initial HF absorption and decay calculation
The HF absorption is applied given the source distance, as relative to the
source's immediate environment, with additional absorption being applied given
the room/reverb environment. This does double up the amount of absorption
compared to the dry path, but it can be assumed the initial reflections travel
a longer distance.
2017-05-27 22:33:40 -07:00
Chris Robinson c51df897db Use normal air absorption for the sends
Applies just for the normal air absorption which uses the air absorption
factor, not the automated decay applied when WetGainAuto is enabled.
2017-05-27 03:40:52 -07:00
Chris Robinson c4ef7399f8 Add a new compressor/limiter
This is just for the output limiter right now, but in the future can be used
for the compressor EFX effect. The parameters are also hardcoded, but can be
made configurable after 1.18.
2017-05-27 03:36:34 -07:00
Chris Robinson 653f0a1405 Fix handling chorus and flanger LFO displacement offset
The phase offset is modulo-wrapped rather than masked, so it's best to avoid
negative offsets.
2017-05-26 09:09:35 -07:00
Chris Robinson 2b14c1d623 Properly handle the chorus and flanger LFOs
The effects' specified delay is the average delay time, meaning the delay
offset should move between -n and +n relative to the delay, where n <= delay.
2017-05-26 08:52:07 -07:00
Chris Robinson 59d016dfcd Update ChangeLog about ALC_SOFT_output_limiter 2017-05-25 04:17:09 -07:00
Chris Robinson db90dbe9f2 Finalize ALC_SOFT_output_limiter 2017-05-25 04:16:07 -07:00
Chris Robinson 9d4f601a8a Apply distance compensation separately 2017-05-25 03:24:35 -07:00
Chris Robinson c68a537ae8 Update ChangeLog for the limiter and dithering 2017-05-24 04:22:03 -07:00
Chris Robinson 2266fb76b9 Add a config option to specify custom ALSA devices 2017-05-23 18:12:45 -07:00
Chris Robinson dd6c5270b6 Add a dithering option to alsoft-config 2017-05-23 01:12:49 -07:00
Chris Robinson e6be113903 Add an option to dither 8- and 16-bit output 2017-05-23 00:35:22 -07:00
Chris Robinson 49e5c53591 Reduce the amount of variables that hold the same value 2017-05-21 03:47:52 -07:00
Chris Robinson 95ea3fdd05 Avoid calculating the filter coefficients multiple times 2017-05-21 03:38:19 -07:00
Chris Robinson 5691dceb38 Add a method to copy a filter's coefficients 2017-05-21 03:31:44 -07:00
Chris Robinson edcdc1dae8 Avoid unnecessary doubles 2017-05-21 02:42:44 -07:00
Chris Robinson 0b2467ed54 Use a macro to specify the decay target gain 2017-05-21 00:01:39 -07:00
Chris Robinson c234b25ac7 Use more correct doppler shift calculations 2017-05-20 03:28:40 -07:00
Chris Robinson 492050b816 Restore spec-defined cone behavior for auxiliary sends 2017-05-20 02:22:11 -07:00
Chris Robinson a306407b67 Apply more proper air absorption to the wet path
This properly accounts for the room rolloff factor for normal air absorption
(which makes it none by default, like distance attenuation), and uses the
reverb's decay time, decay hf ratio, decay hf limit, and room air absorption
properties to calculate an initial hf decay with the WetGainAuto flag. This
mirrors the behavior of the initial distance decay.
2017-05-19 23:13:39 -07:00
Chris Robinson efd797a6f6 Reorganize some code to have fewer temporaries 2017-05-17 22:49:34 -07:00
Chris Robinson a49e2ebbc5 Add an env var to specify a default pulse device
Some apps don't allow selecting an audio device, and due to problems with KDE,
PulseAudio isn't allowed to move the stream after being created by default.
2017-05-16 16:50:43 -07:00
Chris Robinson 154e53b911 Reduce the main reverb filter gain limit to match the rest 2017-05-16 13:23:16 -07:00
Chris Robinson 98392fbe90 Limit the dry and wet path filter gains to -60dB 2017-05-15 17:20:09 -07:00
Chris Robinson fecf26318a Improve distance-related absorption and decay attenuation 2017-05-15 17:13:05 -07:00
Chris Robinson 2aa620e58f Make reverb delay line structs use interleaved floats 2017-05-15 00:30:47 -07:00
Chris Robinson b2760baa64 Make the reverb's early and late feedback lines interleaved 2017-05-14 23:28:22 -07:00
Chris Robinson cc1b774837 Don't enable NFC for a 0 reference delay 2017-05-14 18:50:22 -07:00
Chris Robinson 232f05be93 Update ChangeLog for AL_SOFT_source_spatialize 2017-05-13 19:52:37 -07:00
Chris Robinson 7cbce6806b Update a couple comments about the reverb modulation 2017-05-13 16:08:45 -07:00
Chris Robinson 0b66b2bbe7 Replace 4 separate all-passes with one vector all-pass
Each 4 related all-passes now share a structure with one delay line, which uses
an interleaved sample history. Also fixes some potential rounding problems for
delay lines with interleaved samples.
2017-05-13 15:45:05 -07:00
Chris Robinson 87d4710bc4 Apply reverb modulation to the late feedback lines
This seems to be more in-line with the intended behavior, to allow build-up and
overlap within the reverb decay, rather than a pitch-shift on input.
Unfortunately there's no readily available implementation of this reverb model
that includes modulation to compare with, so a low depth coefficient is used to
keep it very subtle.
2017-05-12 20:53:14 -07:00
Chris Robinson d456c799fd Remove the 0.5 gain on the reverb output
Turns out to be unnecessary, as it reduced the volume below what other reverb
implementations provide with the same presets.
2017-05-11 23:26:29 -07:00
Chris Robinson 685dc24299 Restore the previous reverb B2A and A2B matrices
Also, untranspose the A2B matrix.
2017-05-11 21:48:47 -07:00
Chris Robinson ef58a8f205 Log whether the output limiter is enabled or disabled 2017-05-11 16:29:05 -07:00
Chris Robinson 748dfb1526 Finalize AL_SOFT_source_spatialize 2017-05-11 14:38:04 -07:00
Chris Robinson a6f6533a28 Update ALC_OUTPUT_LIMITER_SOFT to handle ALC_DONT_CARE_SOFT
Essentially just adding a comment about it. Since we default to on, the
behavior already fit.
2017-05-11 11:04:25 -07:00
Chris Robinson a2c25378a9 Reduce LIMITER_VALUE_MAX
The previous value couldn't actually be expressed as a float and got rounded up
to the next whole number value, leaving the potential for an overrun in the
squared sum.
2017-05-09 11:56:03 -07:00
Chris Robinson 4a4442ad91 Store the output limiter values as fixed-point integers
This helps keep the squared sum stable over larger updates, also avoiding the
need to keep recalculating it.
2017-05-08 16:23:16 -07:00
Chris Robinson f880f67049 Update reverb conversion matrices and output gain 2017-05-08 13:57:31 -07:00
Chris Robinson 0dabe6398f Apply attenuation when downmixing multi-channel sounds for panning 2017-05-07 18:28:43 -07:00
Chris Robinson 5308ea7e2a Put the app name after filename in the window title 2017-05-07 04:29:18 -07:00
Chris Robinson d9d2e73228 Update AL_SOURCE_SPATIALIZE_SOFT value
Though it didn't strictly clash since it was for a different component (global
state vs source property), 0x1213 was used by AL_RESAMPLER_NAME_SOFT. Probably
best to avoid duplicate property values regardless.
2017-05-06 10:10:10 -07:00
Chris Robinson e1bc4c0730 Include the Built-In HRTF names in alsoft-config when enabled 2017-05-05 07:49:56 -07:00
Chris Robinson 074e4496ba Calculate the output limiter gain using the RMS 2017-05-05 07:38:26 -07:00
Chris Robinson 64f0630fef Move native-tools to the root directory 2017-05-05 05:03:50 -07:00
Chris Robinson 47f843632f Make the generated data array static const 2017-05-05 04:59:07 -07:00
Chris Robinson db6f14748c Rename RollOff to Rolloff 2017-05-05 04:54:07 -07:00
Chris Robinson d82e3be8f7 Increase the HRTF transition to 128 samples 2017-05-05 04:54:07 -07:00
kcat af997bf853 Merge pull request #112 from alexey-lysiuk/embed_hrtf_pr
Cross-platform embedding of HRTF data
2017-05-05 04:53:11 -07:00
alexey.lysiuk 24bc8070c8 Enable embedding of HRTF data in CI config files 2017-05-05 14:30:06 +03:00
alexey.lysiuk 17dfaca43d Implement cross-platform embedding of HRTF data 2017-05-05 14:30:06 +03:00
Chris Robinson 9c9ad2f60a Start an extension to change the source's spatialize property 2017-05-05 02:41:34 -07:00
Chris Robinson b639bc9913 Add a property to force source spatialization on or off 2017-05-04 12:27:10 -07:00
Chris Robinson 7829ad8fc9 Handle attenuated sources in CalcPanningAndFilters 2017-05-04 11:09:45 -07:00
Chris Robinson 23bf3d3959 Calculate the multi-channel panning in a separate function 2017-05-04 04:35:53 -07:00
Chris Robinson 14b23c2502 Print available resamplers in openal-info 2017-05-03 04:53:22 -07:00
Chris Robinson e0ae3c7cb6 Update changelog 2017-05-03 04:41:09 -07:00
Chris Robinson ab1cca729f Finalize AL_SOFT_source_resampler 2017-05-03 04:33:38 -07:00
Chris Robinson 732dee5375 Rename Zero-Order Hold to Nearest
A bit of a misnomer now since "Nearest" implies rounding (i.e. when the sample
offset is >= .5, it should pick the next sample being closer in time), but that
adds unnecessary complications.
2017-05-03 03:34:44 -07:00
Chris Robinson 444e9563b3 Add a mixing function to blend HRIRs
This is a bit more efficient than calling the normal HRTF mixing function
twice, and helps solve the problem of the values generated from convolution not
being consistent with the new HRIR.
2017-05-03 03:29:21 -07:00
Chris Robinson 4e5c4b8e01 Add a slight bit of attenuation to the reverb output 2017-05-02 15:55:58 -07:00
Chris Robinson 66f9640787 Remove a duplicate check 2017-05-02 07:34:28 -07:00
Chris Robinson 53c3d48fe0 Change some ALuint parameters to ALsizei 2017-05-02 04:54:59 -07:00
Chris Robinson 1e5334176e Rename VOICE_IS_HRTF to VOICE_HAS_HRTF 2017-05-02 04:25:08 -07:00
Chris Robinson 2f8d597f4e Rename 'moving' flag to 'fading' 2017-05-02 04:09:01 -07:00
Chris Robinson afe2065d60 Set a voice as 'moving' if it starts/resumes at an offset 2017-05-02 03:58:18 -07:00
Chris Robinson f1a249b47a Reimplement bilinear interpolation between HRIRs
Some data sets are just too sparse, having noticeably few measurements to
properly handle slowly panning sources. Although not perfect, bilinearly
interpolating the HRIR measurements improves the positional accuracy.
2017-05-01 15:46:25 -07:00
Chris Robinson 8d50b72d8f Allow querying the output limiter state 2017-04-30 08:54:49 -07:00
Chris Robinson fc2afa1eaa Start an extension to toggle the output limiter 2017-04-30 04:21:48 -07:00
Chris Robinson 9767f4f9c3 Don't do more reverb samples than there are to fade.
This avoids having to clamp the fade value when incrementing it.
2017-04-28 10:25:22 -07:00
Chris Robinson dc25370063 Fade HRTF coefficients over 64 samples at most
This greatly improves HRTF performance since the dual-mix only applies to the
64-sample coefficient transition. So rather than doubling the full mix, it only
doubles 64 samples out of the full mix.
2017-04-28 10:05:57 -07:00
Chris Robinson bf138fb4ea Combine mostly duplicate functions 2017-04-27 19:23:42 -07:00
Chris Robinson 5dc7265ad3 Properly update the delay line offsets when fading is done 2017-04-27 18:54:33 -07:00
Chris Robinson 55d9988f3f Write directly to the output for HRTF 2017-04-27 06:35:53 -07:00
Chris Robinson 8f56c9522e Document the output-limiter config option
Expose it in alsoft-config as well.
2017-04-26 19:27:50 -07:00
Chris Robinson ca5c732261 Implement a limiter on the device output
This reduces the output volume when the mixed samples extend outside of -1,+1,
to prevent excessive clipping. It can reduce the volume by -80dB in 50ms, and
increase it by +80dB in 1s (it will not go below -80dB or above 0dB).
2017-04-26 18:38:09 -07:00
Chris Robinson 1754d54c18 Compile with -fno-math-errno when available
Helps GCC to inline some fp functions, e.g. lrintf
2017-04-26 18:35:05 -07:00
Chris Robinson 12fb0404c1 Reverb code update
This update modifies the reverb in numerous ways. The 3-series, 4-parallel
all-pass is replaced with a Gerzon vector all-pass. The vector all-pass is also
applied to the early reflections, to help with the initial diffusion in the
late reverb. The late reverb filter and feedback attenuation has been replaced
with a dual first-order equalization filter, which controls the low and high
frequencies with individual low-pass/high-shelf and high-pass/low-shelf filters
with gain control.

Additionally, delay lines now have the ability to blend the output between two
offsets, to help smooth out the transition for when the delay changes (without
such, it could result in undesirable clicks and pops).
2017-04-26 02:07:51 -07:00
Chris Robinson ad782c0000 Skip mixing the fade out step when starting silent
Unfortunately it can't skip mixing the fade in when going to silence because
the history needs to be up to date.
2017-04-25 22:10:27 -07:00
Chris Robinson a0a41921fc Remove const from _Atomic vars to make Clang happy
Clang does not allow using C11's atomic_load on const _Atomic variables.
Previously it just disabled use of C11 atomics if atomic_load didn't work on a
const _Atomic variable, but I think I'd prefer to have Clang use C11 atomics
for the added features (more explicit memory ordering) even if it means a few
instances of breaking const.
2017-04-21 16:58:55 -07:00
Chris Robinson d85177cd3e Use more sensible values for the source resampler enums 2017-04-21 16:26:22 -07:00
Chris Robinson d2d5f1d7bd Add the ability to change the source resampler 2017-04-21 15:48:39 -07:00
Chris Robinson 44f026220f Correctly enable the ambisonic upsampler for HOA output 2017-04-21 13:26:29 -07:00
Chris Robinson b59359f80f Add a method to enumerate resamplers 2017-04-21 04:15:08 -07:00
Chris Robinson 26b49c54af Store the resampler as part of the source 2017-04-21 00:06:40 -07:00
Chris Robinson 1e8ea59564 Make the default resampler a variable 2017-04-20 23:21:46 -07:00
Chris Robinson 7776ebcedc Try NEON mixers before SSE 2017-04-20 20:58:32 -07:00
Chris Robinson 63baa3b1c7 Missed a raw atomic variable access 2017-04-20 03:14:49 -07:00
Chris Robinson 5dcbb8db38 Make the buffer list next pointer atomic 2017-04-19 19:54:17 -07:00
Chris Robinson fbb5295f13 Fix a mixed-sign-comparison warning on MSVC 2017-04-19 15:17:55 -07:00
Chris Robinson 0407285c53 Allocate a new context's voices after updating the device params 2017-04-19 12:34:45 -07:00
Chris Robinson f1be335486 Check for the upsampler to determine if HRTF uses HOA 2017-04-18 17:39:10 -07:00
Chris Robinson 55011d4bfd Use a different way to get the size of structs with flexible array members 2017-04-18 14:11:15 -07:00
Chris Robinson de62ab97e9 Store the source queue head in the voice to signify looping
This removes the need to access a couple more source fields in the mixer, and
also makes the looping and queue fields non-atomic.
2017-04-18 00:58:33 -07:00
Chris Robinson 45d52f7124 Remove unnecessary functions in the JACK backend 2017-04-17 21:31:20 -07:00
Chris Robinson 14bc7baeb7 Store the source prop updates with the mixer voice
Also move its declaration and rename it for consistency.
2017-04-17 21:16:01 -07:00
Chris Robinson 660971d0b7 Close some gaps in enum values 2017-04-16 23:22:30 -07:00
Chris Robinson 6d3973f965 Trace unhandled device reset attributes 2017-04-16 16:21:11 -07:00
Chris Robinson 064176d03d Remove some unnecessary parenthesis 2017-04-16 15:05:57 -07:00
Chris Robinson 8f1a968d79 Correctly handle the attribute array size for alcGetInteger64vSOFT 2017-04-15 20:10:32 -07:00
Chris Robinson fca83263f4 Implement capture support in the OpenSL backend 2017-04-15 18:08:52 -07:00
Chris Robinson d9bf4f7620 Allow increasing the maximum source limit
If the requested number of mono and stereo sources exceeds 256, the source
limit will be expanded. Any config file setting overrides this. If the device
is reset to have fewer sources than are currently allocated, excess sources
will remain and be usable as normal, but no more can be generated until enough
are delated to go back below the limit.
2017-04-14 23:50:49 -07:00
Chris Robinson afb59e7f98 Move internal headers out of the include directory 2017-04-14 18:15:56 -07:00
Chris Robinson c5310d2e95 Avoid unnecessary macro parameters 2017-04-14 17:55:23 -07:00
Chris Robinson f94fa5d5cf Use separate atomic macros for pointers 2017-04-14 17:47:55 -07:00
Chris Robinson 9e60eea93b Use atomic flags for the thunk array 2017-04-14 16:14:05 -07:00
Chris Robinson 6476f3277a Mark some pointers with restrict 2017-04-13 11:48:43 -07:00
Chris Robinson 24c172bb96 Use ALsizei for the fir4 resampler fraction 2017-04-12 22:45:54 -07:00
Chris Robinson 684823ebc4 Select NEON when available before SSE 2017-04-12 22:44:16 -07:00
Chris Robinson 901804d724 Store the ambisonic order separate from the channel enum 2017-04-12 18:26:07 -07:00
Chris Robinson 46046f9caa Remove an unnecessary variable 2017-04-11 12:06:57 -07:00
Chris Robinson 05531fbee4 Use the correct channel conversion functions 2017-04-11 11:47:23 -07:00
Chris Robinson 78d5492d2c Use the converters to enable mmdevapi capture 2017-04-11 09:41:23 -07:00
Chris Robinson bcdd1cee10 Add a mono<->stereo converter
This converter always outputs floats, and uses energy-preserving scaling.
2017-04-11 07:25:55 -07:00
Chris Robinson caae349fdc Update the given source pointer in the sample converter 2017-04-10 13:40:45 -07:00
Chris Robinson cc79cb803a Reduce the size of the temp input buffer 2017-04-10 09:31:25 -07:00
Chris Robinson 8a7bc9ab4f Trace the capture device format 2017-04-10 09:28:25 -07:00
Chris Robinson 6cc69c8d94 Add a sample converter
This is intended to do conversions for interleaved samples, and supports
changing from one DevFmtType to another as well as resampling. It does not
handle remixing channels.

The mixer is more optimized to use the resampling functions directly. However,
this should prove useful for recording with certain backends that won't do the
conversion themselves.
2017-04-10 09:26:06 -07:00
Chris Robinson 81527cdbdd Convert the CoreAudio backend to the updated backend API 2017-04-09 11:21:02 -07:00
Chris Robinson aef774a7a0 Handle the source offset fraction as an ALsizei 2017-04-08 14:29:08 -07:00
Chris Robinson 319d097198 Pre-compute the sinc4 resampler coefficient table 2017-04-08 13:43:19 -07:00
Chris Robinson 5ef7d8fe62 Clean up some formatting 2017-04-08 12:27:30 -07:00
Chris Robinson 1f64f9d016 Try to write the full configured buffer length with PulseAudio
This basically ignores tlength even if it's smaller than what was requested. It
keeps up-to-date with minreq changes too now, in case that happens.
2017-04-08 10:01:04 -07:00
Chris Robinson f1a5b6b668 Overwrite the old search path with the new one 2017-04-08 03:00:53 -07:00
Chris Robinson 5e9f5693a4 Combine a couple loops 2017-04-07 14:56:23 -07:00
Chris Robinson 7fd88086f6 Make sure malloc succeeded for 'delays' 2017-04-07 09:49:06 -07:00
Chris Robinson 8f2e4d46ec Store the HRTF coeffs as a stereo pair
This will make it easier to handle HRTF data sets that have separate left and
right ear responses. Will need an mhr version update to take advantage of that.
2017-04-07 08:46:50 -07:00
Chris Robinson e267f6b88e Don't explicitly restore the old HRTF when initializing panning
Otherwise it won't store the name in the device.
2017-04-07 08:02:13 -07:00
Chris Robinson b551291840 Allocate temp storage for delays when loading HRTFs 2017-04-07 06:40:42 -07:00
Chris Robinson 36f7dda1ca Remove another reference to the sinc8 resampler 2017-04-07 03:57:40 -07:00
Chris Robinson 710bbde09c Ensure SDL_AUDIO_BITSIZE is defined for older SDL2 versions 2017-04-07 03:33:03 -07:00
Chris Robinson 70a097bf59 Clean up a comment 2017-04-07 03:21:40 -07:00
Chris Robinson 338d61f907 Reference count HRTFs and unload them when unused 2017-04-06 13:00:29 -07:00
Chris Robinson 94f514ae5a Load embedded HRTF entries as-needed 2017-04-06 01:35:09 -07:00
Chris Robinson 37f666fbab Fix an incorrect message 2017-04-05 12:46:02 -07:00
Chris Robinson 2eaa10fc21 Load HRTF files as needed
Currently only applies to external files, rather than embedded datasets. Also,
HRTFs aren't unloaded after being loaded, until library shutdown.
2017-04-05 12:27:30 -07:00
Chris Robinson f76dea0c03 Store the loaded hrtf entry container in the enumerated hrtf entry 2017-04-05 11:29:58 -07:00
Chris Robinson e7ca61e8b5 Store the HRTF's filename separate from the entry storage 2017-04-05 07:09:16 -07:00
Chris Robinson 26144ca9df Rename al_string_* functions to alstr_* 2017-04-04 06:58:53 -07:00
Chris Robinson b78ddc7ef7 Make sure the mix is done after setting the looping property 2017-04-02 06:40:56 -07:00
Chris Robinson d52752a3fe Recognize %-encoded characters for config section names 2017-04-01 06:07:14 -07:00
Chris Robinson 497d078f50 Remove a couple unused macros 2017-04-01 03:04:15 -07:00
Chris Robinson 13d7d6fe95 Don't rely on sizeof being the offset to a struct's unsized array 2017-04-01 03:00:24 -07:00
Chris Robinson 52112b0b8d Constify a variable 2017-04-01 02:38:44 -07:00
Chris Robinson 1ef916a54b Make a pointer restrict and assume aligned 2017-04-01 01:08:18 -07:00
Chris Robinson 5f245d5950 Avoid some unnecessary string reallocation 2017-03-31 23:22:06 -07:00
Chris Robinson af833c8554 Remove a couple more uses of BYTE3 2017-03-31 09:21:31 -07:00
Chris Robinson 90c005bbec Convert float samples to integer using a power-of-2 multiple 2017-03-31 09:11:28 -07:00
Chris Robinson 355a8898cf Remove the (u)byte3 sample formats
They're not accessible since the removal of the buffer_samples extension, and
were kind of clunky to work with as 24-bit packed values.
2017-03-31 08:15:20 -07:00
Chris Robinson ac8b4aa5f6 Convert integer samples to float using a power-of-2 divisor
This should cut down on unnecessary quantization noise (however minor) for 8-
and 16-bit samples. Unfortunately a power-of-2 multiple can't be used as easily
for converting float samples to integer, due to integer types having a non-
power-of-2 maximum amplitude (it'd require more per-sample clamping).
2017-03-31 06:54:46 -07:00
Chris Robinson 9fb07101dc Load HRTF coefficients as pre-normalized floats 2017-03-31 04:59:09 -07:00
Chris Robinson 2a8970368f Combine some HRTF loading code 2017-03-31 03:45:26 -07:00
Chris Robinson 7dc3fb98ab Use the correct types' sizes for HRTF storage 2017-03-31 02:15:24 -07:00
Chris Robinson c68dac879c Update the ambisonic docs to mention the near-field effect 2017-03-29 00:11:38 -07:00
Chris Robinson facd8ab109 Make ReleaseContext return if any contexts still remain 2017-03-28 05:33:43 -07:00
Chris Robinson 70aefa75e2 Use an array of pointers for effects instead of a linked list 2017-03-27 23:16:23 -07:00
Chris Robinson b49a79a15f Require CMake 3.0.2
Seems it's necessary for the INCLUDES install property with the cmake config
export.
2017-03-26 21:24:20 -07:00
Chris Robinson 7f55d34a7d Add include/AL to the exported includes destination 2017-03-26 15:11:15 -07:00
Chris Robinson 9f4e47d7be Fix HRTF interpolated gain calculation 2017-03-26 02:44:34 -07:00
Chris Robinson 56428cdb74 Use a loop to apply NFC filters 2017-03-25 23:55:44 -07:00
Chris Robinson 1d559ec703 Properly update the resampler label in the UI 2017-03-24 15:34:36 -07:00
Chris Robinson 5c37eca2c5 Use ALsizei for more index lookups 2017-03-24 14:35:25 -07:00
Chris Robinson 9ca92be0b8 Properly calculate the echo damping 2017-03-24 14:08:04 -07:00
Chris Robinson 1aca344688 Fix handling of the PropsClean flags 2017-03-23 19:32:53 -07:00
Chris Robinson 1c49d0542d Use an atomic flag to mark auxiliary effect slot updates 2017-03-23 19:16:32 -07:00
Chris Robinson 5404b2225a Add some comments for ALsource functions 2017-03-23 01:16:13 -07:00
Chris Robinson 85042a74ee Update ChangeLog 2017-03-22 03:13:36 -07:00
Chris Robinson b062d50bf1 Fix setting Ambi formats for loopback devices 2017-03-21 16:40:23 -07:00
Chris Robinson cdfe0d8f5a Use an atomic flag to test if a source needs to update 2017-03-20 21:25:39 -07:00
Chris Robinson 42bcf0870d Make DataPosInt an ALsizei 2017-03-20 19:22:41 -07:00
Chris Robinson d7d9ad806a Use proper bools for boolean states 2017-03-20 16:53:41 -07:00
Chris Robinson e06cf07ab0 Break up a function and move the code to where it's called 2017-03-19 16:49:23 -07:00
Chris Robinson ecfa1dcb6f Don't defer source state or offset changes 2017-03-19 13:48:40 -07:00
Chris Robinson 9e1aa50518 Fix the lib name for the .pc file 2017-03-18 20:24:19 -07:00
Chris Robinson 66b86d43be Fix alcGetInteger64vSOFT to handle ambisonic attributes 2017-03-18 14:33:40 -07:00
Chris Robinson 7bf7cda467 Replace a couple ALuint with ALsizei 2017-03-17 15:45:39 -07:00
Chris Robinson a209edb5ee Fix NFC filter to set the correct center frequency 2017-03-16 19:58:21 -07:00
Chris Robinson 8f5cab5608 Increase the filter slope to -12dB/octave 2017-03-16 11:25:28 -07:00
Chris Robinson 32d521d79c Check usability of MixHrtf_Neon before MixHrtf_SSE 2017-03-14 19:16:59 -07:00
Chris Robinson 6d565ff1fd Remove a couple unneeded typedefs 2017-03-14 13:10:34 -07:00
Chris Robinson cdf63b553f Avoid doing sequential load for the source state 2017-03-12 19:27:51 -07:00
Chris Robinson 1e7c0b4646 Fix the height of some GUI widgets 2017-03-12 16:25:13 -07:00
Chris Robinson dc17f287fe Use a spinbox for the nfc-ref-delay value 2017-03-12 16:10:49 -07:00
Chris Robinson d9e2a0cbf0 Use QString's arg method to format the float value 2017-03-12 15:27:50 -07:00
Chris Robinson 948ce36fa9 Increase _XOPEN_SOURCE to 600 for Solaris 2017-03-12 14:42:02 -07:00
Chris Robinson 097ed84a87 Handle the nfc-ref-delay config option 2017-03-12 13:24:16 -07:00
Chris Robinson f276e83c8d Document the nfc config option and expose it in alsoft-config 2017-03-12 08:56:19 -07:00
Chris Robinson 7b4645f5f8 Store the HRIR coeff pointer and delays directly in MixHrtfParams 2017-03-12 06:58:27 -07:00
Chris Robinson 96aaab9366 Rework HRTF coefficient fading
This improves fading between HRIRs as sources pan around. In particular, it
improves the issue with individual coefficients having various rounding errors
in the stepping values, as well as issues with interpolating delay values.

It does this by doing two mixing passes for each source. First using the last
coefficients that fade to silence, and then again using the new coefficients
that fade from silence. When added together, it creates a linear fade from one
to the other. Additionally, the gain is applied separately so the individual
coefficients don't step with rounding errors. Although this does increase CPU
cost since it's doing two mixes per source, each mix is a bit cheaper now since
the stepping is simplified to a single gain value, and the overall quality is
improved.
2017-03-11 18:04:06 -08:00
Chris Robinson feffe1e81a Make the voice's 'moving' state a bitflag 2017-03-11 06:26:05 -08:00
Chris Robinson 98e8f941b7 Allocate as many channels for DirectHrtfState as needed 2017-03-11 06:20:04 -08:00
Chris Robinson 6b4b00e462 Dynamically allocate the device's HRTF state 2017-03-10 10:47:43 -08:00
Chris Robinson 51cb969446 Fix building on MSVC 2017-03-10 05:05:25 -08:00
Chris Robinson b878769ee0 Fix building without C11 2017-03-10 04:59:22 -08:00
Chris Robinson 583d431947 Implement NFC filters for Ambisonic rendering
NFC filters currently only work when rendering to ambisonic buffers, which
includes HQ rendering and ambisonic output. There are two new config options:
'decoder/nfc' (default on) enables or disables use of NFC filters globally, and
'decoder/nfc-ref-delay' (default 0) specifies the reference delay parameter for
NFC-HOA rendering with ambisonic output (a value of 0 disables NFC).

Currently, NFC filters rely on having an appropriate value set for
AL_METERS_PER_UNIT to get the correct scaling. HQ rendering uses the averaged
speaker distances as a control/reference, and currently doesn't correct for
individual speaker distances (if the speakers are all equidistant, this is
fine, otherwise per-speaker correction should be done as well).
2017-03-10 04:35:32 -08:00
Chris Robinson d9b1995e95 Add an NFC filter implementation 2017-03-09 15:41:20 -08:00
Chris Robinson 9454d3e776 Move ALvoice declaration to alu.h 2017-03-09 07:23:12 -08:00
Chris Robinson ae22d742dc Remove an unnecessary variable 2017-03-09 07:15:06 -08:00
Chris Robinson 5ffb0842ac Remove unnecessary atomic members 2017-03-08 04:59:22 -08:00
Chris Robinson b1b3a369ef Remove an unnecessary variable 2017-03-07 09:38:33 -08:00
Chris Robinson b64da108a9 Check that a source is actually playing before setting paused
Also slightly refactor setting playing state when the device is disconnected or
there's no buffers to play.
2017-03-07 04:50:09 -08:00
Chris Robinson 190120dfd7 Store the channel count and sample size in the voice 2017-03-07 00:19:40 -08:00
Chris Robinson bc0096365e Don't modify the source state in the mixer 2017-03-07 00:19:40 -08:00
Chris Robinson 1b3100ab9a Remove an unused function 2017-03-07 00:19:40 -08:00
kcat 0b591844b1 Merge pull request #97 from adrianbroher/ci-backends
Enable and enforce dependencies on CI services.
2017-03-07 00:19:13 -08:00
Marcel Metz 099b0a1f7c Only download and strip Android NDK when not cached 2017-03-06 11:39:39 +01:00
Marcel Metz 6a067d9f7e Use TravisCI cache to store stripped Android NDK toolchain 2017-03-06 11:21:51 +01:00
Marcel Metz 25de358c9a Unpack only required files from Android NDK 2017-03-06 11:21:51 +01:00
Marcel Metz a2d0bf8d3a Configure CMake to require available backends on CI services
Configure CMake to require the installed backend libraries.  This should
help to find build system regressions.

On TravisCI with Linux this requires the ALSA, PulseAudio, PortAudio, OSS
and JACK backend.

On TravisCI with Android cross compile Linux this requires the OpenSL
backend.

On TravisCI with MacOSX this requires the CoreAudio backend.

ON AppVeyor with Windows this requires the WinMM, DSound and MMDevAPI
backend.
2017-03-06 10:26:03 +01:00
Marcel Metz 1d208c49b5 Add Android cross-compile to TravisCI test matrix
The test entry adds the ability to test the OpenSLES backend.
2017-03-06 10:24:26 +01:00
Marcel Metz adf6fee6d1 Explicit declare test matrix for TravisCI 2017-03-06 10:23:11 +01:00
Marcel Metz 2a08871fba Delete Xamarin.Common.targets on AppVeyor
This is a workaround for a Xamarin build script bug specific to
AppVeyor.  For more details see:

http://help.appveyor.com/discussions/problems/4569
2017-03-06 10:23:11 +01:00
Marcel Metz 441180a08a Install dependencies on TravisCI to enable more features
Install Ubuntu development packages for PulseAudio, PortAudio, ALSA and
JACK to enable the building of most Linux backends on TravisCI.

Intall Ubuntu development packages for Qt5 to enable `alsoft-config`.
2017-03-05 16:14:18 +01:00
Chris Robinson 073829f26a Make the voice's source pointer atomic 2017-03-05 04:50:27 -08:00
Chris Robinson c040491615 Update alffplay for newer ffmpeg and convert to C++ 2017-03-05 01:20:19 -08:00
Chris Robinson 40c5fe4c33 Remove ex-common and test-common static libs 2017-03-04 23:02:52 -08:00
Chris Robinson 87fd288359 Remove unnecessary wrappers around SDL_sound
Also remove wrappers for the now-unsupported buffer_samples extension.
2017-03-04 22:30:57 -08:00
Chris Robinson c013068003 Use the LINK_FLAGS property instead of abusing libs for flags 2017-03-04 18:20:43 -08:00
kcat 9f9faff90a Merge pull request #95 from adrianbroher/export-config
Enable exporting of CMake import targets
2017-03-04 17:47:13 -08:00
Marcel Metz a11a13bdd1 Use Ubuntu 14.04 in TravisCI to get a less antique CMake version 2017-03-05 00:14:49 +01:00
Marcel Metz ad640245d8 Export cmake import targets for project build tree 2017-03-05 00:14:49 +01:00
Marcel Metz df87cf8002 Export cmake import targets for project install tree 2017-03-05 00:14:49 +01:00
Marcel Metz 27be429ca4 Rename logical CMake target openal to OpenAL 2017-03-05 00:14:49 +01:00
Marcel Metz 9d0bf065ee Compile common library within dependent targets 2017-03-04 22:20:56 +01:00
Marcel Metz 952fb94ff7 Make logical target name openal uniform accross all platforms 2017-03-04 21:08:07 +01:00
Chris Robinson 6d7a790183 Add a boolean to specify if a voice should be playing 2017-03-02 01:02:40 -08:00
Chris Robinson 0e8ca50d7a Stretch out some GUI elements for the decoder configurations 2017-03-01 20:53:52 -08:00
Chris Robinson d1833c7b94 Increment MixCount in UpdateClockBase
This is to protect clocktime reads since the backend lock won't protect it.
2017-02-28 23:18:51 -08:00
Chris Robinson 521abf2e07 Dynamically allocate the channel delay buffers 2017-02-28 21:01:13 -08:00
Chris Robinson 51092a6315 Remove unused function declarations 2017-02-28 19:58:20 -08:00
Chris Robinson 6f2a30dea2 Remove an unneeded function 2017-02-28 19:48:44 -08:00
Chris Robinson d3365f1b5b Start a ALC_SOFT_loopback2 extension
This extends the base ALC_SOFT_loopback extension with support for B-Format.
When ALC_FORMAT_CHANNELS_SOFT is set to ALC_BFORMAT3D_SOFT, then additional
attributes must be specified. ALC_AMBISONIC_LAYOUT_SOFT must be set to
ALC_ACN_SOFT or ALC_FUMA_SOFT for the desired channel layout,
ALC_AMBISONIC_SCALING_SOFT must be set to ALC_N3D_SOFT, ALC_SN3D_SOFT, or
ALC_FUMA_SOFT for the desired channel scaling/normalization scheme, and
ALC_AMBISONIC_ORDER_SOFT must be set to an integer value greater than 0 for the
ambisonic order (maximum allowed is implementation-dependent).

Note that the number of channels required for ALC_BFORMAT3D_SOFT is dependent
on the ambisonic order. The number of channels can be calculated by:
num_channels = (order+1) * (order+1); /* or pow(order+1, 2); */

In addition, a new alcIsAmbisonicFormatSupportedSOFT function allows apps to
determine which layout/scaling/order combinations are supported by the loopback
device. For example,
alcIsAmbisonicFormatSupported(device, ALC_ACN_SOFT, ALC_SN3D_SOFT, 2) will
check if 2nd order AmbiX (ACN layout and SN3D scaling) rendering is supported
for ALC_BFORMAT3D_SOFT output.
2017-02-28 19:01:48 -08:00
Chris Robinson f8558ed2b7 Use a variable counter for an array size limit 2017-02-28 04:21:16 -08:00
Chris Robinson 1cd6617ff6 Don't use the mutex in the base getClockLatency implementation 2017-02-28 03:50:42 -08:00
Chris Robinson 52d1f7883b Print WARNs when a device or context error is generated 2017-02-27 20:59:52 -08:00
Chris Robinson 45d6c34015 Avoid standard malloc for buffer queue entries 2017-02-27 20:43:16 -08:00
Chris Robinson a9610b3607 Use separate enums for the ambisonic channel order and normalization 2017-02-27 16:11:45 -08:00
Chris Robinson 5c859af24e Move the current buffer queue entry and play position to the voice
This has a couple behavioral changes. First and biggest is that querying
AL_BUFFERS_PROCESSED from a source will always return all buffers processed
when in an AL_STOPPED state. Previously all buffers would be set as processed
when first becoming stopped, but newly queued buffers would *not* be indicated
as processed. That old behavior was not compliant with the spec, which
unequivocally states "On a source in the AL_STOPPED state, all buffers are
processed."

Secondly, querying AL_BUFFER on an AL_STREAMING source will now always return
0. Previously it would return the current "active" buffer in the queue, but
there's no basis for that in the spec.
2017-02-27 15:35:15 -08:00
Chris Robinson 513c18fdc4 Ensure a non-playing or -paused source does not use a mixing voice 2017-02-25 18:10:09 -08:00
Chris Robinson 9539ccc18b Set CMP0020 for Qt 2017-02-25 16:29:27 -08:00
Chris Robinson eceeabaf2f Improve handling of source state reads
This avoids using seq_cst for loading the source state when either inside the
mixer, or otherwise protected from inconsistencies with async updates. It also
fixes potential race conditions with getting the source offset just as a source
stops.
2017-02-24 01:47:34 -08:00
Chris Robinson 652ef2b7fd Remove an unused function 2017-02-23 20:40:16 -08:00
Chris Robinson c2a79f0f7b Remove CalcXYZCoeffs and inline CalcAngleCoeffs 2017-02-23 16:44:59 -08:00
Chris Robinson 08948079e9 Alter how panpot/pair-wise panning works
This change allows pair-wise panning to mostly go through the normal ambisonic
panning methods, with one special-case. First, a term is added to the stereo
decoder matrix's X coefficient so that a centered sound is reduced by -3dB on
each output channel. Panning in front creates a similar gain response to the
typical
L = sqrt(1-pan)
R = sqrt(pan)
for pan = [0,1]. Panning behind the listener can reduce (up to) an additional
-10dB, creating a audible difference between front and back sounds as if
simulating head obstruction.

Secondly, as a special-case, the source positions are warped when calculating
the ambisonic coefficients so that full left panning is reached at -30 degrees
and full right at +30 degrees. This is to retain the expected 60-degree stereo
width. This warping does not apply to B-Format buffer input, although it
otherwise has the same gain responses.
2017-02-23 01:32:44 -08:00
Chris Robinson 0ce4c9b8fa Rename stereo-panning option to stereo-encoding
Also rename the 'paired' value to 'panpot', and make it the default.
2017-02-22 19:18:01 -08:00
Chris Robinson d04cc28f33 Limit filter gains to -24dB 2017-02-22 18:07:41 -08:00
Chris Robinson 629980d15e Update ChangeLog 2017-02-22 16:31:24 -08:00
Chris Robinson 55e3b840b3 Reduce the default period count to 3 2017-02-22 15:56:09 -08:00
Chris Robinson 2e1f1449bc Don't remove a period from the OSS buffer
Since we're now waiting for space to be available before mixing, the mixing
buffer isn't adding another period.
2017-02-22 15:44:47 -08:00
Chris Robinson e720faf2d4 Fix OpenSL latency calculation 2017-02-22 15:00:41 -08:00
Chris Robinson 5181e78c1e Reduce some code 2017-02-21 18:28:09 -08:00
Chris Robinson 2dd142fed0 Make the "sends" config option act as a limit
Instead of forcing the device to always use the specified send count, it simply
limits requests to it.
2017-02-21 17:23:54 -08:00
Chris Robinson d3cc867bd4 Increase the default effect slot and send count
The default number of auxiliary effect slots is now 64. This can still be
raised by the config file without a hard maximum, but incurs processing cost
for each effect slot generated by the app.

The default number of source sends is now actually 2, as per the EFX docs.
However, it can be raised up to 16 via ALC_MAX_AUXILIARY_SENDS attribute
requests, rather than the previous 4.
2017-02-21 16:54:55 -08:00
Chris Robinson 864d5387dd Dynamically allocate the ALsource Send[] array 2017-02-21 16:31:59 -08:00
Chris Robinson 29994aa2de Interleave the voice and source property objects 2017-02-21 12:29:25 -08:00
Chris Robinson cd24e42b3f Make the voices' Send[] array dynamically sized
The voices are still all allocated in one chunk to avoid memory fragmentation.
But they're accessed as an array of pointers since the size isn't static.
2017-02-21 11:17:47 -08:00
Chris Robinson e0e6efbfea Print warnings about missing libraries and functions 2017-02-21 10:17:48 -08:00
Chris Robinson bb4726d520 Avoid duplicating device buffer layout logic 2017-02-20 16:57:25 -08:00
Chris Robinson f5e8a8c75e Remove an unused flag enum 2017-02-20 09:25:09 -08:00
Chris Robinson 23a34f732b Remove mention of the sinc8 resampler 2017-02-20 09:13:08 -08:00
Chris Robinson 5a2ef2590f Allow distance compensation for non-HQ rendering as well
It still requires a custom configuration to specify appropriate speaker
distances.
2017-02-20 09:08:57 -08:00
Chris Robinson b23f81b686 Remove the separate surround51rear decoder option
Both 5.1 Side and Rear configurations use 'surround51' to look up the
appropriate decoder file. The decoder loader already handles mapping between
rear and side channels, so there's no need for separate options.
2017-02-19 22:59:55 -08:00
Chris Robinson 3761336e6c Apply distance compensation when writing to the output 2017-02-19 22:47:59 -08:00
Chris Robinson 9da152a9c8 Don't use periphonic FOA when the HOA decoder is not periphonic 2017-02-19 17:45:27 -08:00
Chris Robinson d45dd9c668 Remove the sinc8 resampler option
Perf shows less than 1 percent CPU difference from the higher quality bsinc
resampler, but uses almost twice as much memory (a 128KB lookup table).
2017-02-19 16:45:17 -08:00
Chris Robinson 247f56249a Always lock the device backend before calling aluMixData 2017-02-18 17:32:07 -08:00
Chris Robinson 2448f88e70 Return some device latency by default
A device will never have 0 latency. OpenAL Soft itself uses a sample buffer
length of UpdateSize*NumUpdates, and during playback will have about
(NumUpdates-1) periods filled, more or less. Without a more accurate
measurement from the playback system, this is better than reporting 0.
2017-02-18 16:55:48 -08:00
Chris Robinson d8c42918f4 Use select() to wait for audio with OSS and Solaris 2017-02-18 15:58:15 -08:00
Chris Robinson 909193a345 Reorganize ALvoice members
This places the Send[] array at the end of the struct, making it easier to
handle dynamically.
2017-02-15 17:40:26 -08:00
Chris Robinson 5a50c46c22 Make ALsourceProps' Send array dynamically sized
ALsourceProps' Send[] array is placed at the end of the struct, and given an
indeterminate size. Extra space is allocated at the end of each struct given
the number of auxiliary sends set for the device.
2017-02-14 19:59:39 -08:00
Chris Robinson 69dd570961 Fix build with non-C11 atomics 2017-02-13 21:30:20 -08:00
Chris Robinson 0d19a20901 Make the source state atomic
Since it's modified by the mixer when playback is ended, a plain struct member
isn't safe.
2017-02-13 21:18:18 -08:00
Chris Robinson 0324712540 Put BsincState in a generic union 2017-02-13 11:29:32 -08:00
Chris Robinson 841d0bb893 Porperly check for and use __builtin_assume_aligned 2017-02-13 07:36:49 -08:00
Chris Robinson 65f9b2792c Clean up the bsinc mixer a bit 2017-02-12 21:35:08 -08:00
Chris Robinson 27695e2b24 Add NEON-enhanced resamplers 2017-02-12 21:03:30 -08:00
Chris Robinson 427f484e01 Print separate messages for building sdl_sound and ffmpeg examples 2017-02-12 08:37:42 -08:00
Chris Robinson 6b030999cb Don't require SDL_sound for alffplay
Also explicitly link with libz for alffplay, since static ffmpeg libs need it.
2017-02-11 12:56:13 -08:00
Chris Robinson e92229f839 Fix more uses of unsigned sizes and offsets 2017-02-10 06:20:16 -08:00
Chris Robinson 5bd63ff03d Remove a couple context lock wrapper functions 2017-02-07 19:32:49 -08:00
Chris Robinson 7cc8ba99f0 Properly capitalize NEON 2017-02-07 18:33:12 -08:00
Chris Robinson 317d135b96 Clear trailing whitespace from the cpu features string 2017-02-07 11:19:41 -08:00
Chris Robinson c771b82a39 Use the correct IID for the opensl buffer queue 2017-02-07 10:55:36 -08:00
Chris Robinson 9f10ae466c Convert the OpenSL backend to the new backend API
This also removes the buffer queue callback's call to aluMixData, which could
potentially block on a mutex.
2017-02-07 07:06:41 -08:00
Chris Robinson af362c2d05 Fix for NULL JNIEnv
Which can happen with native-only apps
2017-02-05 14:25:17 -08:00
Chris Robinson 428cde5dc2 Call getSystemService as a non-static function 2017-02-03 09:41:21 -08:00
Chris Robinson 2c1791752a Android's AudioManager.getProperty(String) returns a String 2017-02-02 06:14:01 -08:00
Chris Robinson 071b83ba52 Replace more ALuint with ALsizei 2017-01-29 16:42:02 -08:00
Chris Robinson 55a2474d7e Fix late reverb low-pass filtering 2017-01-29 13:06:40 -08:00
Chris Robinson 3cc88b0aab Use an all-pass series on each late reverb line
This attempts to improve the smoothness of the late reverb decay by passing
each line through multiple all-pass filters. Some work is still needed to work
better in high-density and not-so-high-diffusion environments.

This also removes the decay from the early reflections, since it's no longer
continuous feedback.
2017-01-28 17:15:47 -08:00
Chris Robinson 6b2297b508 Add more traces for the Java calls being made 2017-01-27 15:42:06 -08:00
Chris Robinson 9c019126d4 Remove __android_log_print calls for TRACEREF
TRACEREFs aren't normally important, and for as often as it happens, the added
function calls are wasteful even if they end up doing nothing.
2017-01-27 15:17:11 -08:00
Chris Robinson 19e96c6fef Round and clamp the scaled update count with opensl 2017-01-27 01:46:44 -08:00
Chris Robinson 67ffdf7a78 Try to use the system sample rate with Android 2017-01-26 18:23:29 -08:00
Chris Robinson 3ba03c5a29 Also log to __android_log_print on Android 2017-01-26 18:15:19 -08:00
Chris Robinson f0c8b7f255 Get the JavaVM handle on Android targets 2017-01-26 14:33:03 -08:00
Chris Robinson 1ebfce4cac Improve the ambisonic upscaling methods
This now takes advantage of the differences seen in generated decoder matrices
for first-order compared to second- and third-order, such that with the
appropriate frequency-dependent scaling applied to first-order content, the
result is identical with a higher-order decoder matrix compared to a first-
order matrix for the same layout.
2017-01-24 19:03:51 -08:00
Chris Robinson f4d52f43d8 Fix coefficient counts for the built-in B-Format decoders 2017-01-21 12:28:54 -08:00
Chris Robinson 7025660e8b Use a flat sqrt(2) scale for non-directional ambient gains 2017-01-21 11:54:22 -08:00
Chris Robinson 371fda1803 Update the default basic B-Format decoders
This also converts them to ACN/N3D format.
2017-01-21 11:05:05 -08:00
Chris Robinson aa56af1ecb Move the B-Format HRTF virtual speaker stuff to InitHrtfPanning
This keeps the decoder matrices and coefficient mapping together for if it
changes in the future.
2017-01-18 19:16:24 -08:00
Chris Robinson e8ac0e5bfd Replace some ALvoid with void 2017-01-18 07:19:43 -08:00
Chris Robinson d2e5aa79dd Use ALsizei in more places 2017-01-18 07:13:23 -08:00
Chris Robinson ba0944af9b Pass the left and right buffers to the hrtf mixers directly 2017-01-17 16:49:26 -08:00
Chris Robinson bfb7a6e4c8 Small update for the "virtual" Ambi2DDecoder coefficients 2017-01-17 11:35:19 -08:00
Chris Robinson e9009968fb More ALsizei, with the B-Format decoder 2017-01-16 09:37:55 -08:00
Chris Robinson f1f93a593a Fix a couple hard-coded array sizes 2017-01-16 09:04:58 -08:00
Chris Robinson 959812ee13 Use ALsizei in a few more places 2017-01-16 08:59:08 -08:00
Chris Robinson 325a49975a Use ALsizei and ALint for sizes and offsets with resamplers and filters 2017-01-16 08:54:30 -08:00
Chris Robinson cbb796bf31 Use ALsizei for sizes and offsets with the mixer
Unsigned 32-bit offsets actually have some potential overhead on 64-bit targets
for pointer/array accesses due to rules on integer wrapping. No idea how much
impact it has in practice, but it's nice to be correct about it.
2017-01-16 08:06:25 -08:00
Chris Robinson 9f23d17333 Use second-order ambisonics for basic HRTF rendering
This should improve positional quality for relatively low cost. Full HRTF
rendering still only uses first-order since the only use of the dry buffer
there is for first-order content (B-Format buffers, effects).
2017-01-15 13:57:22 -08:00
Chris Robinson 8e868823fd Replace range-based for loops with QList iterators
Less than ideal, but some targets can't rely on C++11
2017-01-13 00:46:49 -08:00
Chris Robinson e254a3f0c2 Search for and use Qt5 for alsoft-config
An option is provided to instead use Qt4.8 still if desired.
2017-01-12 23:53:27 -08:00
Chris Robinson 6e806848eb Use C++11 for alsoft-config 2017-01-12 13:38:21 -08:00
Chris Robinson 24de5127b1 Update binary search algorithm for uintmaps 2017-01-12 10:09:39 -08:00
Chris Robinson 58f84170b6 Avoid using some LP types 2017-01-10 05:12:54 -08:00
Chris Robinson 43ab6075f9 Use proper atomics in the OSS backend 2017-01-10 03:17:23 -08:00
Chris Robinson e20f0ae5a3 Hold Pulse's mainloop lock while calling capture functions
Since commit c837484015, the backend's lock is no longer implicitly held when
calling capture functions. A separate mutex is used to ensure serial access,
and its up to the backend to protect against races and reentrancy with the
audio API.
2017-01-10 03:02:26 -08:00
Chris Robinson 987b6e069b One more update for the HRTF B-Format coefficients
These should better represent the pseudo-inverse matrices with N3D scaling.
2017-01-09 06:36:02 -08:00
Chris Robinson 18bb46163a Add missing AL_EFFECTSLOT_ properties for al(c)GetEnumValue 2017-01-05 20:06:24 -08:00
Chris Robinson da4f0c65c3 Update the B-Format HRTF coefficients to use the pseudo-inverse matrix
It's hard to tell which is ultimately better, although this way does make the
FOA output somewhat louder which will help when it's combined with direct HRTF
rendering.
2017-01-04 21:53:28 -08:00
Chris Robinson fcdf1cea70 Avoid writing to the same buffer that's read from
Also clean up comment formatting a bit.
2016-12-23 12:37:48 -08:00
Chris Robinson 40f359d159 Rename the version target for systems that have a version lib 2016-12-21 21:56:51 -08:00
Chris Robinson 080b0cea8b Reorder filter coefficients 2016-12-21 21:35:50 -08:00
Chris Robinson 4c33818dde Avoid duplicating code using a macro 2016-12-21 19:58:03 -08:00
Chris Robinson 315bd556ac Convert the SndIO backend to the updated API 2016-12-21 17:28:22 -08:00
Chris Robinson dedc782222 Avoid duplicate HRTF entries in the UI
Similar to how the library handles it, duplicate entries of the same file are
ignored. This could happen if, for example, XDG_DATA_DIRS contains the same
path multiple times.
2016-12-21 12:05:26 -08:00
Chris Robinson 1b104dd77b More robustly generate the git commit ID and branch 2016-12-21 11:41:45 -08:00
Chris Robinson e270a9784b Add missing macros for OSS3/Free compatibility 2016-12-21 10:54:19 -08:00
Chris Robinson bcb6dfee71 Trace the commit ID and branch the library was built from 2016-12-21 01:12:47 -08:00
Chris Robinson 8f581c0e66 Use separate macros for atomics that don't take a memory order 2016-12-20 20:49:37 -08:00
kcat 19ba71e767 Merge pull request #89 from rdb/patch-1
Explicitly disable use of GNU89 inline semantics
2016-12-20 13:43:59 -08:00
rdb f0c2c23ad1 Explicitly disable use of GNU89 inline semantics 2016-12-20 22:34:00 +01:00
Chris Robinson 70378925b0 Warn when a given device name isn't found for OSS 2016-12-01 18:34:29 -08:00
Chris Robinson 66569295e5 Minor cleanup for ALCossListPopulate 2016-12-01 18:26:18 -08:00
Chris Robinson 338e0d72b4 Ensure OSS devices are enumerated when a name is requested. 2016-12-01 18:11:52 -08:00
Chris Robinson 02a6031d03 Use atomic flags for boolean atomic locks 2016-11-25 23:25:16 -08:00
Chris Robinson ea82a6d19e Use a function to generate the up-sampler transcode matrix 2016-11-24 21:29:53 -08:00
Chris Robinson 10473285ba Fix an infinite loop 2016-11-23 01:32:14 -08:00
Chris Robinson 6886f77cbc Only send source updates for sources that have updated 2016-11-23 01:31:13 -08:00
Chris Robinson fcb669f803 Set the windows subsystem for DLLs on MSVC and GCC on Windows 2016-11-22 15:47:46 -08:00
Chris Robinson 49fd154829 Update cmake scripts to handle policy 0054 2016-11-22 14:50:55 -08:00
Chris Robinson 8492c2845d Avoid some unnecessary seq_cst memory ordering 2016-11-22 03:40:15 -08:00
Chris Robinson c618971758 Remove the non-atomic COMPARE_EXCHANGE macro 2016-11-22 03:00:16 -08:00
Chris Robinson 01babb69d2 Clean up finding a source's voice 2016-11-22 02:59:54 -08:00
Chris Robinson 616adea4cc Improve seqlock behavior 2016-11-21 23:58:28 -08:00
Chris Robinson 8bf4fe2eea Update some atomic memory ordering 2016-11-21 21:38:49 -08:00
Chris Robinson a502a41be3 Stop using almemory_order_consume 2016-11-17 00:46:46 -08:00
Chris Robinson caead294f2 Update a function comment about its input 2016-11-16 22:04:16 -08:00
Chris Robinson b743bc1c1b Remove the temporary stub files after the output object is made 2016-11-11 13:19:45 -08:00
Chris Robinson e69af7ab92 Fixes for embedded HRTFs on OSX
Use an empty source file to build a stub object file, instead of /dev/null. Use
_mh_dylib_header to retrieve the data on 10.7+, instead of _mh_execute_header.
And shorten the names to fit in the 16-character limit.

Thanks to Anna Cheremnykh for the fixes!
2016-11-11 13:14:02 -08:00
Chris Robinson 9ef7719734 Try to make embedded HRTF data sets work on OSX 2016-11-10 21:51:45 -08:00
Chris Robinson 0532acdf94 Don't use 0 for a resource ID 2016-11-10 12:37:07 -08:00
Chris Robinson 6a91d6a10a Add support for 8-byte types on inline assembly ATOMIC_ADD/SUB 2016-11-03 23:47:50 -07:00
Chris Robinson 939d16d57c Include the full JACK ringbuffer size for the device period count 2016-11-03 23:29:33 -07:00
Chris Robinson acc9f66baf Clean up some ringbuffer atomic calls 2016-11-03 21:04:24 -07:00
Chris Robinson 82e8dd0525 Fix win32 atomic fallbacks 2016-11-03 15:32:31 -07:00
Chris Robinson 815947492c Remove the explicit type from ATOMIC_ADD and ATOMIC_SUB 2016-11-03 01:22:29 -07:00
Chris Robinson 9682a62743 Use proper atomics for the lockless ringbuffer 2016-11-03 00:47:22 -07:00
Chris Robinson 118cc0907d Remove an unnecessary intermediate variable 2016-11-02 16:10:02 -07:00
Chris Robinson 2d5efe424f Be clearer about whether full or basic HRTF rendering is used 2016-11-02 16:10:02 -07:00
kcat c8ce33d5bd Merge pull request #78 from septag/cmake-dsound
Added cmake support for dsound and windows 8/10 SDKs
2016-11-02 16:09:23 -07:00
septag ac26b209a6 fixed minor check in FindDSound.cmake 2016-11-03 01:52:40 +03:30
septag d142ba1ab4 Added cmake FindWindowsSDK for FindDSound module and fixed FindDSound.cmake 2016-11-03 00:37:09 +03:30
Chris Robinson 43e7323adb Rebalance the frequencies for B-Format HRTF coefficients
The original pseudo-inverse method that generated the LF matrix expects the
high frequencies to be scaled up by ~2.645751 over the low frequencies (or
sqrt(7), ~8.45dB). However, the AllRAD method used to generate the HF matrix
produced a matrix that was only scaled up by 1.46551981258 (based on the
average of the W coefficients).

Previously, the LF matrix was scaled down by sqrt(7), as the difference
specified in the pseudo-inverse results. This failed to account for the
increase already present in the HF matrix, so now the LF matrix is scaled down
by the remaining difference between the expected scaling and the scaling
already present in the HF matrix (sqrt(7) / 1.46551981258 = 1.80533302205, or
roughly 5.13dB, where the reciprocal is 0.553914423 for -5.13 dB).
2016-11-01 02:20:19 -07:00
Chris Robinson a44f4c2fcb Initial ChangeLog update for 1.18 changes 2016-10-30 11:09:34 -07:00
Chris Robinson e46a92c220 Workaround some systems having an ECHO macro 2016-10-30 08:45:09 -07:00
septag d01d30ad5e Added cmake support for dsound and windows 8/10 SDKs 2016-10-30 14:18:45 +03:30
Chris Robinson 9120e7987e Cleanup and clarify a bit of the ambisonic docs 2016-10-28 06:27:01 -07:00
Chris Robinson 2668da696c Round the early and late delay tap sample offsets 2016-10-26 22:12:48 -07:00
Chris Robinson 16ed117d71 Restore a comment that was accidentally deleted 2016-10-10 01:33:33 -07:00
Chris Robinson 4bb6b9589f Don't interpolate between nearest HRIRs
It still fades between HRIRs when it changes, but now it selects the nearest
one instead of blending the nearest four. Due to the minimum-phase nature of
the HRIRs, interpolating between delays lead to some oddities which are
exasperated by the fading (and the fading is needed to avoid clicks and pops,
and smooth out changes).
2016-10-09 00:37:47 -07:00
Chris Robinson 698eddbb0c Better sort the main delay line taps 2016-10-06 20:05:16 -07:00
Chris Robinson f826f86842 Decorrelate the early reflection inputs 2016-10-06 19:45:48 -07:00
Chris Robinson 76cd6797b7 Add some more 'restrict' keywords 2016-10-06 01:39:18 -07:00
Chris Robinson 965e91c702 Remove an unused struct 2016-10-05 20:35:14 -07:00
Chris Robinson 9b8f36b758 Pass current and target gains directly for mixing 2016-10-05 20:33:45 -07:00
Chris Robinson 1e1a8837f8 Update a comment about using row mixers 2016-10-05 15:09:14 -07:00
Chris Robinson 06639b8250 Better pack the late reverb low- and all-pass variables 2016-10-05 13:31:53 -07:00
Chris Robinson bb6fba2183 Properly check for struct timespec 2016-10-04 17:19:47 -07:00
Chris Robinson 422f065809 Use the row mixer functions for the B-to-A-Format conversion 2016-10-04 16:42:28 -07:00
Chris Robinson 9349ee9002 Make some pointer-to-array parameters const 2016-10-04 16:25:43 -07:00
Chris Robinson a0e4696f55 Include wtypes.h for defining Windows' property keys 2016-10-04 11:20:01 -07:00
Chris Robinson 1e4d9cfa7e Enhance reverb using B-Format processing
Technically it uses A-Format processing from the B-Format input and output. But
this attempts to provide better spatial definition to the reverberation so that
it can be used in a more generic fashion, allowing it to be decoded as any
other B-Format signal to whatever output is needed, and also allowing for a bit
of height information when the output is capable of such.

There may still be some kinks to work out, such as properly decorrelating the
early reflection taps and tweaking the late reverb density. But it seems to be
a good enough start.
2016-10-03 12:20:13 -07:00
Chris Robinson 67c74e858b Finalize AL_SOFT_gain_clamp_ex 2016-10-03 12:11:50 -07:00
Chris Robinson a258790539 Update the ambisonic coefficients for HRTF
This uses an AllRAD-derived decoder matrix for the high frequencies, which
seems to improve positioning response. It also switches back to dual-band.
The low frequencies appear to be unexpectedly quiet by comparison, but it's not
that bad and can be tweaked later.
2016-09-26 11:18:26 -07:00
Chris Robinson f5e4a3ed85 Add a volume-adjust config option to adjust the source output volume
Designed for apps that either don't change the listener's AL_GAIN, or don't
allow the listener's AL_GAIN to go above 1. This allows the volume to still be
increased further than such apps may allow, if users find it too quiet.

Be aware that increasing this can easily cause clipping. The gain limit
reported by AL_GAIN_LIMIT_SOFT is also affected by this.
2016-09-24 18:46:41 -07:00
Chris Robinson 24f9a0f2ae Remove some more unnecessary volatiles 2016-09-24 14:29:27 -07:00
Chris Robinson d89624b03c Recognize Headset formfactors as headphones 2016-09-21 15:16:09 -07:00
Chris Robinson 4486043ae5 Skip audio packets that fail to decode in alffplay 2016-09-14 13:37:20 -07:00
kcat 2f1f7f4c6a Merge pull request #63 from adrianbroher/gnuinstalldir
Use GNUInstallDirs to place the build artifacts properly
2016-09-14 09:48:03 -07:00
Chris Robinson bb48a7e520 Fix EAX reverb effect output for HRTF and UHJ 2016-09-13 16:55:39 -07:00
Chris Robinson af5fb3d6e7 Fix the libatomic check 2016-09-13 12:11:52 -07:00
kcat a56bbbcf36 Merge pull request #70 from snikulov/appveyor_win_ci
build: added appveyor-ci script to verify windows build
2016-09-13 10:44:24 -07:00
Sergei Nikulov 1ff24c7171 build: added appveyour-ci script to verify windows build 2016-09-13 15:08:47 +03:00
Chris Robinson a004ccfa1a Check for libatomic, in case C11 atomics need it 2016-09-12 12:42:11 -07:00
Chris Robinson 53d8a49673 Call ALfilterState_processC directly
It's the only implementation currently, so there's no point to having it stored
as a function pointer in the filter struct. Even if there were SIMD versions,
it'd be a global selection, not per-instance.
2016-09-12 11:48:15 -07:00
Chris Robinson 46b3e1d08c Check if -mfpu=neon is available for compiling the NEON mixer 2016-09-12 11:31:59 -07:00
Chris Robinson efaa09dc23 Write to the correct outputs for extra reverb channels 2016-09-11 17:11:19 -07:00
Chris Robinson 651715abc9 Combine the reverb decorrelator delay line with the main delay line
Since it was merely acting as an extension of it anyway, with the second delay
line tap (for late reverb) copying attenuated samples to the decorrelator line
that was being tapped off of. Just extend the delay line and offset the
decorrelator taps to be relative to the late reverb tap.
2016-09-11 12:25:06 -07:00
Chris Robinson 4fcf9279fe Mark a global variable declaration as extern 2016-09-11 07:20:02 -07:00
Chris Robinson f993fd0cef Don't warn if the desired default HRTF is already first 2016-09-10 07:55:33 -07:00
Chris Robinson ba449ccce5 Handle UTF-8 output on Windows in openal-info 2016-09-09 09:52:01 -07:00
Chris Robinson 45dfdca6f9 Reduce the volume for the HRTF ambisonic decoder
Since it's accumulating multiple HRIRs for two output speakers, it seems to be
a better option to preserve the amplitude of the high-frequency decoder instead
of increasing it, and reduce the amplitude of the low-frequency decoder to
compensate.
2016-09-08 16:22:46 -07:00
Chris Robinson 0f24f49a44 Allow specifying the device to open for the examples 2016-09-08 12:14:28 -07:00
Chris Robinson b21e481827 Only WARN if GetProcPath fails to find the binary 2016-09-08 11:56:25 -07:00
Chris Robinson 958301d880 Try increasing the stack size if thread creation fails
Also increase the default stack size to 2MB.
2016-09-08 06:30:25 -07:00
kcat 4d91afb203 Merge pull request #64 from tpetazzoni/arm-neon-fix
Build NEON code with -mfpu=neon
2016-09-08 02:09:55 -07:00
Chris Robinson c3c283a0b5 Properly check if /proc/cpuinfo opened 2016-09-08 02:02:09 -07:00
Chris Robinson 742f181595 Use a few more HRIRs for the HRTF B-Format decoder
14 in total, an 8-point cube and a 6-point diamond shape, to help improve sound
localization a bit. Incurs no real extra CPU cost once the IRs are built.
2016-09-07 16:26:13 -07:00
Chris Robinson a52cfc8048 Check for run-time NEON support by reading /proc/cpuinfo
Less than ideal since documentations warn it may not list 'neon' even if it's
really supported. However, the "proper" APIs to check for NEON extensions don't
seem to exist in my toolchain.
2016-09-07 09:57:40 -07:00
Chris Robinson ef67d17a84 Simplify mmdevapi's device name search
Avoids converting each enumerated devid from WSTR to UTF-8, and instead just
converts the device name from UTF-8 to WSTR once if needed.
2016-09-07 09:22:34 -07:00
kcat 64a8ad9711 Merge pull request #65 from Dmytry/dmytry_github_master
mmdevapi: Allow specifying output device by it's audio endpoint GUID …
2016-09-07 08:32:31 -07:00
Chris Robinson 3af1d5b722 Properly align 16-bit fields in the Hrtf struct 2016-09-07 05:38:22 -07:00
Chris Robinson 7973c5abf8 Use unsigned int shifts for device flags 2016-09-07 05:18:42 -07:00
Dmytry Lavrov 6b7e14f11f mmdevapi: Allow specifying output device by it's audio endpoint GUID or by the device id string (Oculus VR api requires you to play back on a specific device). 2016-09-06 19:25:44 -05:00
Chris Robinson 1d9d1958db Make the SelectMixer function sharable 2016-09-06 13:21:11 -07:00
Thomas Petazzoni 27916ce3db Build NEON code with -mfpu=neon
The ARM-specific NEON code needs to be built with -mfpu=neon to avoid
build failures when a difference FPU is used by default by the
compiler.

Fixes issue #54.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
2016-09-06 22:19:14 +02:00
Marcel Metz 3481cf6fba Use GNUInstallDirs for generated pkg-config file
Continuation of 6387933
2016-09-06 21:52:05 +02:00
Marcel Metz 6387933f8b Use GNUInstallDirs to place the build artifacts properly
CMake 2.8.5 added the GNUInstallDirs module, which provides various
variables following the CMAKE_INSTALL_*DIR pattern to allow users a more
flexible installation setup and to provide sensible defaults while
respecting distribution specific install locations like lib64 for RPM
based linux distributions or debian multiarch tuples.
2016-09-06 21:43:01 +02:00
Chris Robinson 9cbe02fd85 Use the optimized mixing functions for reverb output 2016-09-06 12:16:49 -07:00
Chris Robinson 0558869d94 Use deinterlaced buffers for the intermediate reverb storage 2016-09-06 11:07:45 -07:00
Chris Robinson a758cc8243 Remove use of DECL_CONST
No idea if it was really gaining us anything, but removing it fixes a crash I
was getting with libs built with Clang.
2016-09-06 09:09:25 -07:00
Chris Robinson 1541ff24b8 Do reverb modulation before band-pass filtering
Ideally the band-pass should probably happen closer to output, like gain is.
However, doing that would require 16 filters (4 early + 4 late channels, each
with a low-pass and high-pass filter), compared to the two needed to do it on
input.
2016-09-06 07:02:17 -07:00
Chris Robinson b1f70b5b78 Rename some variables for clarity 2016-09-06 03:10:38 -07:00
Chris Robinson 564030ffa4 Use more correct cube decoder matrices 2016-09-05 10:33:52 -07:00
Chris Robinson a20576bbd7 Do multiple samples at once for reverb modulation 2016-09-05 06:08:01 -07:00
Chris Robinson 8a64f07121 Use a predefined identity matrix 2016-09-05 02:02:14 -07:00
Chris Robinson 42452b7f79 Correct a comment about B-Format conversion 2016-09-05 00:38:41 -07:00
Chris Robinson cf0ef500ec Rename MatrixMixerFunc to RowMixerFunc 2016-09-02 00:29:46 -07:00
Chris Robinson 17636a0c1c Calculate a variable closer to where it's used 2016-09-01 21:05:24 -07:00
Chris Robinson 7428636071 Use MixMatrixRow to upsample the split frequency bands to the output 2016-09-01 07:08:52 -07:00
Chris Robinson 6fb634c3e1 Remove unnecessary consts
They were causing GCC's built-in atomic cmpxchg to complain.
2016-08-31 09:26:57 -07:00
Chris Robinson 7a140b6912 Reorganize sample type conversion functions
To help avoid redundant manual definitions.
2016-08-31 08:30:52 -07:00
Chris Robinson 566d449e53 Always load HRTF files through memory pointers 2016-08-31 08:16:49 -07:00
Chris Robinson e01c337921 Add some helper wrappers to mmap files 2016-08-31 08:14:50 -07:00
Chris Robinson ccf90df072 Initialize some enums to dummy values 2016-08-31 04:56:10 -07:00
Chris Robinson f791b8c517 Add a compile-time macro to use dual-band ambisonic HRTF processing
Use single-band processing for now, to see if dual-band is causing a drop in
quality at all.
2016-08-30 22:33:33 -07:00
Chris Robinson 8d3a286577 Simplify the ambisonic up-sampler
It still behaves the same, although now combines the separate decode+encode
matrices into a transcode matrix (one per frequency band).
2016-08-30 04:21:57 -07:00
Chris Robinson 6d7f9aacd6 Print whether direct channels are off or on to stdout in alffplay 2016-08-29 22:18:44 -07:00
Chris Robinson 4859984e33 Add an option to alffplay to toggle AL_DIRECT_CHANNELS_SOFT
Using the 'd' key will toggle the playback source's AL_DIRECT_CHANNELS_SOFT
property. Although there is no visual feedback showing when it's on or off.
2016-08-29 01:55:44 -07:00
Chris Robinson 54649851fa Remove the upper limit from AL_MIN_GAIN and AL_MAX_GAIN
As per the current AL_SOFT_gain_clamp_ex proposal.
2016-08-29 01:53:52 -07:00
Chris Robinson 5bf0c64258 Add a query for the maximum source gain limit 2016-08-28 18:21:09 -07:00
Chris Robinson 3d59021702 Clamp the maximum mixing gain boost to 16
The combined source and listener gains now can't exceed a multiplier of 16
(~24dB). This is to avoid mixes getting out of control with large volume
boosts, which reduces the effective precision given by floating-point.
2016-08-27 06:28:04 -07:00
Chris Robinson 4b153dade8 Allow sources to play while alcSuspendContext is in effect
This appears to be how Creative's Windows drivers handle it, and is necessary
for at least the Windows version of UT2k4 (otherwise it tries to play a source
while suspended, checks and sees it's stopped, then kills it before it's given
a chance to start playing).

Consequently, the internal properties it gets mixed with are determined by what
the source properties are at the time of the play call, and the listener
properties at the time of the suspend call.

This does not change alDeferUpdatesSOFT, which will still hold the play state
change until alProcessUpdatesSOFT.
2016-08-26 21:19:38 -07:00
Chris Robinson ef03de3981 Avoid directly replacing the effect slot Update pointer 2016-08-25 18:19:13 -07:00
Chris Robinson a16739f765 Properly defer effect slot changes
Note that this now also causes all playing sources to update when an effect
slot is updated. This is a bit wasteful, as it should only need to re-update
sources that are using the effect slot (and only when a relevant property is
changed), but it's good enough. Especially with deferring since all playing
sources are going to get updated on the process call anyway.
2016-08-25 06:17:36 -07:00
Chris Robinson 4e4e597fa5 Track all references for effect states
This allows us to not have to play around with trying to avoid duplicate state
pointers, since the reference count will ensure they're deleted as appropriate.
The only caveat is that the mixer is not allowed to decrement references, since
that can cause the object to be freed (which the mixer code is not allowed to
do).
2016-08-25 04:57:58 -07:00
Chris Robinson 0fbf34fb45 Add a ref count to ALeffectState
This is mostly just reorganizing the effects to call the Construct method which
initializes the ref count.
2016-08-25 03:49:57 -07:00
Chris Robinson d8e9b3c621 Also rotate stereo sounds in the alhrtf example 2016-08-24 03:49:45 -07:00
Chris Robinson 849f85549d Consolidate duplicate code 2016-08-24 02:17:55 -07:00
Chris Robinson 8bf4a22876 Combine related members into a struct 2016-08-24 00:25:28 -07:00
Chris Robinson e77de8b12a Make a function definition static 2016-08-23 23:57:55 -07:00
Chris Robinson ea2fb38627 Hold updates for both listener and source updates 2016-08-23 19:37:26 -07:00
Chris Robinson c7eb0b7393 Don't pass the context's distance model as the source's 2016-08-23 19:17:17 -07:00
Chris Robinson dc8b7814c7 Avoid resupplying unneeded source updates
The source's voice holds a copy of the last properties it received, so listener
updates can make sources recalculate internal properties from that stored copy.
2016-08-23 18:56:01 -07:00
Chris Robinson bd054632e0 Remove an unneeded typedef 2016-08-21 23:59:11 -07:00
Chris Robinson 846cdd472d Band-split the HRIRs when building the ambisonic decoder filters
This allows each HRIR to contribute a frequency-dependent response, essentially
acting like a dual-band decoder playing over the cube speaker array.
2016-08-21 03:05:42 -07:00
Chris Robinson d16954c34e Fix HRTF index calculations for B-Format coefficients
The CalcEvIndices and CalcAzIndices methods were dependent on the FPU being in
round-to-zero mode, which is not the case for panning initialization. And since
we just need the closest index and don't need to lerp between them, it's better
to just directly calculate the index with rounding.
2016-08-18 23:33:08 -07:00
Chris Robinson e13c6bca20 Only use the cube points for generating the ambisonic HRTF coefficients
Using all the HRIRs seems to have problems with volume balancing, due in part
to HRTF data sets not having uniform enough measurements for a simple decoder
matrix to work (and generating a proper one that would work better is not that
easy). This still maintains the benefits of decoding ambisonics directly to
HRTF, namely that it only needs to filter the 4 ambisonic channels and can use
more optimized HRTF filtering methods on those channels. It can also be
improved further with frequency-dependent processing baked into the generated
coefficients, incurring no extra run-time cost for it.
2016-08-17 05:34:09 -07:00
Chris Robinson 770e2ff7ed Use a more specialized mixer function for B-Format to HRTF 2016-08-12 05:26:36 -07:00
Chris Robinson c6c6e3324d Decode directly from B-Format to HRTF instead of a cube
Last time this attempted to average the HRIRs according to their contribution
to a given B-Format channel as if they were loudspeakers, as well as averaging
the HRIR delays. The latter part resulted in the loss of the ITD (inter-aural
time delay), a key component of HRTF.

This time, the HRIRs are averaged similar to above, except instead of averaging
the delays, they're applied to the resulting coefficients (for example, a delay
of 8 would apply the HRIR starting at the 8th sample of the target HRIR). This
does roughly double the IR length, as the largest delay is about 35 samples
while the filter is normally 32 samples. However, this is still smaller the
original data set IR (which was 256 samples), it also only needs to be applied
to 4 channels for first-order ambisonics, rather than the 8-channel cube. So
it's doing twice as much work per sample, but only working on half the number
of samples.

Additionally, since the resulting HRIRs no longer rely on an extra delay line,
a more efficient HRTF mixing function can be made that doesn't use one. Such a
function can also avoid the per-sample stepping parameters the original uses.
2016-08-11 23:20:35 -07:00
Chris Robinson 9a60184bf6 Set a JACK error message handler when initializing the backend
JACK2 will print error messages to stderr if it fails to connect to a server.
Users who don't normally use JACK but have the client lib installed will get
those messages even though OpenAL Soft will continue on to find a working
backend without trouble. So to avoid it, set an error message handler that'll
log them as warnings.

This isn't that great because there's no way to tell whether the error messages
are due to the server not running, or some other problem. And it resets the
callback to the default afterward even if it may have been set to something
else before. JACK2, which is what needs this workaround in the first place,
doesn't export the jack_error_callback pointer to properly save and restore it.
2016-08-11 20:43:54 -07:00
Chris Robinson 56d3598020 Avoid checking DeferUpdates for each source state change 2016-08-08 22:31:08 -07:00
Chris Robinson 6117cb2377 Mix gain steps using SIMD with Neon 2016-08-05 18:47:26 -07:00
Chris Robinson f775f25379 Modify NumUpdates for different sample rates instead of UpdateSize
Not that this really changes anything since the CoreAudio backend doesn't honor
the ALCdevice's buffer metrics, nor accurately report the device's actual
metrics. But it clears up warnings from a non-multiple-of-four update size if
the sample rate causes it to change.
2016-08-04 21:19:04 -07:00
Chris Robinson 232293792d Constify some variables 2016-08-04 20:42:25 -07:00
Chris Robinson 48b954160e Look for the correct DLL for JACK on Windows 2016-08-03 19:07:35 -07:00
Chris Robinson 3b2d8d3949 Use al_calloc/al_free in more places 2016-08-03 17:36:42 -07:00
Chris Robinson 2b3c7f211c Add 'restrict' to another parameter 2016-08-03 08:34:25 -07:00
Chris Robinson f1e3f0762b Use the ACN and N3D map and scale lookup tables in SetChannelMap 2016-08-02 09:16:12 -07:00
Chris Robinson 0fcd39c4c0 Don't store the looping state in the voice
Certain operations on the buffer queue depend on the loop state to behave
properly, so it should not be deferred until the async voice update occurs.
2016-07-31 23:42:30 -07:00
Chris Robinson 48ff5d4ce8 Rename Ambisonics to Ambisonic in a couple places 2016-07-31 08:17:08 -07:00
Chris Robinson bff5268ed4 Remove DevFmtBFormat3D, which is covered by DevFmtAmbi1 2016-07-31 08:13:41 -07:00
Chris Robinson 2dd2756890 Handle ambi-format with alsoft-config 2016-07-31 08:03:26 -07:00
Chris Robinson 4bcd2fbb2e Add an option to specify the ambisonic output configuration 2016-07-31 07:46:38 -07:00
Chris Robinson 01af7b432d Update alsoft-config for the ambisonic output configurations 2016-07-30 16:32:26 -07:00
Chris Robinson af258efda4 Update alsoftrc.sample with the new channel configurations 2016-07-30 16:24:46 -07:00
Chris Robinson e4380ece5e Update alsoftrc.sample for proper txt filenames 2016-07-30 15:56:22 -07:00
Chris Robinson 0c5985374a Simplify a format check 2016-07-30 10:16:29 -07:00
Chris Robinson d253719ead Rename ambisonic channel config values to be shorter 2016-07-30 09:35:54 -07:00
Chris Robinson 33a84f17ac Add a stand-alone upsampler for higher-order ambisonic oputput 2016-07-30 09:29:21 -07:00
Chris Robinson b5b3ea95f8 Add a config to output first-, second-, or third-order ambisonics
Currently incomplete, as second- and third-order output will not correctly
handle B-Format input buffers. A standalone up-sampler will be needed, similar
to the high-quality decoder.

Also, output is ACN ordering with SN3D normalization. A config option will
eventually be provided to change this if desired.
2016-07-29 21:55:43 -07:00
Chris Robinson 96e83f95ee Don't try freeing the context in ReleaseThreadCtx
Unfortunately on certain systems, the TLS callback is called in a restricted
context, and isn't allowed to access certain messaging sub-systems. Such sub-
systems may be used if the thread's context is freed, in turn freeing the
device, which it tries to close.

Ideally, the app shouldn't have tried to destroy a context while it was still
current on a thread, or even leave a context current on a thread that's being
destroyed,. So for now, release the context ref and print an ERR that it might
be leaked.
2016-07-29 08:02:02 -07:00
Chris Robinson 5e11a738c6 Combine VECTOR_RESIZE and VECTOR_RESERVE 2016-07-26 12:02:03 -07:00
Chris Robinson d2eb866abe Avoid a NULL deref when creating 0 auxiliary effect slots 2016-07-26 04:54:27 -07:00
Chris Robinson b047eda1cb Avoid passing NULL to a parameter that must not be NULL 2016-07-26 04:53:25 -07:00
Chris Robinson a6f41e4cb0 Remove the last use of ALfilterState_processSingle 2016-07-26 04:09:01 -07:00
Chris Robinson 25d1b7bdba Remove broken autowah effect code
It's been disabled forever, and I have no idea how to make it work properly.
Better to just redo it when making something that works.
2016-07-26 03:45:25 -07:00
Chris Robinson 45514ee32f Add some more restrict keywords 2016-07-26 00:07:39 -07:00
Chris Robinson 11b38e1190 Rename input_gain to b0 2016-07-26 00:03:44 -07:00
Chris Robinson 0a693d039a Avoid more uses of ALfilterState_processSingle
It's a horriobly inefficient way to process multiple samples through the
filter.
2016-07-25 23:51:37 -07:00
Chris Robinson 8fcc4afd2d Fix use of a loop var 2016-07-25 22:20:47 -07:00
Chris Robinson e4bbbe06fa Avoid manual loops for ALfilterState processing 2016-07-25 19:04:54 -07:00
Chris Robinson 94dd34fb4b Increase the default thunk array size
This value should be enough to hold IDs for most apps without needing to
reallocate it, while not being unnecessarily large (4KB).
2016-07-25 06:29:08 -07:00
Chris Robinson c0e7aab823 Properly skip loading of already-loaded HRTF data sets
Previously, if an HRTF file was loaded it would not only skip loading it, but
it would also skip adding it to the output enumeration list. Now it properly
skips loading it when it's already loaded, but still adds it to the enumeration
list if it's not already in it.
2016-07-24 21:59:02 -07:00
kcat 9f2e416bbd Merge pull request #49 from ryanpcmcquen/patch-1
Fix for ffmpeg.
2016-07-21 16:42:21 -07:00
Ryan P.C. McQuen 69af50cadb Fix for ffmpeg.
Closes https://github.com/kcat/openal-soft/issues/48.
2016-07-21 08:09:31 -07:00
Chris Robinson fbba2828cd Update the default quad decoder matrix 2016-07-18 19:42:11 -07:00
Chris Robinson dc8bbca7f1 Constify and use the correct size for an array 2016-07-17 15:38:39 -07:00
Chris Robinson 84ca38ba95 Make a MAX_AMBI2D_COEFFS macro instead of a magic number 2016-07-17 00:46:18 -07:00
Chris Robinson 35cbecabf9 Repack the ambisonic dual-band decoder matrices
The decoders use a row of the HF decoder matrix followed by a row of the LF
decoder matrix, for each given output channel in turn. Packing the two matrices
accordingly results in less memory hopping.
2016-07-17 00:29:43 -07:00
Chris Robinson 2f3cffa517 Improve the UHJ encoder's allpass processing loops 2016-07-14 01:42:02 -07:00
Chris Robinson 470f454c53 Modify bs2b_cross_feed to do multiple samples at once 2016-07-13 23:08:11 -07:00
Chris Robinson 5106f035df Move the input channel array out of the DirectParams and SendParams 2016-07-13 01:39:44 -07:00
Chris Robinson e8202b915d Fix indentation 2016-07-12 19:05:56 -07:00
Chris Robinson b4cb0e9bf5 Fix a copy-paste message error 2016-07-12 19:05:21 -07:00
Chris Robinson ee929a2eea Enumerate the embedded HRTF resources when available 2016-07-12 19:02:19 -07:00
Chris Robinson 4898234da8 Add a cmake option to embed the HRTF data 2016-07-12 14:22:17 -07:00
Chris Robinson 14166264d6 Store the voice output buffers separate from the params 2016-07-11 23:30:32 -07:00
Chris Robinson e4039cb9ae Update comment about the source radius calculations 2016-07-10 22:11:11 -07:00
Chris Robinson fe2fb81047 Fix a direct access to a UIntMap 2016-07-09 16:42:36 -07:00
Chris Robinson 0aae992f94 Reorder some source fields 2016-07-07 19:48:21 -07:00
Chris Robinson 745cad5231 Avoid standard malloc/free for Hrtf allocation 2016-07-07 10:31:43 -07:00
Chris Robinson 7ec89b4b6e Avoid function calls to get the HRTF sample rate and IR size 2016-07-07 10:26:42 -07:00
Chris Robinson b495d80f56 Avoid using memcpy to copy a single struct 2016-07-06 13:33:40 -07:00
Chris Robinson d096e183a8 Remove a slightly outdated comment 2016-07-06 09:52:42 -07:00
Chris Robinson d340d50d49 Remove the VirtOut buffer alias 2016-07-05 14:18:17 -07:00
Chris Robinson 8f4d6c48ce Use separate arrays for UIntMap keys and values 2016-07-04 20:35:32 -07:00
Chris Robinson f0cbcdc928 Use al_malloc/al_free for the UIntMap array 2016-07-04 10:26:46 -07:00
Chris Robinson 80da138d7f Ensure voices has been updated once before mixing them
Sometimes the mixer is temporarily prevented from applying updates, when
multiple sources need to be updated simultaneously for example, but does not
prevent mixing. If the mixer runs during that time and a voice was just
started, it would've mixed the voice without any internal properties being set
for it.
2016-06-16 18:29:18 -07:00
Chris Robinson 697ee19f71 Rename MaxNoOfSources for consistency 2016-06-08 11:19:33 -07:00
Chris Robinson a49db89d7a Remove an IN_IDE_PARSER hack
Not all IDE parsers necessarily choke on restrict, and even those that do can
probably have their own configuration to define macros that can hide the
parsing errors caused by it.
2016-06-07 16:11:50 -07:00
Chris Robinson 0477d61599 Look in the executable's dir for another config file
On Windows it'll look for alsoft.ini, and elsewhere is alsoft.conf. This
applies after the user-local settings and before ALSOFT_CONF.
2016-06-04 10:45:44 -07:00
Chris Robinson b2041a5ddf Only define 8 HRTF filter states and params for the device
It will only be used with a cube channel setup, so there's no need to have one
for every possible output channel.
2016-06-04 08:40:06 -07:00
Chris Robinson 2c402e1ab5 Add property queries to get the device latency with the clock 2016-06-03 09:40:30 -07:00
Chris Robinson e38a81c5b6 Improve the filter processing function 2016-06-03 06:55:54 -07:00
Chris Robinson ce676ab70a Remove some unnecessary volatile keywords 2016-06-03 05:46:29 -07:00
Chris Robinson b7da69510c Implement a Neon-enhanced MixRow 2016-06-01 23:39:13 -07:00
Chris Robinson a16d0b192e Make a function static 2016-06-01 10:21:16 -07:00
Chris Robinson c63d468d4c Use a macro to specify the ambisonic periphonic channel mask 2016-06-01 05:30:06 -07:00
Chris Robinson 5e64882be9 Use SSE for applying the HQ B-Format decoder matrices 2016-05-31 10:18:34 -07:00
Chris Robinson 72d2febccb Don't access the band splitter fields in the processing loops
perf shows a 5% drop in relative execution time on the alffplay example with an
audio-only file (20% to 15%). Kinda figured the optimizer would handle it
better, but I guess not.
2016-05-31 07:50:23 -07:00
Chris Robinson d1c4fb6364 Don't try to emulate almtx_timedlock 2016-05-30 05:04:49 -07:00
Chris Robinson 612b24fa91 Clean up a couple variable names and declarations 2016-05-30 02:10:06 -07:00
Chris Robinson 70a105c22c Remove unnecessary VECTOR_INSERT 2016-05-30 00:05:10 -07:00
Chris Robinson 4802465f1a Hold the effectslot map lock while handling it 2016-05-29 02:47:54 -07:00
Chris Robinson 8aa4a74a7b Use a linked list for active effect slots 2016-05-29 01:40:16 -07:00
Chris Robinson 4b8aa9caf1 Avoid the mixer lock when getting the plain source offset
i.e. without the latency
2016-05-28 06:23:55 -07:00
Chris Robinson 9025e42afd Avoid an explicit mixer lock for getting the source offset and latency
The only mixer locking involved is with the backend, as determined by it's
ability to get the device clock and latency atomically.
2016-05-28 04:11:57 -07:00
Chris Robinson 6d4380a48c Change the backend getLatency method to return the clock time too
This will also allow backends to better synchronize the tracked clock time with
the device output latency, without necessarily needing to lock if the backend
API can allow for it.
2016-05-28 00:43:14 -07:00
Chris Robinson 800e38bac6 Use the backend lock for the effectstate's deviceUpdate call 2016-05-27 19:40:54 -07:00
Chris Robinson c837484015 Use a specific lock for the backend's stop/reset/play calls
This helps protect against the device changing unexpectedly from multiple
threads, instead of using the global list/library lock.
2016-05-27 19:23:39 -07:00
Chris Robinson 01f3e33df9 Remove a couple unneeded functions 2016-05-25 06:45:56 -07:00
Chris Robinson ea83c959ca Increment the device's mix count closer to the mixing loops 2016-05-23 01:03:37 -07:00
Chris Robinson e8b274d349 Properly pluralize some messages 2016-05-22 21:03:16 -07:00
Chris Robinson ea3fa06bc5 Improve locking for device attribute queries
Avoids the backend device lock, instead using the list lock to prevent the
device from changing while being queried, and adds some missing locks.
2016-05-22 20:42:00 -07:00
Chris Robinson 2e7ec3979a Avoid using realloc in a number of places 2016-05-21 03:27:51 -07:00
Chris Robinson 7bf64eaee0 Make the source position calues atomic 2016-05-19 20:50:55 -07:00
Chris Robinson d80f00173f Copy the source's Looping property into the voice 2016-05-18 12:15:19 -07:00
Chris Robinson aff725cba3 Avoid redundantly storing distance model settings 2016-05-17 20:02:46 -07:00
Chris Robinson 82675c018d Update the right effect state when the device is reset 2016-05-17 18:23:41 -07:00
Chris Robinson 82720c4759 Don't assume the "real" output buffer follows the dry buffer 2016-05-17 15:03:56 -07:00
Chris Robinson 2172f974e7 Improve reverb panning gains for "3D" output. 2016-05-17 14:28:30 -07:00
Chris Robinson 51e4aa7fc6 Ignore the listening angle for the wet path sound cones
Since the wet path is essentially the room response to a sound, the direction
of the sound to the listener doesn't change the amount of energy the room
receives. Instead, the surface area defined by the cones dictate the volume the
room gets for the sound.
2016-05-16 22:42:41 -07:00
Chris Robinson aea7c85daa Use floats for the listener transforms 2016-05-16 18:28:46 -07:00
Chris Robinson 56c6b3f56c Don't store the source's update method with the voice 2016-05-16 14:46:06 -07:00
Chris Robinson 945fd022d6 Avoid separate updates to sources that should apply together 2016-05-15 22:16:27 -07:00
Chris Robinson 63e98481ee Allocate context storage before starting/resetting the device
In case allocation fails, we don't have to worry about the playing status of
the backend.
2016-05-15 14:12:56 -07:00
Chris Robinson c522658d19 Avoid duplicate effect state objects in the freelist
If an unapplied update was superceded, it would be placed in the freelist with
its effect state object intact. This would cause another update with the same
effect state object to be placed into the freelist as well, or worse, cause it
to get deleted while in use when the container had its effect state cleared.
2016-05-15 13:50:56 -07:00
Chris Robinson 576c1116a6 Avoid using a flag to specify if the effect state needs to be updated
This fixes a potential missed state change if an update with a new state got
replaced with one that doesn't.
2016-05-15 01:19:05 -07:00
Chris Robinson b3338d25f6 Provide asynchronous property updates for sources
This necessitates a change in how source updates are handled. Rather than just
being able to update sources when a dependent object state is changed (e.g. a
listener gain change), now all source updates must be proactively provided.
Consequently, apps that do not utilize any deferring (AL_SOFT_defer_updates or
alcSuspendContext/alcProcessContext) may utilize more CPU since it'll be
filling out more update containers for the mixer thread to use.

The upside is that there's less blocking between the app's calling thread and
the mixer thread, particularly for vectors and other multi-value properties
(filters and sends). Deferring behavior when used is also improved, since
updates that shouldn't be applied yet are simply not provided. And when they
are provided, the mixer doesn't have to ignore them, meaning the actual
deferring of a context doesn't have to synchrnously force an update -- the
process call will send any pending updates, which the mixer will apply even if
another deferral occurs before the mixer runs, because it'll still be there
waiting on the next mixer invocation.

There is one slight bug introduced by this commit. When a listener change is
made, or changes to multiple sources while updates are being deferred, it is
possible for the mixer to run while the sources are prepping their updates,
causing some of the source updates to be seen before the other. This will be
fixed in short order.
2016-05-14 23:43:40 -07:00
kcat 0f7e499323 Merge pull request #41 from ColdPie1/source_limit
alsoft-config: Raise source limit to 4096
2016-05-14 12:44:58 -07:00
Chris Robinson f751f5e25e Store the remaining context properties with the listener properties 2016-05-13 20:21:20 -07:00
Chris Robinson 93a94d177c Get rid of an unnecessary copy of ALeffectProps 2016-05-13 18:28:01 -07:00
Andrew Eikum d89043b88e alsoft-config: Raise source limit to 4096 2016-05-13 09:04:26 -05:00
Chris Robinson 59cd6230a6 Properly load the effect state pointer from the property container 2016-05-13 00:24:26 -07:00
Chris Robinson 770bdcc108 Recognize AUX0...AUX15 for decoder speaker labels 2016-05-12 23:41:23 -07:00
Chris Robinson 9e6d8342de Hold the effect and filter maps while handling effects and filters 2016-05-12 23:12:11 -07:00
Chris Robinson 8d14824c65 Call the effect state update method after "returning" the container object. 2016-05-12 19:17:08 -07:00
Chris Robinson 210e150601 Avoid updating the effect state object if it's not changed 2016-05-12 19:05:06 -07:00
Chris Robinson ef0d4f8210 Provide (mostly) lockless updates for effect slots
Similar to the listener, separate containers are provided atomically for the
mixer thread to apply updates without needing to block, and a free-list is used
to reuse container objects.

A couple things to note. First, the lock is still used when the effect state's
deviceUpdate method is called to prevent asynchronous calls to reset the device
from interfering. This can be fixed by using the list lock in ALc.c instead.

Secondly, old effect states aren't immediately deleted when the effect type
changes (the actual type, not just its properties). This is because the mixer
thread is intended to be real-time safe, and so can't be freeing anything. They
are cleared away when updates reuse the container they were kept in, and they
don't incur any extra processing cost, but there may be cases where the memory
is kept around until the effect slot is deleted.
2016-05-12 18:41:33 -07:00
Chris Robinson 186b54aa3d Use a lockless method for updating listener and context properties
This uses a separate container to provide the relevant properties to the
internal update method, using atomic pointer swaps. A free-list is used to
avoid having too many individual containers.

This allows the mixer to update the internal listener properties without
requiring the lock to protect against async updates. It also allows concurrent
read access to the user-facing property values, even the multi-value ones (e.g.
the vectors).
2016-05-11 21:02:11 -07:00
Chris Robinson 21bc0f5ef8 Hold the buffer map lock while handling the buffer 2016-05-10 23:42:44 -07:00
Chris Robinson 906a4bb22d Hold the source map lock while handling it 2016-05-10 22:49:24 -07:00
Chris Robinson c026f44a09 Add a comment about CoeffCount being 0 2016-05-10 17:07:44 -07:00
Chris Robinson 7dac02148b Use the source's offset type to determine if there's an offset 2016-05-09 17:01:18 -07:00
Chris Robinson 182c0cb61a Find a valid source buffer before updating the voice 2016-05-09 14:22:26 -07:00
Chris Robinson c2611f10ab Store more "active" listener and context properties separately
This helps ensure async listener/context property changes affect all playing
sources at the same time.
2016-05-09 11:26:49 -07:00
kcat dcdeb8d1a6 Merge pull request #40 from rdb/master
Fix placement of alignas - fixes Mac OS X build
2016-05-03 03:03:51 -07:00
rdb b0224485aa Fix placement of alignas - fixes Mac OS X build 2016-05-03 05:38:36 -04:00
Chris Robinson d1e98c36d3 Don't crash when there's no backend to probe 2016-04-30 17:20:25 -07:00
Chris Robinson 3556da0d02 Start AL_SOFT_buffer_samples2 as a replacement for AL_SOFT_buffer_samples 2016-04-25 18:56:59 -07:00
Chris Robinson b9eacf4641 Remove unnecessary code for the now-unused write offset 2016-04-25 02:22:54 -07:00
Chris Robinson 0ed6791a58 Add support for AL_EXT_SOURCE_RADIUS 2016-04-25 00:30:47 -07:00
Chris Robinson 7555c86e7d Drop support for AL_SOFT_buffer_samples and AL_SOFT_buffer_sub_data
Unfortunately they conflict with AL_EXT_SOURCE_RADIUS, as AL_SOURCE_RADIUS and
AL_BYTE_RW_OFFSETS_SOFT share the same source property value. A replacement for
AL_SOFT_buffer_samples will eventually be made.
2016-04-24 23:58:11 -07:00
Chris Robinson 574ec13e5b Avoid an unnecessary aluVector 2016-04-24 23:35:11 -07:00
Chris Robinson f0871c8cfc Improve radius behavior with scaling of ambisonic coefficients 2016-04-24 21:42:59 -07:00
Chris Robinson fdee577940 Provide a decoder preset for 5.1 Surround output 2016-04-24 21:11:24 -07:00
Chris Robinson c796f99d36 Open the file selector to an existing path for presets 2016-04-23 17:31:34 -07:00
Chris Robinson c2dec5008b Install the ambdec preset files 2016-04-23 17:29:51 -07:00
Chris Robinson c6a11770c5 Add some general info about how ambdec works to ambdec.txt 2016-04-23 15:01:44 -07:00
Chris Robinson be7938ed38 Move some docs to the docs directory 2016-04-23 14:54:37 -07:00
Chris Robinson 80c9008798 Add docs for OpenAL Soft and standard decoder presets 2016-04-23 14:12:43 -07:00
kcat 1c736f4eb9 Merge pull request #38 from geron-cn/patch-1
fix audio play error in sample alffplay
2016-04-22 02:57:50 -07:00
gero cec03f39ae fix audio play error in sample
fix audio play error in sample
2016-04-22 17:33:00 +08:00
Chris Robinson 3387f23074 Update a config option comment 2016-04-22 02:19:43 -07:00
Chris Robinson 42531703cc Increase max output channels to 16
This also enables fully periphonic 3rd order HQ decoding.
2016-04-19 18:58:19 -07:00
Chris Robinson a59332f622 Combine two if checks into one 2016-04-19 13:58:33 -07:00
Chris Robinson cd2e9114e8 Remove unneeded ChannelMaps for BFormat formats 2016-04-17 22:11:15 -07:00
Chris Robinson 894c7d0f7e Trace the requested attribute values 2016-04-17 17:22:03 -07:00
Chris Robinson 3b571e03ab Avoid storing channel names for the dry buffer 2016-04-16 17:21:31 -07:00
Chris Robinson d2e8fae005 Don't look up the dry buffer's FrontCenter in the Dedicated effect
The real FrontCenter output is used if it exists, and if it doesn't, it's
unlikely the dry buffer will have it (and even if it does, it won't be any
better than panning).
2016-04-16 14:11:10 -07:00
Chris Robinson d1f3a15470 Reorganize a bit of aluInitRenderer code 2016-04-16 14:00:22 -07:00
Chris Robinson 325245f4b8 Silence possible out-of-bounds warning again 2016-04-16 01:24:37 -07:00
Chris Robinson a6c70992b0 More directly map coefficients for ambisonic mixing buffers
Instead of looping over all the coefficients for each channel with multiplies,
when we know only one will have a non-0 factor for ambisonic mixing buffers,
just index the one with a non-0 factor.
2016-04-15 22:05:47 -07:00
Chris Robinson e16032e1f0 Update some comments 2016-04-15 18:14:19 -07:00
Chris Robinson bd65f64d05 Avoid mixing all coefficients together when only some are used 2016-04-15 17:31:04 -07:00
Chris Robinson e27fad90de Fix possible out-of-bounds warning 2016-04-15 12:33:42 -07:00
Chris Robinson f4ff5fc106 Shorten VECTOR_ITER_ macros to VECTOR_ 2016-04-15 12:22:54 -07:00
Chris Robinson d5e624391b Update a comment 2016-04-15 10:50:46 -07:00
Chris Robinson fb97822d8c Avoid unnecessary loops for setting up effect slot b-format buffer mixing 2016-04-14 21:50:36 -07:00
Chris Robinson 21921cdaff Prepare the custom decoder in aluInitRenderer 2016-04-14 16:42:32 -07:00
Chris Robinson 65a9b97e46 Move the InitRenderer method to panning.c 2016-04-14 15:27:19 -07:00
Chris Robinson 221281688f Separate some long if-else blocks into a separate function 2016-04-14 14:24:52 -07:00
Chris Robinson d924e3d6c4 Split aluInitPanning into separate functions for HRTF or UHJ 2016-04-14 10:44:57 -07:00
Chris Robinson 38247e021a Put the decoder options in a group box 2016-04-10 13:48:26 -07:00
Chris Robinson 695cfa50b2 Improve error trace for bad matrix definitions 2016-04-10 13:06:36 -07:00
Chris Robinson 1faa7b536d Fix xover_freq type in the ambdec doc 2016-04-10 13:03:00 -07:00
Chris Robinson 7bdd68ddbe Use frequency-dependent processing for the ambisonic up-sampler 2016-04-09 13:10:52 -07:00
Chris Robinson a8101da956 Use the correct slot callback for the 6.1 and 7.1 decoder buttons 2016-04-08 00:52:08 -07:00
Chris Robinson f7efa0aff7 Avoid double slashes when constructing paths 2016-04-07 02:59:26 -07:00
Chris Robinson e127072b41 Use al_malloc/al_free for allocations 2016-04-05 20:51:38 -07:00
Chris Robinson 071f8a7b18 Also disable ALSA's resampler when not requesting a sample rate 2016-04-05 19:35:03 -07:00
Chris Robinson 8145a26d07 Add a hack to workaround erroneous prebuf values from pulse 2016-03-31 16:21:49 -07:00
Chris Robinson 93047e2a7a Use the 3D panned output for reverb with HQ decoding
This is less than ideal, but matching each reverb line to a speaker with
surround sound output is way too loud without the ambient volume scaling
offered by the "direct" panning.
2016-03-31 02:14:13 -07:00
Chris Robinson 2cd73a8d97 Allow dual-band decoders for basic rendering
Since the basic renderer does not do frequency-dependent processing, the high-
frequency matrix is used for panning.
2016-03-30 02:25:59 -07:00
Chris Robinson df0d225dbf Properly make hq-mode default to off 2016-03-30 01:49:58 -07:00
Chris Robinson b3e500be32 Remove the old ringbuffer implementation 2016-03-30 01:12:02 -07:00
Chris Robinson d6163fe570 Convert remaining ringbuffers to the lockless variant 2016-03-29 23:48:36 -07:00
Chris Robinson 2ccc1d1d8a Move the aligned malloc functions to the common lib 2016-03-29 00:44:58 -07:00
Chris Robinson ca309e685e Add a config dialog tab for decoder options 2016-03-28 16:35:42 -07:00
Chris Robinson 34539bc973 Skip unused output channels for the HQ decode 2016-03-28 14:50:18 -07:00
Chris Robinson 33cd3834ac Align the ChannelMix buffer and use it for up-sampling 2016-03-27 13:23:37 -07:00
Chris Robinson 91d53560d6 Add a text file describing AmbDec config file support. 2016-03-27 01:57:28 -07:00
Chris Robinson 764d580100 Document the ambisonic decoder options 2016-03-26 21:39:12 -07:00
Chris Robinson 6aaa11df30 Use the same option for decoder configs 2016-03-26 21:02:10 -07:00
Chris Robinson ad7ad48bd1 Don't use custom decoder configurations with mono or stereo
By default, stereo outputs using UHJ, which renders to a B-Format buffer that
gets run through a UHJ encoder for output. It may also output using pair-wise
panning, which pans between -30 and +30 degrees with the speakers at the two
ends. In both cases, the stereo coefficients are ignored.

Mono, having only one output channel, can realistically only attenuate its
channel. Turning the volume up and down accomplishes the same result.
2016-03-26 20:23:20 -07:00
Chris Robinson b4a1ba9d3f Include the distance gain compensation in the decoder matrix 2016-03-26 18:06:36 -07:00
Chris Robinson 1dcb04157c Add a config option for distance compensation 2016-03-26 17:52:42 -07:00
Chris Robinson 67db77452f Add distance compensation to the HQ decoder
This only compensates for timing and gain differences caused by differences in
the physical speaker distances. It's not near-field compensation. This also
relies on having proper distance values defined in the ambdec definition file.
2016-03-26 17:33:49 -07:00
Chris Robinson 7f4bd13f60 Allow up to third-order for horizontal-only rendering 2016-03-26 01:50:19 -07:00
Chris Robinson e0466766d7 Include any first-order scaling in the FOAOut coefficients 2016-03-25 23:25:13 -07:00
Chris Robinson e23da7a1de Skip height-related ambisonic channels for 2D rendering 2016-03-25 19:57:25 -07:00
Chris Robinson 05dace6525 Mix Dedicated effects to the real output if possible 2016-03-25 14:47:30 -07:00
Chris Robinson 3148986184 Implement AL_EXT_STEREO_ANGLES support 2016-03-25 14:40:44 -07:00
Chris Robinson 19b130c45d Use the "decoder" config section instead of "ambisonics" 2016-03-25 13:55:23 -07:00
Chris Robinson b0acfa1763 Add a cast and a couple float type fixes 2016-03-24 11:11:17 -07:00
Chris Robinson 3646cf817c Trace the HQ decoder order 2016-03-23 16:00:07 -07:00
Chris Robinson 2847590d45 Simplify setting a custom channel map configuration 2016-03-23 15:11:33 -07:00
Chris Robinson 147274f165 Up-sample first-order content when using a higher order HQ decoder 2016-03-23 15:10:59 -07:00
Chris Robinson 0dc35784db Allow second-order HQ decoding
Could really do with some optimizations to the mixing gain calculations. For
ambisonic targets, the coefficients will only have 1 non-0 entry for each
output, so the double loop in unnecessarily wasteful. Similarly, most uses
won't need a full height encoding either, so a horizontal-only or mixed-order
target could reduce the number of channels.
2016-03-23 10:39:14 -07:00
Chris Robinson 713ac9e679 Add a specific output for first-order sources 2016-03-22 17:52:20 -07:00
Chris Robinson 0a03596af1 Simplify setting the matrix coeffs 2016-03-22 14:42:53 -07:00
Chris Robinson 3877545d8c Add a workaround for a buggy modff 2016-03-18 14:05:45 -07:00
Chris Robinson ce575718ef Store the effect's output buffer in the effect state 2016-03-17 10:10:26 -07:00
Chris Robinson 606402fff0 Make sure enough reverb panning gains are defined 2016-03-16 08:51:12 -07:00
Chris Robinson 55ff0c143e Rename the BFormat channels to Aux 2016-03-16 06:49:35 -07:00
Chris Robinson a3863d5834 Add config options to enable the hq ambisonic decoder 2016-03-16 01:36:57 -07:00
Chris Robinson 8ff4a54356 Properly handle negative matrix values and fix decoder initialization 2016-03-15 09:07:03 -07:00
Chris Robinson 3a26d853ba Replace the custom layout options with an ambdec loader
The ambisonic layout options were never very good for clarity or flexibility.
Hopefully using ambdec files will prove to be better.
2016-03-15 08:00:03 -07:00
Chris Robinson 23bce59c66 Rename a couple functions for more informative logging 2016-03-15 07:07:09 -07:00
Chris Robinson 53fadf5497 Add a dual-band ambisonic decoder
This uses a virtual B-Format buffer for mixing, and then uses a dual-band
decoder for improved positional quality. This currently only works with first-
order output since first-order input (from the AL_EXT_BFROMAT extension) would
not sound correct when fed through a second- or third-order decoder.

This also does not currently implement near-field compensation since near-field
rendering effects are not implemented.
2016-03-15 05:08:05 -07:00
Chris Robinson 64cb21cb9f Downgrade some ERRs to WARNs or TRACEs 2016-03-15 04:02:25 -07:00
Chris Robinson 066df88a2c Always mix to the real output for DirectChannels 2016-03-14 20:25:36 -07:00
Chris Robinson 919b35295e Add a loader for ambdec files 2016-03-14 20:13:50 -07:00
Chris Robinson 0220404a91 Use second-order coefficients for 5.1
Since the panning only drives 5 speakers, it shouldn't use more than 5
ambisonic channels (9 total in second order, minus 4 for unused height).
2016-03-13 03:32:32 -07:00
Chris Robinson 0c4653085c Don't request a specific HRTF when one isn't specified 2016-03-11 21:01:32 -08:00
Chris Robinson 22abaa287d Use the real output's left and right channels with HRTF 2016-03-11 20:59:12 -08:00
Chris Robinson 5328972193 Separate writing to the output buffer from HRTF filtering 2016-03-11 20:44:05 -08:00
Chris Robinson bb03fe227b Update the current HRTF delays if the stepping is not finished 2016-03-11 19:59:14 -08:00
Chris Robinson 859cc703e7 Use the proper left and right channels for UHJ output 2016-03-10 22:56:44 -08:00
Chris Robinson d648486bcd Generalize GetChannelIdxByName 2016-03-10 14:29:44 -08:00
Chris Robinson da5f75615b Allocate enough reverb panning gains 2016-03-10 01:45:30 -08:00
Chris Robinson effb9d1e35 Keep track of the real output's channel names 2016-03-10 01:04:28 -08:00
Chris Robinson a457157516 Organize the dry buffer properties into a struct 2016-03-09 23:43:57 -08:00
Chris Robinson 3e2672ec9f Track the virtual and real output buffers ecplicitly 2016-03-09 22:57:38 -08:00
Chris Robinson 3b9fe27cbe Browse with the current or default device already selected 2016-03-06 12:03:36 -08:00
Chris Robinson 6971e86847 Add a simple About page that shows the library build version 2016-03-06 02:02:37 -08:00
Chris Robinson 101ae7644e Add a couple helpers for dealing with name-pair lists 2016-03-06 01:50:49 -08:00
Chris Robinson 58aa1751c3 Handle the stereo panning option dynamically 2016-03-05 19:20:38 -08:00
Chris Robinson 1ca45fa3eb Use the correct index for finding an empty string 2016-03-05 16:45:10 -08:00
Chris Robinson 4e3fdf9f5d Fix reverb with UHJ encoding 2016-03-03 01:41:42 -08:00
Chris Robinson 457c34c189 Add a backend tab page for JACK options 2016-03-02 23:18:07 -08:00
Chris Robinson d169fd859d Use the clicked() signal for the Browse buttons 2016-03-02 15:43:51 -08:00
Chris Robinson 773e2846e7 Add a Wave Writer backend config page 2016-03-02 15:31:14 -08:00
Chris Robinson 71d927333f Add OSS and Solaris config pages 2016-03-02 12:08:22 -08:00
Chris Robinson 9fdca9e29f Remove the extra scaling on W for UHJ encoding
There seems to be some inconsistent info about whether W should be scaled by
sqrt(2) for encoding. Not applying the scaling results in a wider stereo image,
which seems more appropriate.
2016-03-01 15:48:23 -08:00
Chris Robinson c89511b95e Properly remove empty config values 2016-02-28 21:55:58 -08:00
Chris Robinson abf6a9260a Add an option to select between UHJ and pair-wise mixing. 2016-02-28 20:55:41 -08:00
Chris Robinson 81384949e2 Properly check for preexisting HRTF names 2016-02-28 18:30:12 -08:00
Chris Robinson 9e3a1942a3 Add an ALSA backend tab 2016-02-28 09:27:52 -08:00
Chris Robinson 11acbfebf7 Hide backend list items for backends that aren't available 2016-02-27 22:12:53 -08:00
Chris Robinson 96520520be Show the full name in the backend lists 2016-02-27 21:52:36 -08:00
Chris Robinson 6d664367bc Generalize the backend list 2016-02-27 19:37:42 -08:00
Chris Robinson a12c420c59 Reorganize the Backends tab and add a PulseAudio section 2016-02-27 19:18:57 -08:00
Chris Robinson 0e1edc151d Move the Backends tab over 2016-02-27 12:39:25 -08:00
Chris Robinson adce176a35 Separate the left and right output writes with UHJ encoding 2016-02-27 11:38:40 -08:00
Chris Robinson 99f685d20d Add an option for pair-wise stereo panning 2016-02-26 21:48:03 -08:00
Chris Robinson ac91083ceb Use 2-channel UHJ for stereo output 2016-02-26 16:09:06 -08:00
Chris Robinson 67f086d1d4 Add a function to encode 2-channel UHJ from B-Format 2016-02-26 14:51:56 -08:00
Chris Robinson 3c127dc422 Update and add some tooltips 2016-02-24 05:37:48 -08:00
Chris Robinson 99a49122b8 Sort HRTFs in alsoft-config like in the lib 2016-02-24 05:08:39 -08:00
Chris Robinson b6824ca716 Add and use a copy-range string function 2016-02-24 04:53:32 -08:00
Chris Robinson d04970e568 Exclude the file extension from the HRTF name 2016-02-24 04:21:03 -08:00
Chris Robinson 93d73aae4d Add speed and quality labels for the resampler slider
And space out the buffer metric sliders from the edit boxes
2016-02-23 11:12:57 -08:00
Chris Robinson c9b2a0ae26 Collect HRTF names from the paths list for alsoft-config 2016-02-23 11:03:58 -08:00
Chris Robinson 68a2ae4024 Replace the hrtf_tables option with hrtf-paths 2016-02-23 10:56:06 -08:00
Chris Robinson e7ed3e2f72 Remove an unused function 2016-02-23 07:41:17 -08:00
Chris Robinson dea880dbf4 Better handle duplicate HRTF names in alsoft-config 2016-02-23 07:29:07 -08:00
Chris Robinson ae1a2fa9c0 Allow selecting a preferred HRTF in alsoft-config
This currently only checks the default paths when they're being used.
2016-02-23 06:52:13 -08:00
Chris Robinson 6512dcb233 Don't bother with a maximum size for the window 2016-02-23 01:46:38 -08:00
Chris Robinson 2c6e59c4ca Fix when ESTRPIPE and EPIPE are the same 2016-02-22 08:12:09 -08:00
Chris Robinson 840fa1b5e8 Add a warning if closing alsoft-config with unsaved changes 2016-02-22 07:56:05 -08:00
Chris Robinson 9d94f792de Restructure alsoft-config UI
The HRTF stuff is moved to its own tab, and a Preferred HRTF combo box is added
for future use. The resampler option is moved to the main Playback tab, and
some options are moved to an Advanced Settings section on the tab.
2016-02-22 03:05:13 -08:00
Chris Robinson bd233c8cdb Avoid enumerating the same HRTF file twice 2016-02-21 04:46:14 -08:00
Chris Robinson e6f120df23 Add a config option to specify the preferred HRTF 2016-02-21 02:44:02 -08:00
Chris Robinson c2dd681940 Remove an unused declaration 2016-02-20 21:01:24 -08:00
Chris Robinson e2bbee653e Include the HRTF filename in the HRTF memory chunk 2016-02-20 05:32:42 -08:00
Chris Robinson e1ce7f9180 Use an 8-channel cube for HRTF's virtual format.
There were phase issues caused by applying HRTF directly to the B-Format
channels, since the HRIR delays were all averaged which removed the inter-aural
time-delay, which in turn removed significant spatial information.
2016-02-20 00:53:01 -08:00
Chris Robinson e90cdbcf98 Reorganize makehrtf's File I/O functions 2016-02-19 22:23:37 -08:00
Chris Robinson e1c653b571 Reorganize and reformat makehrtf code 2016-02-18 22:55:03 -08:00
Chris Robinson 8575ecf3cb Cleanup makehrtf type definitions 2016-02-18 16:40:57 -08:00
Chris Robinson 670aeec59a Fix default makehrtf output name
And clean up the parameter processing.
2016-02-18 06:11:54 -08:00
Chris Robinson 7ff5cf7820 Allocate each HRTF as a single chunk 2016-02-16 19:56:44 -08:00
Chris Robinson ee9917fe96 Remove a dead assignment 2016-02-16 02:42:43 -08:00
Chris Robinson 6b1f322220 Only calculate steps for the used coefficients 2016-02-14 13:30:03 -08:00
Chris Robinson ecdc93f3ca Calculate HRTF stepping params right before mixing
This means we track the current params and the target params, rather than the
target params and the stepping. This closer matches the non-HRTF mixers.
2016-02-14 03:23:06 -08:00
Chris Robinson 25732d0895 Calculate channel gain stepping just before mixing 2016-02-14 01:22:01 -08:00
Chris Robinson a9135ec39d Don't pass the channel count to GetBFormatHrtfCoeffs
Since it's hard-coded anyway, there's no need to specify it.
2016-02-09 21:42:24 -08:00
Chris Robinson 000ced3795 Avoid underflow in alcnd_timedwait if the time point is already passed 2016-02-07 17:47:52 -08:00
Chris Robinson f4fa41487c Replace some CreateEvent calls with CreateEventW 2016-02-07 16:14:27 -08:00
Chris Robinson 6105d36fd7 Add special HRTF handling for reverb
This is pretty hacky. Since HRTF normally renders to B-Format with two "extra"
channels for the real stereo output, the panning interpolates between a panned
reverb channel on B-Format, and two non-panned reverb channels on stereo
output, given the panning vector length.
2016-02-06 23:00:07 -08:00
Chris Robinson fd54f4f03d Only apply the +3dB reverb gain boost to the ambient response. 2016-02-06 18:05:27 -08:00
Chris Robinson 3ee42d9826 Avoid an extra sample of delay in the reverb modulator 2016-02-05 15:39:31 -08:00
Chris Robinson c533060875 Remove an unnecessary variable and move duplicate code to a common spot 2016-02-05 14:21:02 -08:00
Chris Robinson 127c7e3b8c Don't explicitly check for LFE
It's going to be set to 0 gain anyway
2016-02-05 11:06:10 -08:00
Chris Robinson 3cac4dff95 Boost the direct-pan reverb to better match the expected volume
Hopefully. Would be nice to have a reference implementation for this style of
reverb to compare with.
2016-02-05 09:55:08 -08:00
Chris Robinson 301d4c158b Update a couple outdated comments 2016-02-05 09:42:43 -08:00
Chris Robinson 538f2839d1 Apply the early and late reverb gains with the panning gains 2016-02-05 08:43:51 -08:00
Chris Robinson 8627a92ea8 Better organize the reverb code into separate labeled sections 2016-02-05 08:14:41 -08:00
Chris Robinson b8e74c88cf Separate early and late reverb output for standard reverb too 2016-02-05 07:46:33 -08:00
Chris Robinson 305ef3215d Modify how the four output reverb points are calculated
For HRTF and B-Format output, the points no longer move but instead scale based
on the desired panning direction.
2016-02-05 07:34:31 -08:00
Chris Robinson d315ca4139 Try to map reverb outputs directly to output channels
HRTF and B-Format output still need to use virtual panning directions, but the
reverb works better when it outputs directly to an output channel. Ambient and
directional panning is used to properly mask and attenuate each output channel.

Note that currently the "direct panning" output is quieter than it should be.
Work is underway to attenuate the early reflections and late reverb better.
2016-02-01 22:24:40 -08:00
Chris Robinson b4a9b40d68 Apply the main reverb gain with the panning 2016-02-01 01:45:16 -08:00
Chris Robinson 46cae36ef9 Fix an out of date comment 2016-01-31 09:39:07 -08:00
Chris Robinson 7f908d90af Rename ComputeBFormatGains to ComputeFirstOrderGains 2016-01-31 09:00:23 -08:00
Chris Robinson d69dd6dc7a Make the source's buffer queue a singly-linked list 2016-01-31 00:42:58 -08:00
Chris Robinson 063ef9c2fc Properly silence the LFE input channel gain on the source sends 2016-01-30 07:13:07 -08:00
Chris Robinson 729f213c09 Fix scaling for effect sends of B-Format sources 2016-01-30 07:10:36 -08:00
Chris Robinson 7111322526 Make the modulator effect multichannel 2016-01-29 23:44:43 -08:00
Chris Robinson a046a951e9 Use separate modulator functions only for the waveform 2016-01-29 23:28:38 -08:00
Chris Robinson 3ac786c6af Convert the equalizer effect to multichannel 2016-01-29 07:26:19 -08:00
Chris Robinson 210b4c1fcd Make the compressor effect multichannel 2016-01-28 02:49:40 -08:00
Chris Robinson c1f87414c5 Mix to multichannel for effects
This mixes to a 4-channel first-order ambisonics buffer. With ACN ordering and
N3D scaling, this makes it easy to remain compatible with effects that only
care about mono input since channel 0 is an unattenuated mono signal.
2016-01-28 00:02:46 -08:00
Chris Robinson 2fa3ae85c9 Pass a pointer to the input samples array for effect processing 2016-01-27 08:16:47 -08:00
Chris Robinson fd387beda1 Avoid passing the device to SetChannelMap 2016-01-27 04:44:21 -08:00
Chris Robinson f547ef6d39 Separate calculating ambisonic coefficients from the panning gains 2016-01-25 06:11:51 -08:00
Chris Robinson 79e0f3e747 Don't write one byte at a time for the wave writer on big endian 2016-01-25 01:49:28 -08:00
152 changed files with 33564 additions and 15348 deletions
+69 -4
View File
@@ -1,5 +1,70 @@
os:
- linux
- osx
language: c
script: cmake . && make -j2
matrix:
include:
- os: linux
dist: trusty
- os: linux
dist: trusty
env:
- BUILD_ANDROID=true
- os: osx
sudo: required
cache:
directories:
- $HOME/android-ndk-r14
install:
- >
if [[ "${TRAVIS_OS_NAME}" == "linux" && -z "${BUILD_ANDROID}" ]]; then
# Install pulseaudio, portaudio, ALSA, JACK dependencies for
# corresponding backends.
# Install Qt5 dependency for alsoft-config.
sudo apt-get install -qq \
libpulse-dev \
portaudio19-dev \
libasound2-dev \
libjack-dev \
qtbase5-dev
fi
- >
if [[ "${TRAVIS_OS_NAME}" == "linux" && "${BUILD_ANDROID}" == "true" ]]; then
if [[ ! -d ~/android-ndk-r14 || -z "$(ls -A ~/android-ndk-r14)" ]]; then
curl -o ~/android-ndk.zip https://dl.google.com/android/repository/android-ndk-r14-linux-x86_64.zip
unzip -q ~/android-ndk.zip -d ~ \
'android-ndk-r14/build/cmake/*' \
'android-ndk-r14/platforms/android-9/arch-arm/*' \
'android-ndk-r14/source.properties' \
'android-ndk-r14/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/*' \
'android-ndk-r14/sysroot/*' \
'android-ndk-r14/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/*' \
'android-ndk-r14/toolchains/llvm/prebuilt/linux-x86_64/*'
sed -i -e 's/VERSION 3.6.0/VERSION 3.2/' ~/android-ndk-r14/build/cmake/android.toolchain.cmake
fi
fi
script:
- >
if [[ "${TRAVIS_OS_NAME}" == "linux" && -z "${BUILD_ANDROID}" ]]; then
cmake \
-DALSOFT_REQUIRE_ALSA=ON \
-DALSOFT_REQUIRE_OSS=ON \
-DALSOFT_REQUIRE_PORTAUDIO=ON \
-DALSOFT_REQUIRE_PULSEAUDIO=ON \
-DALSOFT_REQUIRE_JACK=ON \
-DALSOFT_EMBED_HRTF_DATA=YES \
.
fi
- >
if [[ "${TRAVIS_OS_NAME}" == "linux" && "${BUILD_ANDROID}" == "true" ]]; then
cmake \
-DCMAKE_TOOLCHAIN_FILE=~/android-ndk-r14/build/cmake/android.toolchain.cmake \
-DALSOFT_REQUIRE_OPENSL=ON \
-DALSOFT_EMBED_HRTF_DATA=YES \
.
fi
- >
if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then
cmake \
-DALSOFT_REQUIRE_COREAUDIO=ON \
-DALSOFT_EMBED_HRTF_DATA=YES \
.
fi
- make -j2
+1510 -877
View File
File diff suppressed because it is too large Load Diff
+1294 -1055
View File
File diff suppressed because it is too large Load Diff
+96 -10
View File
@@ -233,7 +233,61 @@ static void LoadConfigFromFile(FILE *f)
curSection[0] = 0;
else
{
strncpy(curSection, section, sizeof(curSection)-1);
size_t len, p = 0;
do {
char *nextp = strchr(section, '%');
if(!nextp)
{
strncpy(curSection+p, section, sizeof(curSection)-1-p);
break;
}
len = nextp - section;
if(len > sizeof(curSection)-1-p)
len = sizeof(curSection)-1-p;
strncpy(curSection+p, section, len);
p += len;
section = nextp;
if(((section[1] >= '0' && section[1] <= '9') ||
(section[1] >= 'a' && section[1] <= 'f') ||
(section[1] >= 'A' && section[1] <= 'F')) &&
((section[2] >= '0' && section[2] <= '9') ||
(section[2] >= 'a' && section[2] <= 'f') ||
(section[2] >= 'A' && section[2] <= 'F')))
{
unsigned char b = 0;
if(section[1] >= '0' && section[1] <= '9')
b = (section[1]-'0') << 4;
else if(section[1] >= 'a' && section[1] <= 'f')
b = (section[1]-'a'+0xa) << 4;
else if(section[1] >= 'A' && section[1] <= 'F')
b = (section[1]-'A'+0x0a) << 4;
if(section[2] >= '0' && section[2] <= '9')
b |= (section[2]-'0');
else if(section[2] >= 'a' && section[2] <= 'f')
b |= (section[2]-'a'+0xa);
else if(section[2] >= 'A' && section[2] <= 'F')
b |= (section[2]-'A'+0x0a);
if(p < sizeof(curSection)-1)
curSection[p++] = b;
section += 3;
}
else if(section[1] == '%')
{
if(p < sizeof(curSection)-1)
curSection[p++] = '%';
section += 2;
}
else
{
if(p < sizeof(curSection)-1)
curSection[p++] = '%';
section += 1;
}
if(p < sizeof(curSection)-1)
curSection[p] = 0;
} while(p < sizeof(curSection)-1 && *section != 0);
curSection[sizeof(curSection)-1] = 0;
}
@@ -313,44 +367,61 @@ void ReadALConfig(void)
{
WCHAR buffer[PATH_MAX];
const WCHAR *str;
al_string ppath;
FILE *f;
if(SHGetSpecialFolderPathW(NULL, buffer, CSIDL_APPDATA, FALSE) != FALSE)
{
al_string filepath = AL_STRING_INIT_STATIC();
al_string_copy_wcstr(&filepath, buffer);
al_string_append_cstr(&filepath, "\\alsoft.ini");
alstr_copy_wcstr(&filepath, buffer);
alstr_append_cstr(&filepath, "\\alsoft.ini");
TRACE("Loading config %s...\n", al_string_get_cstr(filepath));
f = al_fopen(al_string_get_cstr(filepath), "rt");
TRACE("Loading config %s...\n", alstr_get_cstr(filepath));
f = al_fopen(alstr_get_cstr(filepath), "rt");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
alstr_reset(&filepath);
}
ppath = GetProcPath();
if(!alstr_empty(ppath))
{
alstr_append_cstr(&ppath, "\\alsoft.ini");
TRACE("Loading config %s...\n", alstr_get_cstr(ppath));
f = al_fopen(alstr_get_cstr(ppath), "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
al_string_deinit(&filepath);
}
if((str=_wgetenv(L"ALSOFT_CONF")) != NULL && *str)
{
al_string filepath = AL_STRING_INIT_STATIC();
al_string_copy_wcstr(&filepath, str);
alstr_copy_wcstr(&filepath, str);
TRACE("Loading config %s...\n", al_string_get_cstr(filepath));
f = al_fopen(al_string_get_cstr(filepath), "rt");
TRACE("Loading config %s...\n", alstr_get_cstr(filepath));
f = al_fopen(alstr_get_cstr(filepath), "rt");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
al_string_deinit(&filepath);
alstr_reset(&filepath);
}
alstr_reset(&ppath);
}
#else
void ReadALConfig(void)
{
char buffer[PATH_MAX];
const char *str;
al_string ppath;
FILE *f;
str = "/etc/openal/alsoft.conf";
@@ -430,6 +501,19 @@ void ReadALConfig(void)
}
}
ppath = GetProcPath();
if(!alstr_empty(ppath))
{
alstr_append_cstr(&ppath, "/alsoft.conf");
TRACE("Loading config %s...\n", alstr_get_cstr(ppath));
f = al_fopen(alstr_get_cstr(ppath), "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
}
if((str=getenv("ALSOFT_CONF")) != NULL && *str)
{
TRACE("Loading config %s...\n", str);
@@ -440,6 +524,8 @@ void ReadALConfig(void)
fclose(f);
}
}
alstr_reset(&ppath);
}
#endif
+63 -147
View File
@@ -25,117 +25,17 @@
#include "alMain.h"
#include "threads.h"
#include "almalloc.h"
#include "compat.h"
struct RingBuffer {
ALubyte *mem;
ALsizei frame_size;
ALsizei length;
ALint read_pos;
ALint write_pos;
almtx_t mtx;
};
RingBuffer *CreateRingBuffer(ALsizei frame_size, ALsizei length)
{
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->read_pos = 0;
ring->write_pos = 0;
almtx_init(&ring->mtx, almtx_plain);
}
return ring;
}
void DestroyRingBuffer(RingBuffer *ring)
{
if(ring)
{
almtx_destroy(&ring->mtx);
free(ring);
}
}
ALsizei RingBufferSize(RingBuffer *ring)
{
ALsizei s;
almtx_lock(&ring->mtx);
s = (ring->write_pos-ring->read_pos+ring->length) % ring->length;
almtx_unlock(&ring->mtx);
return s;
}
void WriteRingBuffer(RingBuffer *ring, const ALubyte *data, ALsizei len)
{
int remain;
almtx_lock(&ring->mtx);
remain = (ring->read_pos-ring->write_pos-1+ring->length) % ring->length;
if(remain < len) len = remain;
if(len > 0)
{
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;
}
almtx_unlock(&ring->mtx);
}
void ReadRingBuffer(RingBuffer *ring, ALubyte *data, ALsizei len)
{
int remain;
almtx_lock(&ring->mtx);
remain = ring->length - ring->read_pos;
if(remain < len)
{
memcpy(data, ring->mem+(ring->read_pos*ring->frame_size), remain*ring->frame_size);
memcpy(data+(remain*ring->frame_size), ring->mem, (len-remain)*ring->frame_size);
}
else
memcpy(data, ring->mem+(ring->read_pos*ring->frame_size), len*ring->frame_size);
ring->read_pos += len;
ring->read_pos %= ring->length;
almtx_unlock(&ring->mtx);
}
/* NOTE: This lockless ringbuffer implementation is copied from JACK, extended
* to include an element size. Consequently, parameters and return values for a
* size or count is in 'elements', not bytes. Additionally, it only supports
* single-consumer/single-provider operation. */
struct ll_ringbuffer {
volatile size_t write_ptr;
volatile size_t read_ptr;
ATOMIC(size_t) write_ptr;
ATOMIC(size_t) read_ptr;
size_t size;
size_t size_mask;
size_t elem_size;
@@ -158,11 +58,11 @@ ll_ringbuffer_t *ll_ringbuffer_create(size_t sz, size_t elem_sz)
rb = al_malloc(16, sizeof(*rb) + power_of_two*elem_sz);
if(!rb) return NULL;
ATOMIC_INIT(&rb->write_ptr, 0);
ATOMIC_INIT(&rb->read_ptr, 0);
rb->size = power_of_two;
rb->size_mask = rb->size - 1;
rb->elem_size = elem_sz;
rb->write_ptr = 0;
rb->read_ptr = 0;
rb->mlocked = 0;
return rb;
}
@@ -184,7 +84,7 @@ void ll_ringbuffer_free(ll_ringbuffer_t *rb)
int ll_ringbuffer_mlock(ll_ringbuffer_t *rb)
{
#ifdef USE_MLOCK
if(!rb->locked && mlock(rb, sizeof(*rb) + rb->size*rb->elem_size))
if(!rb->mlocked && mlock(rb, sizeof(*rb) + rb->size*rb->elem_size))
return -1;
#endif /* USE_MLOCK */
rb->mlocked = 1;
@@ -194,8 +94,8 @@ int ll_ringbuffer_mlock(ll_ringbuffer_t *rb)
/* Reset the read and write pointers to zero. This is not thread safe. */
void ll_ringbuffer_reset(ll_ringbuffer_t *rb)
{
rb->read_ptr = 0;
rb->write_ptr = 0;
ATOMIC_STORE(&rb->write_ptr, 0, almemory_order_release);
ATOMIC_STORE(&rb->read_ptr, 0, almemory_order_release);
memset(rb->buf, 0, rb->size*rb->elem_size);
}
@@ -203,23 +103,24 @@ void ll_ringbuffer_reset(ll_ringbuffer_t *rb)
* elements in front of the read pointer and behind the write pointer. */
size_t ll_ringbuffer_read_space(const ll_ringbuffer_t *rb)
{
size_t w = rb->write_ptr;
size_t r = rb->read_ptr;
return (rb->size+w-r) & rb->size_mask;
size_t w = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->write_ptr, almemory_order_acquire);
size_t r = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->read_ptr, almemory_order_acquire);
return (w-r) & rb->size_mask;
}
/* Return the number of elements available for writing. This is the number of
* elements in front of the write pointer and behind the read pointer. */
size_t ll_ringbuffer_write_space(const ll_ringbuffer_t *rb)
{
size_t w = rb->write_ptr;
size_t r = rb->read_ptr;
return (rb->size+r-w-1) & rb->size_mask;
size_t w = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->write_ptr, almemory_order_acquire);
size_t r = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->read_ptr, almemory_order_acquire);
return (r-w-1) & rb->size_mask;
}
/* The copying data reader. Copy at most `cnt' elements from `rb' to `dest'.
* Returns the actual number of elements copied. */
size_t ll_ringbuffer_read(ll_ringbuffer_t *rb, char *dest, size_t cnt)
{
size_t read_ptr;
size_t free_cnt;
size_t cnt2;
size_t to_read;
@@ -229,10 +130,12 @@ size_t ll_ringbuffer_read(ll_ringbuffer_t *rb, char *dest, size_t cnt)
if(free_cnt == 0) return 0;
to_read = (cnt > free_cnt) ? free_cnt : cnt;
cnt2 = rb->read_ptr + to_read;
read_ptr = ATOMIC_LOAD(&rb->read_ptr, almemory_order_relaxed) & rb->size_mask;
cnt2 = read_ptr + to_read;
if(cnt2 > rb->size)
{
n1 = rb->size - rb->read_ptr;
n1 = rb->size - read_ptr;
n2 = cnt2 & rb->size_mask;
}
else
@@ -241,13 +144,15 @@ size_t ll_ringbuffer_read(ll_ringbuffer_t *rb, char *dest, size_t cnt)
n2 = 0;
}
memcpy(dest, &(rb->buf[rb->read_ptr*rb->elem_size]), n1*rb->elem_size);
rb->read_ptr = (rb->read_ptr + n1) & rb->size_mask;
memcpy(dest, &rb->buf[read_ptr*rb->elem_size], n1*rb->elem_size);
read_ptr += n1;
if(n2)
{
memcpy(dest + n1*rb->elem_size, &(rb->buf[rb->read_ptr*rb->elem_size]), n2*rb->elem_size);
rb->read_ptr = (rb->read_ptr + n2) & rb->size_mask;
memcpy(dest + n1*rb->elem_size, &rb->buf[(read_ptr&rb->size_mask)*rb->elem_size],
n2*rb->elem_size);
read_ptr += n2;
}
ATOMIC_STORE(&rb->read_ptr, read_ptr, almemory_order_release);
return to_read;
}
@@ -260,17 +165,18 @@ size_t ll_ringbuffer_peek(ll_ringbuffer_t *rb, char *dest, size_t cnt)
size_t cnt2;
size_t to_read;
size_t n1, n2;
size_t tmp_read_ptr;
size_t read_ptr;
tmp_read_ptr = rb->read_ptr;
free_cnt = ll_ringbuffer_read_space(rb);
if(free_cnt == 0) return 0;
to_read = (cnt > free_cnt) ? free_cnt : cnt;
cnt2 = tmp_read_ptr + to_read;
read_ptr = ATOMIC_LOAD(&rb->read_ptr, almemory_order_relaxed) & rb->size_mask;
cnt2 = read_ptr + to_read;
if(cnt2 > rb->size)
{
n1 = rb->size - tmp_read_ptr;
n1 = rb->size - read_ptr;
n2 = cnt2 & rb->size_mask;
}
else
@@ -279,10 +185,13 @@ size_t ll_ringbuffer_peek(ll_ringbuffer_t *rb, char *dest, size_t cnt)
n2 = 0;
}
memcpy(dest, &(rb->buf[tmp_read_ptr*rb->elem_size]), n1*rb->elem_size);
tmp_read_ptr = (tmp_read_ptr + n1) & rb->size_mask;
memcpy(dest, &rb->buf[read_ptr*rb->elem_size], n1*rb->elem_size);
if(n2)
memcpy(dest + n1*rb->elem_size, &(rb->buf[tmp_read_ptr*rb->elem_size]), n2*rb->elem_size);
{
read_ptr += n1;
memcpy(dest + n1*rb->elem_size, &rb->buf[(read_ptr&rb->size_mask)*rb->elem_size],
n2*rb->elem_size);
}
return to_read;
}
@@ -290,6 +199,7 @@ size_t ll_ringbuffer_peek(ll_ringbuffer_t *rb, char *dest, size_t cnt)
* Returns the actual number of elements copied. */
size_t ll_ringbuffer_write(ll_ringbuffer_t *rb, const char *src, size_t cnt)
{
size_t write_ptr;
size_t free_cnt;
size_t cnt2;
size_t to_write;
@@ -299,10 +209,12 @@ size_t ll_ringbuffer_write(ll_ringbuffer_t *rb, const char *src, size_t cnt)
if(free_cnt == 0) return 0;
to_write = (cnt > free_cnt) ? free_cnt : cnt;
cnt2 = rb->write_ptr + to_write;
write_ptr = ATOMIC_LOAD(&rb->write_ptr, almemory_order_relaxed) & rb->size_mask;
cnt2 = write_ptr + to_write;
if(cnt2 > rb->size)
{
n1 = rb->size - rb->write_ptr;
n1 = rb->size - write_ptr;
n2 = cnt2 & rb->size_mask;
}
else
@@ -311,28 +223,28 @@ size_t ll_ringbuffer_write(ll_ringbuffer_t *rb, const char *src, size_t cnt)
n2 = 0;
}
memcpy(&(rb->buf[rb->write_ptr*rb->elem_size]), src, n1*rb->elem_size);
rb->write_ptr = (rb->write_ptr + n1) & rb->size_mask;
memcpy(&rb->buf[write_ptr*rb->elem_size], src, n1*rb->elem_size);
write_ptr += n1;
if(n2)
{
memcpy(&(rb->buf[rb->write_ptr*rb->elem_size]), src + n1*rb->elem_size, n2*rb->elem_size);
rb->write_ptr = (rb->write_ptr + n2) & rb->size_mask;
memcpy(&rb->buf[(write_ptr&rb->size_mask)*rb->elem_size], src + n1*rb->elem_size,
n2*rb->elem_size);
write_ptr += n2;
}
ATOMIC_STORE(&rb->write_ptr, write_ptr, almemory_order_release);
return to_write;
}
/* Advance the read pointer `cnt' places. */
void ll_ringbuffer_read_advance(ll_ringbuffer_t *rb, size_t cnt)
{
size_t tmp = (rb->read_ptr + cnt) & rb->size_mask;
rb->read_ptr = tmp;
ATOMIC_ADD(&rb->read_ptr, cnt, almemory_order_acq_rel);
}
/* Advance the write pointer `cnt' places. */
void ll_ringbuffer_write_advance(ll_ringbuffer_t *rb, size_t cnt)
{
size_t tmp = (rb->write_ptr + cnt) & rb->size_mask;
rb->write_ptr = tmp;
ATOMIC_ADD(&rb->write_ptr, cnt, almemory_order_acq_rel);
}
/* The non-copying data reader. `vec' is an array of two places. Set the values
@@ -344,16 +256,18 @@ void ll_ringbuffer_get_read_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data
size_t cnt2;
size_t w, r;
w = rb->write_ptr;
r = rb->read_ptr;
free_cnt = (rb->size+w-r) & rb->size_mask;
w = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->write_ptr, almemory_order_acquire);
r = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->read_ptr, almemory_order_acquire);
w &= rb->size_mask;
r &= rb->size_mask;
free_cnt = (w-r) & rb->size_mask;
cnt2 = r + free_cnt;
if(cnt2 > rb->size)
{
/* Two part vector: the rest of the buffer after the current write ptr,
* plus some from the start of the buffer. */
vec[0].buf = (char*)&(rb->buf[r*rb->elem_size]);
vec[0].buf = (char*)&rb->buf[r*rb->elem_size];
vec[0].len = rb->size - r;
vec[1].buf = (char*)rb->buf;
vec[1].len = cnt2 & rb->size_mask;
@@ -361,7 +275,7 @@ void ll_ringbuffer_get_read_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data
else
{
/* Single part vector: just the rest of the buffer */
vec[0].buf = (char*)&(rb->buf[r*rb->elem_size]);
vec[0].buf = (char*)&rb->buf[r*rb->elem_size];
vec[0].len = free_cnt;
vec[1].buf = NULL;
vec[1].len = 0;
@@ -377,23 +291,25 @@ void ll_ringbuffer_get_write_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_dat
size_t cnt2;
size_t w, r;
w = rb->write_ptr;
r = rb->read_ptr;
free_cnt = (rb->size+r-w-1) & rb->size_mask;
w = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->write_ptr, almemory_order_acquire);
r = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->read_ptr, almemory_order_acquire);
w &= rb->size_mask;
r &= rb->size_mask;
free_cnt = (r-w-1) & rb->size_mask;
cnt2 = w + free_cnt;
if(cnt2 > rb->size)
{
/* Two part vector: the rest of the buffer after the current write ptr,
* plus some from the start of the buffer. */
vec[0].buf = (char*)&(rb->buf[w*rb->elem_size]);
vec[0].buf = (char*)&rb->buf[w*rb->elem_size];
vec[0].len = rb->size - w;
vec[1].buf = (char*)rb->buf;
vec[1].len = cnt2 & rb->size_mask;
}
else
{
vec[0].buf = (char*)&(rb->buf[w*rb->elem_size]);
vec[0].buf = (char*)&rb->buf[w*rb->elem_size];
vec[0].len = free_cnt;
vec[1].buf = NULL;
vec[1].len = 0;
+18 -17
View File
@@ -10,39 +10,40 @@ typedef char al_string_char_type;
TYPEDEF_VECTOR(al_string_char_type, al_string)
TYPEDEF_VECTOR(al_string, vector_al_string)
inline void al_string_deinit(al_string *str)
inline void alstr_reset(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))
#define AL_STRING_DEINIT(_x) alstr_reset(&(_x))
inline size_t al_string_length(const_al_string str)
inline size_t alstr_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 ALboolean alstr_empty(const_al_string str)
{ return alstr_length(str) == 0; }
inline const al_string_char_type *al_string_get_cstr(const_al_string str)
inline const al_string_char_type *alstr_get_cstr(const_al_string str)
{ return str ? &VECTOR_FRONT(str) : ""; }
void al_string_clear(al_string *str);
void alstr_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);
int alstr_cmp(const_al_string str1, const_al_string str2);
int alstr_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 alstr_copy(al_string *str, const_al_string from);
void alstr_copy_cstr(al_string *str, const al_string_char_type *from);
void alstr_copy_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to);
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);
void alstr_append_char(al_string *str, const al_string_char_type c);
void alstr_append_cstr(al_string *str, const al_string_char_type *from);
void alstr_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);
void al_string_append_wcstr(al_string *str, const wchar_t *from);
void al_string_append_wrange(al_string *str, const wchar_t *from, const wchar_t *to);
void alstr_copy_wcstr(al_string *str, const wchar_t *from);
void alstr_append_wcstr(al_string *str, const wchar_t *from);
void alstr_append_wrange(al_string *str, const wchar_t *from, const wchar_t *to);
#endif
#endif /* ALSTRING_H */
+566
View File
@@ -0,0 +1,566 @@
#include "config.h"
#include "ambdec.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "compat.h"
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;
}
/* Custom strtok_r, since we can't rely on it existing. */
static char *my_strtok_r(char *str, const char *delim, char **saveptr)
{
/* Sanity check and update internal pointer. */
if(!saveptr || !delim) return NULL;
if(str) *saveptr = str;
str = *saveptr;
/* Nothing more to do with this string. */
if(!str) return NULL;
/* Find the first non-delimiter character. */
while(*str != '\0' && strchr(delim, *str) != NULL)
str++;
if(*str == '\0')
{
/* End of string. */
*saveptr = NULL;
return NULL;
}
/* Find the next delimiter character. */
*saveptr = strpbrk(str, delim);
if(*saveptr) *((*saveptr)++) = '\0';
return str;
}
static char *read_int(ALint *num, const char *line, int base)
{
char *end;
*num = strtol(line, &end, base);
if(end && *end != '\0')
end = lstrip(end);
return end;
}
static char *read_uint(ALuint *num, const char *line, int base)
{
char *end;
*num = strtoul(line, &end, base);
if(end && *end != '\0')
end = lstrip(end);
return end;
}
static char *read_float(ALfloat *num, const char *line)
{
char *end;
#ifdef HAVE_STRTOF
*num = strtof(line, &end);
#else
*num = (ALfloat)strtod(line, &end);
#endif
if(end && *end != '\0')
end = lstrip(end);
return end;
}
char *read_clipped_line(FILE *f, char **buffer, size_t *maxlen)
{
while(readline(f, buffer, maxlen))
{
char *line, *comment;
line = lstrip(*buffer);
comment = strchr(line, '#');
if(comment) *(comment++) = 0;
line = rstrip(line);
if(line[0]) return line;
}
return NULL;
}
static int load_ambdec_speakers(AmbDecConf *conf, FILE *f, char **buffer, size_t *maxlen, char **saveptr)
{
ALsizei cur = 0;
while(cur < conf->NumSpeakers)
{
const char *cmd = my_strtok_r(NULL, " \t", saveptr);
if(!cmd)
{
char *line = read_clipped_line(f, buffer, maxlen);
if(!line)
{
ERR("Unexpected end of file\n");
return 0;
}
cmd = my_strtok_r(line, " \t", saveptr);
}
if(strcmp(cmd, "add_spkr") == 0)
{
const char *name = my_strtok_r(NULL, " \t", saveptr);
const char *dist = my_strtok_r(NULL, " \t", saveptr);
const char *az = my_strtok_r(NULL, " \t", saveptr);
const char *elev = my_strtok_r(NULL, " \t", saveptr);
const char *conn = my_strtok_r(NULL, " \t", saveptr);
if(!name) WARN("Name not specified for speaker %u\n", cur+1);
else alstr_copy_cstr(&conf->Speakers[cur].Name, name);
if(!dist) WARN("Distance not specified for speaker %u\n", cur+1);
else read_float(&conf->Speakers[cur].Distance, dist);
if(!az) WARN("Azimuth not specified for speaker %u\n", cur+1);
else read_float(&conf->Speakers[cur].Azimuth, az);
if(!elev) WARN("Elevation not specified for speaker %u\n", cur+1);
else read_float(&conf->Speakers[cur].Elevation, elev);
if(!conn) TRACE("Connection not specified for speaker %u\n", cur+1);
else alstr_copy_cstr(&conf->Speakers[cur].Connection, conn);
cur++;
}
else
{
ERR("Unexpected speakers command: %s\n", cmd);
return 0;
}
cmd = my_strtok_r(NULL, " \t", saveptr);
if(cmd)
{
ERR("Unexpected junk on line: %s\n", cmd);
return 0;
}
}
return 1;
}
static int load_ambdec_matrix(ALfloat *gains, ALfloat (*matrix)[MAX_AMBI_COEFFS], ALsizei maxrow, FILE *f, char **buffer, size_t *maxlen, char **saveptr)
{
int gotgains = 0;
ALsizei cur = 0;
while(cur < maxrow)
{
const char *cmd = my_strtok_r(NULL, " \t", saveptr);
if(!cmd)
{
char *line = read_clipped_line(f, buffer, maxlen);
if(!line)
{
ERR("Unexpected end of file\n");
return 0;
}
cmd = my_strtok_r(line, " \t", saveptr);
}
if(strcmp(cmd, "order_gain") == 0)
{
ALuint curgain = 0;
char *line;
while((line=my_strtok_r(NULL, " \t", saveptr)) != NULL)
{
ALfloat value;
line = read_float(&value, line);
if(line && *line != '\0')
{
ERR("Extra junk on gain %u: %s\n", curgain+1, line);
return 0;
}
if(curgain < MAX_AMBI_ORDER+1)
gains[curgain] = value;
curgain++;
}
while(curgain < MAX_AMBI_ORDER+1)
gains[curgain++] = 0.0f;
gotgains = 1;
}
else if(strcmp(cmd, "add_row") == 0)
{
ALuint curidx = 0;
char *line;
while((line=my_strtok_r(NULL, " \t", saveptr)) != NULL)
{
ALfloat value;
line = read_float(&value, line);
if(line && *line != '\0')
{
ERR("Extra junk on matrix element %ux%u: %s\n", cur, curidx, line);
return 0;
}
if(curidx < MAX_AMBI_COEFFS)
matrix[cur][curidx] = value;
curidx++;
}
while(curidx < MAX_AMBI_COEFFS)
matrix[cur][curidx++] = 0.0f;
cur++;
}
else
{
ERR("Unexpected speakers command: %s\n", cmd);
return 0;
}
cmd = my_strtok_r(NULL, " \t", saveptr);
if(cmd)
{
ERR("Unexpected junk on line: %s\n", cmd);
return 0;
}
}
if(!gotgains)
{
ERR("Matrix order_gain not specified\n");
return 0;
}
return 1;
}
void ambdec_init(AmbDecConf *conf)
{
ALsizei i;
memset(conf, 0, sizeof(*conf));
AL_STRING_INIT(conf->Description);
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
{
AL_STRING_INIT(conf->Speakers[i].Name);
AL_STRING_INIT(conf->Speakers[i].Connection);
}
}
void ambdec_deinit(AmbDecConf *conf)
{
ALsizei i;
alstr_reset(&conf->Description);
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
{
alstr_reset(&conf->Speakers[i].Name);
alstr_reset(&conf->Speakers[i].Connection);
}
memset(conf, 0, sizeof(*conf));
}
int ambdec_load(AmbDecConf *conf, const char *fname)
{
char *buffer = NULL;
size_t maxlen = 0;
char *line;
FILE *f;
f = al_fopen(fname, "r");
if(!f)
{
ERR("Failed to open: %s\n", fname);
return 0;
}
while((line=read_clipped_line(f, &buffer, &maxlen)) != NULL)
{
char *saveptr;
char *command;
command = my_strtok_r(line, "/ \t", &saveptr);
if(!command)
{
ERR("Malformed line: %s\n", line);
goto fail;
}
if(strcmp(command, "description") == 0)
{
char *value = my_strtok_r(NULL, "", &saveptr);
alstr_copy_cstr(&conf->Description, lstrip(value));
}
else if(strcmp(command, "version") == 0)
{
line = my_strtok_r(NULL, "", &saveptr);
line = read_uint(&conf->Version, line, 10);
if(line && *line != '\0')
{
ERR("Extra junk after version: %s\n", line);
goto fail;
}
if(conf->Version != 3)
{
ERR("Unsupported version: %u\n", conf->Version);
goto fail;
}
}
else if(strcmp(command, "dec") == 0)
{
const char *dec = my_strtok_r(NULL, "/ \t", &saveptr);
if(strcmp(dec, "chan_mask") == 0)
{
line = my_strtok_r(NULL, "", &saveptr);
line = read_uint(&conf->ChanMask, line, 16);
if(line && *line != '\0')
{
ERR("Extra junk after mask: %s\n", line);
goto fail;
}
}
else if(strcmp(dec, "freq_bands") == 0)
{
line = my_strtok_r(NULL, "", &saveptr);
line = read_uint(&conf->FreqBands, line, 10);
if(line && *line != '\0')
{
ERR("Extra junk after freq_bands: %s\n", line);
goto fail;
}
if(conf->FreqBands != 1 && conf->FreqBands != 2)
{
ERR("Invalid freq_bands value: %u\n", conf->FreqBands);
goto fail;
}
}
else if(strcmp(dec, "speakers") == 0)
{
line = my_strtok_r(NULL, "", &saveptr);
line = read_int(&conf->NumSpeakers, line, 10);
if(line && *line != '\0')
{
ERR("Extra junk after speakers: %s\n", line);
goto fail;
}
if(conf->NumSpeakers > MAX_OUTPUT_CHANNELS)
{
ERR("Unsupported speaker count: %u\n", conf->NumSpeakers);
goto fail;
}
}
else if(strcmp(dec, "coeff_scale") == 0)
{
line = my_strtok_r(NULL, " \t", &saveptr);
if(strcmp(line, "n3d") == 0)
conf->CoeffScale = ADS_N3D;
else if(strcmp(line, "sn3d") == 0)
conf->CoeffScale = ADS_SN3D;
else if(strcmp(line, "fuma") == 0)
conf->CoeffScale = ADS_FuMa;
else
{
ERR("Unsupported coeff scale: %s\n", line);
goto fail;
}
}
else
{
ERR("Unexpected /dec option: %s\n", dec);
goto fail;
}
}
else if(strcmp(command, "opt") == 0)
{
const char *opt = my_strtok_r(NULL, "/ \t", &saveptr);
if(strcmp(opt, "xover_freq") == 0)
{
line = my_strtok_r(NULL, "", &saveptr);
line = read_float(&conf->XOverFreq, line);
if(line && *line != '\0')
{
ERR("Extra junk after xover_freq: %s\n", line);
goto fail;
}
}
else if(strcmp(opt, "xover_ratio") == 0)
{
line = my_strtok_r(NULL, "", &saveptr);
line = read_float(&conf->XOverRatio, line);
if(line && *line != '\0')
{
ERR("Extra junk after xover_ratio: %s\n", line);
goto fail;
}
}
else if(strcmp(opt, "input_scale") == 0 || strcmp(opt, "nfeff_comp") == 0 ||
strcmp(opt, "delay_comp") == 0 || strcmp(opt, "level_comp") == 0)
{
/* Unused */
my_strtok_r(NULL, " \t", &saveptr);
}
else
{
ERR("Unexpected /opt option: %s\n", opt);
goto fail;
}
}
else if(strcmp(command, "speakers") == 0)
{
const char *value = my_strtok_r(NULL, "/ \t", &saveptr);
if(strcmp(value, "{") != 0)
{
ERR("Expected { after %s command, got %s\n", command, value);
goto fail;
}
if(!load_ambdec_speakers(conf, f, &buffer, &maxlen, &saveptr))
goto fail;
value = my_strtok_r(NULL, "/ \t", &saveptr);
if(!value)
{
line = read_clipped_line(f, &buffer, &maxlen);
if(!line)
{
ERR("Unexpected end of file\n");
goto fail;
}
value = my_strtok_r(line, "/ \t", &saveptr);
}
if(strcmp(value, "}") != 0)
{
ERR("Expected } after speaker definitions, got %s\n", value);
goto fail;
}
}
else if(strcmp(command, "lfmatrix") == 0 || strcmp(command, "hfmatrix") == 0 ||
strcmp(command, "matrix") == 0)
{
const char *value = my_strtok_r(NULL, "/ \t", &saveptr);
if(strcmp(value, "{") != 0)
{
ERR("Expected { after %s command, got %s\n", command, value);
goto fail;
}
if(conf->FreqBands == 1)
{
if(strcmp(command, "matrix") != 0)
{
ERR("Unexpected \"%s\" type for a single-band decoder\n", command);
goto fail;
}
if(!load_ambdec_matrix(conf->HFOrderGain, conf->HFMatrix, conf->NumSpeakers,
f, &buffer, &maxlen, &saveptr))
goto fail;
}
else
{
if(strcmp(command, "lfmatrix") == 0)
{
if(!load_ambdec_matrix(conf->LFOrderGain, conf->LFMatrix, conf->NumSpeakers,
f, &buffer, &maxlen, &saveptr))
goto fail;
}
else if(strcmp(command, "hfmatrix") == 0)
{
if(!load_ambdec_matrix(conf->HFOrderGain, conf->HFMatrix, conf->NumSpeakers,
f, &buffer, &maxlen, &saveptr))
goto fail;
}
else
{
ERR("Unexpected \"%s\" type for a dual-band decoder\n", command);
goto fail;
}
}
value = my_strtok_r(NULL, "/ \t", &saveptr);
if(!value)
{
line = read_clipped_line(f, &buffer, &maxlen);
if(!line)
{
ERR("Unexpected end of file\n");
goto fail;
}
value = my_strtok_r(line, "/ \t", &saveptr);
}
if(strcmp(value, "}") != 0)
{
ERR("Expected } after matrix definitions, got %s\n", value);
goto fail;
}
}
else if(strcmp(command, "end") == 0)
{
line = my_strtok_r(NULL, "/ \t", &saveptr);
if(line)
{
ERR("Unexpected junk on end: %s\n", line);
goto fail;
}
fclose(f);
free(buffer);
return 1;
}
else
{
ERR("Unexpected command: %s\n", command);
goto fail;
}
line = my_strtok_r(NULL, "/ \t", &saveptr);
if(line)
{
ERR("Unexpected junk on line: %s\n", line);
goto fail;
}
}
ERR("Unexpected end of file\n");
fail:
fclose(f);
free(buffer);
return 0;
}
+46
View File
@@ -0,0 +1,46 @@
#ifndef AMBDEC_H
#define AMBDEC_H
#include "alstring.h"
#include "alMain.h"
/* Helpers to read .ambdec configuration files. */
enum AmbDecScaleType {
ADS_N3D,
ADS_SN3D,
ADS_FuMa,
};
typedef struct AmbDecConf {
al_string Description;
ALuint Version; /* Must be 3 */
ALuint ChanMask;
ALuint FreqBands; /* Must be 1 or 2 */
ALsizei NumSpeakers;
enum AmbDecScaleType CoeffScale;
ALfloat XOverFreq;
ALfloat XOverRatio;
struct {
al_string Name;
ALfloat Distance;
ALfloat Azimuth;
ALfloat Elevation;
al_string Connection;
} Speakers[MAX_OUTPUT_CHANNELS];
/* Unused when FreqBands == 1 */
ALfloat LFOrderGain[MAX_AMBI_ORDER+1];
ALfloat LFMatrix[MAX_OUTPUT_CHANNELS][MAX_AMBI_COEFFS];
ALfloat HFOrderGain[MAX_AMBI_ORDER+1];
ALfloat HFMatrix[MAX_OUTPUT_CHANNELS][MAX_AMBI_COEFFS];
} AmbDecConf;
void ambdec_init(AmbDecConf *conf);
void ambdec_deinit(AmbDecConf *conf);
int ambdec_load(AmbDecConf *conf, const char *fname);
#endif /* AMBDEC_H */
+125 -71
View File
@@ -199,15 +199,21 @@ static ALCboolean alsa_load(void)
#ifdef HAVE_DYNLOAD
if(!alsa_handle)
{
al_string missing_funcs = AL_STRING_INIT_STATIC();
alsa_handle = LoadLib("libasound.so.2");
if(!alsa_handle)
{
WARN("Failed to load %s\n", "libasound.so.2");
return ALC_FALSE;
}
error = ALC_FALSE;
#define LOAD_FUNC(f) do { \
p##f = GetSymbol(alsa_handle, #f); \
if(p##f == NULL) { \
error = ALC_TRUE; \
alstr_append_cstr(&missing_funcs, "\n" #f); \
} \
} while(0)
ALSA_FUNCS(LOAD_FUNC);
@@ -215,10 +221,11 @@ static ALCboolean alsa_load(void)
if(error)
{
WARN("Missing expected functions:%s\n", alstr_get_cstr(missing_funcs));
CloseLib(alsa_handle);
alsa_handle = NULL;
return ALC_FALSE;
}
alstr_reset(&missing_funcs);
}
#endif
@@ -237,16 +244,13 @@ static vector_DevMap CaptureDevices;
static void clear_devlist(vector_DevMap *devlist)
{
DevMap *iter, *end;
iter = VECTOR_ITER_BEGIN(*devlist);
end = VECTOR_ITER_END(*devlist);
for(;iter != end;iter++)
{
AL_STRING_DEINIT(iter->name);
AL_STRING_DEINIT(iter->device_name);
}
VECTOR_RESIZE(*devlist, 0);
#define FREE_DEV(i) do { \
AL_STRING_DEINIT((i)->name); \
AL_STRING_DEINIT((i)->device_name); \
} while(0)
VECTOR_FOR_EACH(DevMap, *devlist, FREE_DEV);
VECTOR_RESIZE(*devlist, 0, 0);
#undef FREE_DEV
}
@@ -272,11 +276,45 @@ static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList)
AL_STRING_INIT(entry.name);
AL_STRING_INIT(entry.device_name);
al_string_copy_cstr(&entry.name, alsaDevice);
al_string_copy_cstr(&entry.device_name, GetConfigValue(NULL, "alsa", (stream==SND_PCM_STREAM_PLAYBACK) ?
"device" : "capture", "default"));
alstr_copy_cstr(&entry.name, alsaDevice);
alstr_copy_cstr(&entry.device_name, GetConfigValue(
NULL, "alsa", (stream==SND_PCM_STREAM_PLAYBACK) ? "device" : "capture", "default"
));
VECTOR_PUSH_BACK(*DeviceList, entry);
if(stream == SND_PCM_STREAM_PLAYBACK)
{
const char *customdevs, *sep, *next;
next = GetConfigValue(NULL, "alsa", "custom-devices", "");
while((customdevs=next) != NULL && customdevs[0])
{
next = strchr(customdevs, ';');
sep = strchr(customdevs, '=');
if(!sep)
{
al_string spec = AL_STRING_INIT_STATIC();
if(next)
alstr_copy_range(&spec, customdevs, next++);
else
alstr_copy_cstr(&spec, customdevs);
ERR("Invalid ALSA device specification \"%s\"\n", alstr_get_cstr(spec));
alstr_reset(&spec);
continue;
}
AL_STRING_INIT(entry.name);
AL_STRING_INIT(entry.device_name);
alstr_copy_range(&entry.name, customdevs, sep++);
if(next)
alstr_copy_range(&entry.device_name, sep, next++);
else
alstr_copy_cstr(&entry.device_name, sep);
TRACE("Got device \"%s\", \"%s\"\n", alstr_get_cstr(entry.name),
alstr_get_cstr(entry.device_name));
VECTOR_PUSH_BACK(*DeviceList, entry);
}
}
card = -1;
if((err=snd_card_next(&card)) < 0)
ERR("Failed to find a card: %s\n", snd_strerror(err));
@@ -321,7 +359,8 @@ static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList)
snd_pcm_info_set_device(pcminfo, dev);
snd_pcm_info_set_subdevice(pcminfo, 0);
snd_pcm_info_set_stream(pcminfo, stream);
if((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
if((err = snd_ctl_pcm_info(handle, pcminfo)) < 0)
{
if(err != -ENOENT)
ERR("control digital audio info (hw:%d): %s\n", card, snd_strerror(err));
continue;
@@ -333,15 +372,15 @@ static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList)
ConfigValueStr(NULL, "alsa", name, &device_prefix);
snprintf(name, sizeof(name), "%s, %s (CARD=%s,DEV=%d)",
cardname, devname, cardid, dev);
cardname, devname, cardid, dev);
snprintf(device, sizeof(device), "%sCARD=%s,DEV=%d",
device_prefix, cardid, dev);
device_prefix, cardid, dev);
TRACE("Got device \"%s\", \"%s\"\n", name, device);
AL_STRING_INIT(entry.name);
AL_STRING_INIT(entry.device_name);
al_string_copy_cstr(&entry.name, name);
al_string_copy_cstr(&entry.device_name, device);
alstr_copy_cstr(&entry.name, name);
alstr_copy_cstr(&entry.device_name, device);
VECTOR_PUSH_BACK(*DeviceList, entry);
}
snd_ctl_close(handle);
@@ -413,7 +452,7 @@ static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self);
static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self);
static DECLARE_FORWARD2(ALCplaybackAlsa, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, ALCuint, availableSamples)
static ALint64 ALCplaybackAlsa_getLatency(ALCplaybackAlsa *self);
static ClockLatency ALCplaybackAlsa_getClockLatency(ALCplaybackAlsa *self);
static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCplaybackAlsa)
@@ -588,7 +627,9 @@ static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr)
{
case -EAGAIN:
continue;
#if ESTRPIPE != EPIPE
case -ESTRPIPE:
#endif
case -EPIPE:
case -EINTR:
ret = snd_pcm_recover(self->pcmHandle, ret, 1);
@@ -630,12 +671,12 @@ static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name)
if(VECTOR_SIZE(PlaybackDevices) == 0)
probe_devices(SND_PCM_STREAM_PLAYBACK, &PlaybackDevices);
#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, name) == 0)
#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, name) == 0)
VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
#undef MATCH_NAME
if(iter == VECTOR_ITER_END(PlaybackDevices))
if(iter == VECTOR_END(PlaybackDevices))
return ALC_INVALID_VALUE;
driver = al_string_get_cstr(iter->device_name);
driver = alstr_get_cstr(iter->device_name);
}
else
{
@@ -654,7 +695,7 @@ static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name)
/* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
snd_config_update_free_global();
al_string_copy_cstr(&device->DeviceName, name);
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
@@ -705,7 +746,7 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
break;
}
allowmmap = GetConfigValueBool(al_string_get_cstr(device->DeviceName), "alsa", "mmap", 1);
allowmmap = GetConfigValueBool(alstr_get_cstr(device->DeviceName), "alsa", "mmap", 1);
periods = device->NumUpdates;
periodLen = (ALuint64)device->UpdateSize * 1000000 / device->Frequency;
bufferLen = periodLen * periods;
@@ -749,7 +790,7 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
}
CHECK(snd_pcm_hw_params_set_format(self->pcmHandle, hp, format));
/* test and set channels (implicitly sets frame bits) */
if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)) < 0)
if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder)) < 0)
{
static const enum DevFmtChannels channellist[] = {
DevFmtStereo,
@@ -762,20 +803,24 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
for(k = 0;k < COUNTOF(channellist);k++)
{
if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(channellist[k])) >= 0)
if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(channellist[k], 0)) >= 0)
{
device->FmtChans = channellist[k];
device->AmbiOrder = 0;
break;
}
}
}
CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)));
CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder)));
/* set rate (implicitly constrains period/buffer parameters) */
if(!GetConfigValueBool(al_string_get_cstr(device->DeviceName), "alsa", "allow-resampler", 0))
if(!GetConfigValueBool(alstr_get_cstr(device->DeviceName), "alsa", "allow-resampler", 0) ||
!(device->Flags&DEVICE_FREQUENCY_REQUEST))
{
if(snd_pcm_hw_params_set_rate_resample(self->pcmHandle, hp, 0) < 0)
ERR("Failed to disable ALSA resampler\n");
}
else if(snd_pcm_hw_params_set_rate_resample(self->pcmHandle, hp, 1) < 0)
ERR("Failed to enable ALSA resampler\n");
CHECK(snd_pcm_hw_params_set_rate_near(self->pcmHandle, hp, &rate, NULL));
/* set buffer time (implicitly constrains period/buffer parameters) */
if((err=snd_pcm_hw_params_set_buffer_time_near(self->pcmHandle, hp, &bufferLen, NULL)) < 0)
@@ -840,7 +885,7 @@ static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self)
self->size = snd_pcm_frames_to_bytes(self->pcmHandle, device->UpdateSize);
if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
{
self->buffer = malloc(self->size);
self->buffer = al_malloc(16, self->size);
if(!self->buffer)
{
ERR("buffer malloc failed\n");
@@ -862,7 +907,7 @@ static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self)
if(althrd_create(&self->thread, thread_func, self) != althrd_success)
{
ERR("Could not create playback thread\n");
free(self->buffer);
al_free(self->buffer);
self->buffer = NULL;
return ALC_FALSE;
}
@@ -885,22 +930,29 @@ static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self)
self->killNow = 1;
althrd_join(self->thread, &res);
free(self->buffer);
al_free(self->buffer);
self->buffer = NULL;
}
static ALint64 ALCplaybackAlsa_getLatency(ALCplaybackAlsa *self)
static ClockLatency ALCplaybackAlsa_getClockLatency(ALCplaybackAlsa *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
snd_pcm_sframes_t delay = 0;
ClockLatency ret;
int err;
ALCplaybackAlsa_lock(self);
ret.ClockTime = GetDeviceClockTime(device);
if((err=snd_pcm_delay(self->pcmHandle, &delay)) < 0)
{
ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
return 0;
delay = 0;
}
return maxi64((ALint64)delay*1000000000/device->Frequency, 0);
if(delay < 0) delay = 0;
ret.Latency = delay * DEVICE_CLOCK_RES / device->Frequency;
ALCplaybackAlsa_unlock(self);
return ret;
}
@@ -913,7 +965,7 @@ typedef struct ALCcaptureAlsa {
ALsizei size;
ALboolean doCapture;
RingBuffer *ring;
ll_ringbuffer_t *ring;
snd_pcm_sframes_t last_avail;
} ALCcaptureAlsa;
@@ -927,7 +979,7 @@ static ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self);
static void ALCcaptureAlsa_stop(ALCcaptureAlsa *self);
static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buffer, ALCuint samples);
static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self);
static ALint64 ALCcaptureAlsa_getLatency(ALCcaptureAlsa *self);
static ClockLatency ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa *self);
static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCcaptureAlsa)
@@ -961,12 +1013,12 @@ static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name)
if(VECTOR_SIZE(CaptureDevices) == 0)
probe_devices(SND_PCM_STREAM_CAPTURE, &CaptureDevices);
#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, name) == 0)
#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, name) == 0)
VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
#undef MATCH_NAME
if(iter == VECTOR_ITER_END(CaptureDevices))
if(iter == VECTOR_END(CaptureDevices))
return ALC_INVALID_VALUE;
driver = al_string_get_cstr(iter->device_name);
driver = alstr_get_cstr(iter->device_name);
}
else
{
@@ -1023,7 +1075,7 @@ static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name)
/* set format (implicitly sets sample bits) */
CHECK(snd_pcm_hw_params_set_format(self->pcmHandle, hp, format));
/* set channels (implicitly sets frame bits) */
CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)));
CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder)));
/* set rate (implicitly constrains period/buffer parameters) */
CHECK(snd_pcm_hw_params_set_rate(self->pcmHandle, hp, device->Frequency, 0));
/* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
@@ -1045,24 +1097,18 @@ static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name)
if(needring)
{
self->ring = CreateRingBuffer(FrameSizeFromDevFmt(device->FmtChans, device->FmtType),
device->UpdateSize*device->NumUpdates);
self->ring = ll_ringbuffer_create(
device->UpdateSize*device->NumUpdates + 1,
FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder)
);
if(!self->ring)
{
ERR("ring buffer create failed\n");
goto error2;
}
self->size = snd_pcm_frames_to_bytes(self->pcmHandle, periodSizeInFrames);
self->buffer = malloc(self->size);
if(!self->buffer)
{
ERR("buffer malloc failed\n");
goto error2;
}
}
al_string_copy_cstr(&device->DeviceName, name);
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
@@ -1071,9 +1117,7 @@ error:
if(hp) snd_pcm_hw_params_free(hp);
error2:
free(self->buffer);
self->buffer = NULL;
DestroyRingBuffer(self->ring);
ll_ringbuffer_free(self->ring);
self->ring = NULL;
snd_pcm_close(self->pcmHandle);
@@ -1083,9 +1127,9 @@ error2:
static void ALCcaptureAlsa_close(ALCcaptureAlsa *self)
{
snd_pcm_close(self->pcmHandle);
DestroyRingBuffer(self->ring);
ll_ringbuffer_free(self->ring);
free(self->buffer);
al_free(self->buffer);
self->buffer = NULL;
}
@@ -1120,11 +1164,11 @@ static void ALCcaptureAlsa_stop(ALCcaptureAlsa *self)
void *ptr;
size = snd_pcm_frames_to_bytes(self->pcmHandle, avail);
ptr = malloc(size);
ptr = al_malloc(16, size);
if(ptr)
{
ALCcaptureAlsa_captureSamples(self, ptr, avail);
free(self->buffer);
al_free(self->buffer);
self->buffer = ptr;
self->size = size;
}
@@ -1141,7 +1185,7 @@ static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buff
if(self->ring)
{
ReadRingBuffer(self->ring, buffer, samples);
ll_ringbuffer_read(self->ring, buffer, samples);
return ALC_NO_ERROR;
}
@@ -1166,7 +1210,7 @@ static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buff
}
else
{
free(self->buffer);
al_free(self->buffer);
self->buffer = NULL;
self->size = 0;
}
@@ -1244,12 +1288,15 @@ static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self)
while(avail > 0)
{
ll_ringbuffer_data_t vec[2];
snd_pcm_sframes_t amt;
amt = snd_pcm_bytes_to_frames(self->pcmHandle, self->size);
if(avail < amt) amt = avail;
ll_ringbuffer_get_write_vector(self->ring, vec);
if(vec[0].len == 0) break;
amt = snd_pcm_readi(self->pcmHandle, self->buffer, amt);
amt = (vec[0].len < (snd_pcm_uframes_t)avail) ?
vec[0].len : (snd_pcm_uframes_t)avail;
amt = snd_pcm_readi(self->pcmHandle, vec[0].buf, amt);
if(amt < 0)
{
ERR("read error: %s\n", snd_strerror(amt));
@@ -1273,32 +1320,39 @@ static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self)
continue;
}
WriteRingBuffer(self->ring, self->buffer, amt);
ll_ringbuffer_write_advance(self->ring, amt);
avail -= amt;
}
return RingBufferSize(self->ring);
return ll_ringbuffer_read_space(self->ring);
}
static ALint64 ALCcaptureAlsa_getLatency(ALCcaptureAlsa *self)
static ClockLatency ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
snd_pcm_sframes_t delay = 0;
ClockLatency ret;
int err;
ALCcaptureAlsa_lock(self);
ret.ClockTime = GetDeviceClockTime(device);
if((err=snd_pcm_delay(self->pcmHandle, &delay)) < 0)
{
ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
return 0;
delay = 0;
}
return maxi64((ALint64)delay*1000000000/device->Frequency, 0);
if(delay < 0) delay = 0;
ret.Latency = delay * DEVICE_CLOCK_RES / device->Frequency;
ALCcaptureAlsa_unlock(self);
return ret;
}
static inline void AppendAllDevicesList2(const DevMap *entry)
{ AppendAllDevicesList(al_string_get_cstr(entry->name)); }
{ AppendAllDevicesList(alstr_get_cstr(entry->name)); }
static inline void AppendCaptureDeviceList2(const DevMap *entry)
{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); }
{ AppendCaptureDeviceList(alstr_get_cstr(entry->name)); }
typedef struct ALCalsaBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
+25 -159
View File
@@ -4,17 +4,19 @@
#include <stdlib.h>
#include "alMain.h"
#include "alu.h"
#include "backends/base.h"
extern inline ALuint64 GetDeviceClockTime(ALCdevice *device);
/* Base ALCbackend method implementations. */
void ALCbackend_Construct(ALCbackend *self, ALCdevice *device)
{
int ret;
self->mDevice = device;
ret = almtx_init(&self->mMutex, almtx_recursive);
int ret = almtx_init(&self->mMutex, almtx_recursive);
assert(ret == althrd_success);
self->mDevice = device;
}
void ALCbackend_Destruct(ALCbackend *self)
@@ -37,9 +39,27 @@ ALCuint ALCbackend_availableSamples(ALCbackend* UNUSED(self))
return 0;
}
ALint64 ALCbackend_getLatency(ALCbackend* UNUSED(self))
ClockLatency ALCbackend_getClockLatency(ALCbackend *self)
{
return 0;
ALCdevice *device = self->mDevice;
ALuint refcount;
ClockLatency ret;
do {
while(((refcount=ATOMIC_LOAD(&device->MixCount, almemory_order_acquire))&1))
althrd_yield();
ret.ClockTime = GetDeviceClockTime(device);
ATOMIC_THREAD_FENCE(almemory_order_acquire);
} while(refcount != ATOMIC_LOAD(&device->MixCount, almemory_order_relaxed));
/* NOTE: The device will generally have about all but one periods filled at
* any given time during playback. Without a more accurate measurement from
* the output, this is an okay approximation.
*/
ret.Latency = device->UpdateSize * DEVICE_CLOCK_RES / device->Frequency *
maxu(device->NumUpdates-1, 1);
return ret;
}
void ALCbackend_lock(ALCbackend *self)
@@ -59,157 +79,3 @@ void ALCbackend_unlock(ALCbackend *self)
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 DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ALint64, getLatency)
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);
}
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 DECLARE_FORWARD(CaptureWrapper, ALCbackend, ALint64, getLatency)
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);
}
ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
PlaybackWrapper *backend;
NEW_OBJ(backend, PlaybackWrapper)(device, funcs);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
if(type == ALCbackend_Capture)
{
CaptureWrapper *backend;
NEW_OBJ(backend, CaptureWrapper)(device, funcs);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
+23 -6
View File
@@ -5,6 +5,21 @@
#include "threads.h"
typedef struct ClockLatency {
ALint64 ClockTime;
ALint64 Latency;
} ClockLatency;
/* Helper to get the current clock time from the device's ClockBase, and
* SamplesDone converted from the sample rate.
*/
inline ALuint64 GetDeviceClockTime(ALCdevice *device)
{
return device->ClockBase + (device->SamplesDone * DEVICE_CLOCK_RES /
device->Frequency);
}
struct ALCbackendVtable;
typedef struct ALCbackend {
@@ -20,7 +35,7 @@ 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);
ClockLatency ALCbackend_getClockLatency(ALCbackend *self);
void ALCbackend_lock(ALCbackend *self);
void ALCbackend_unlock(ALCbackend *self);
@@ -37,7 +52,7 @@ struct ALCbackendVtable {
ALCenum (*const captureSamples)(ALCbackend*, void*, ALCuint);
ALCuint (*const availableSamples)(ALCbackend*);
ALint64 (*const getLatency)(ALCbackend*);
ClockLatency (*const getClockLatency)(ALCbackend*);
void (*const lock)(ALCbackend*);
void (*const unlock)(ALCbackend*);
@@ -54,7 +69,7 @@ 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, ClockLatency, getClockLatency) \
DECLARE_THUNK(T, ALCbackend, void, lock) \
DECLARE_THUNK(T, ALCbackend, void, unlock) \
static void T##_ALCbackend_Delete(void *ptr) \
@@ -70,7 +85,7 @@ static const struct ALCbackendVtable T##_ALCbackend_vtable = { \
T##_ALCbackend_stop, \
T##_ALCbackend_captureSamples, \
T##_ALCbackend_availableSamples, \
T##_ALCbackend_getLatency, \
T##_ALCbackend_getClockLatency, \
T##_ALCbackend_lock, \
T##_ALCbackend_unlock, \
\
@@ -122,17 +137,19 @@ static const struct ALCbackendFactoryVtable T##_ALCbackendFactory_vtable = { \
ALCbackendFactory *ALCpulseBackendFactory_getFactory(void);
ALCbackendFactory *ALCalsaBackendFactory_getFactory(void);
ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void);
ALCbackendFactory *ALCossBackendFactory_getFactory(void);
ALCbackendFactory *ALCjackBackendFactory_getFactory(void);
ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void);
ALCbackendFactory *ALCsndioBackendFactory_getFactory(void);
ALCbackendFactory *ALCqsaBackendFactory_getFactory(void);
ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void);
ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void);
ALCbackendFactory *ALCwinmmBackendFactory_getFactory(void);
ALCbackendFactory *ALCportBackendFactory_getFactory(void);
ALCbackendFactory *ALCopenslBackendFactory_getFactory(void);
ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
ALCbackendFactory *ALCwaveBackendFactory_getFactory(void);
ALCbackendFactory *ALCloopbackFactory_getFactory(void);
ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type);
#endif /* AL_BACKENDS_BASE_H */
+293 -183
View File
@@ -33,6 +33,8 @@
#include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioToolbox.h>
#include "backends/base.h"
typedef struct {
AudioUnit audioUnit;
@@ -45,23 +47,12 @@ typedef struct {
AudioBufferList *bufferList; // Buffer for data coming from the input device
ALCvoid *resampleBuffer; // Buffer for returned RingBuffer data when resampling
RingBuffer *ring;
ll_ringbuffer_t *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;
@@ -83,68 +74,85 @@ static AudioBufferList* allocate_buffer_list(UInt32 channelCount, UInt32 byteSiz
return list;
}
static OSStatus ca_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
static void destroy_buffer_list(AudioBufferList* list)
{
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)
if(list)
{
ERR("AudioUnitRender error: %d\n", err);
return err;
UInt32 i;
for(i = 0;i < list->mNumberBuffers;i++)
free(list->mBuffers[i].mData);
free(list);
}
}
WriteRingBuffer(data->ring, data->bufferList->mBuffers[0].mData, inNumberFrames);
typedef struct ALCcoreAudioPlayback {
DERIVE_FROM_TYPE(ALCbackend);
AudioUnit audioUnit;
ALuint frameSize;
AudioStreamBasicDescription format; // This is the OpenAL format as a CoreAudio ASBD
} ALCcoreAudioPlayback;
static void ALCcoreAudioPlayback_Construct(ALCcoreAudioPlayback *self, ALCdevice *device);
static void ALCcoreAudioPlayback_Destruct(ALCcoreAudioPlayback *self);
static ALCenum ALCcoreAudioPlayback_open(ALCcoreAudioPlayback *self, const ALCchar *name);
static void ALCcoreAudioPlayback_close(ALCcoreAudioPlayback *self);
static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self);
static ALCboolean ALCcoreAudioPlayback_start(ALCcoreAudioPlayback *self);
static void ALCcoreAudioPlayback_stop(ALCcoreAudioPlayback *self);
static DECLARE_FORWARD2(ALCcoreAudioPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, ALCuint, availableSamples)
static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCcoreAudioPlayback)
DEFINE_ALCBACKEND_VTABLE(ALCcoreAudioPlayback);
static void ALCcoreAudioPlayback_Construct(ALCcoreAudioPlayback *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCcoreAudioPlayback, ALCbackend, self);
self->frameSize = 0;
memset(&self->format, 0, sizeof(self->format));
}
static void ALCcoreAudioPlayback_Destruct(ALCcoreAudioPlayback *self)
{
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
static OSStatus ALCcoreAudioPlayback_MixerProc(void *inRefCon,
AudioUnitRenderActionFlags* UNUSED(ioActionFlags), const AudioTimeStamp* UNUSED(inTimeStamp),
UInt32 UNUSED(inBusNumber), UInt32 UNUSED(inNumberFrames), AudioBufferList *ioData)
{
ALCcoreAudioPlayback *self = inRefCon;
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
ALCdevice_Lock(device);
aluMixData(device, ioData->mBuffers[0].mData,
ioData->mBuffers[0].mDataByteSize / self->frameSize);
ALCdevice_Unlock(device);
return noErr;
}
static ALCenum ca_open_playback(ALCdevice *device, const ALCchar *deviceName)
static ALCenum ALCcoreAudioPlayback_open(ALCcoreAudioPlayback *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
AudioComponentDescription desc;
AudioComponent comp;
ca_data *data;
OSStatus err;
if(!deviceName)
deviceName = ca_device;
else if(strcmp(deviceName, ca_device) != 0)
if(!name)
name = ca_device;
else if(strcmp(name, ca_device) != 0)
return ALC_INVALID_VALUE;
/* open the default output unit */
@@ -161,57 +169,47 @@ static ALCenum ca_open_playback(ALCdevice *device, const ALCchar *deviceName)
return ALC_INVALID_VALUE;
}
data = calloc(1, sizeof(*data));
err = AudioComponentInstanceNew(comp, &data->audioUnit);
err = AudioComponentInstanceNew(comp, &self->audioUnit);
if(err != noErr)
{
ERR("AudioComponentInstanceNew failed\n");
free(data);
return ALC_INVALID_VALUE;
}
/* init and start the default audio unit... */
err = AudioUnitInitialize(data->audioUnit);
err = AudioUnitInitialize(self->audioUnit);
if(err != noErr)
{
ERR("AudioUnitInitialize failed\n");
AudioComponentInstanceDispose(data->audioUnit);
free(data);
AudioComponentInstanceDispose(self->audioUnit);
return ALC_INVALID_VALUE;
}
al_string_copy_cstr(&device->DeviceName, deviceName);
device->ExtraData = data;
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
static void ca_close_playback(ALCdevice *device)
static void ALCcoreAudioPlayback_close(ALCcoreAudioPlayback *self)
{
ca_data *data = (ca_data*)device->ExtraData;
AudioUnitUninitialize(data->audioUnit);
AudioComponentInstanceDispose(data->audioUnit);
free(data);
device->ExtraData = NULL;
AudioUnitUninitialize(self->audioUnit);
AudioComponentInstanceDispose(self->audioUnit);
}
static ALCboolean ca_reset_playback(ALCdevice *device)
static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self)
{
ca_data *data = (ca_data*)device->ExtraData;
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
AudioStreamBasicDescription streamFormat;
AURenderCallbackStruct input;
OSStatus err;
UInt32 size;
err = AudioUnitUninitialize(data->audioUnit);
err = AudioUnitUninitialize(self->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);
err = AudioUnitGetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamFormat, &size);
if(err != noErr || size != sizeof(AudioStreamBasicDescription))
{
ERR("AudioUnitGetProperty failed\n");
@@ -229,7 +227,7 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
#endif
/* set default output unit's input side to match output side */
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, size);
err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, size);
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
@@ -238,7 +236,7 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
if(device->Frequency != streamFormat.mSampleRate)
{
device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize *
device->NumUpdates = (ALuint)((ALuint64)device->NumUpdates *
streamFormat.mSampleRate /
device->Frequency);
device->Frequency = streamFormat.mSampleRate;
@@ -313,7 +311,7 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
streamFormat.mFormatFlags |= kAudioFormatFlagsNativeEndian |
kLinearPCMFormatFlagIsPacked;
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, sizeof(AudioStreamBasicDescription));
err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, sizeof(AudioStreamBasicDescription));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
@@ -321,11 +319,11 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
}
/* setup callback */
data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
input.inputProc = ca_callback;
input.inputProcRefCon = device;
self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
input.inputProc = ALCcoreAudioPlayback_MixerProc;
input.inputProcRefCon = self;
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct));
err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
@@ -333,7 +331,7 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
}
/* init the default audio unit... */
err = AudioUnitInitialize(data->audioUnit);
err = AudioUnitInitialize(self->audioUnit);
if(err != noErr)
{
ERR("AudioUnitInitialize failed\n");
@@ -343,12 +341,9 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
return ALC_TRUE;
}
static ALCboolean ca_start_playback(ALCdevice *device)
static ALCboolean ALCcoreAudioPlayback_start(ALCcoreAudioPlayback *self)
{
ca_data *data = (ca_data*)device->ExtraData;
OSStatus err;
err = AudioOutputUnitStart(data->audioUnit);
OSStatus err = AudioOutputUnitStart(self->audioUnit);
if(err != noErr)
{
ERR("AudioOutputUnitStart failed\n");
@@ -358,18 +353,107 @@ static ALCboolean ca_start_playback(ALCdevice *device)
return ALC_TRUE;
}
static void ca_stop_playback(ALCdevice *device)
static void ALCcoreAudioPlayback_stop(ALCcoreAudioPlayback *self)
{
ca_data *data = (ca_data*)device->ExtraData;
OSStatus err;
err = AudioOutputUnitStop(data->audioUnit);
OSStatus err = AudioOutputUnitStop(self->audioUnit);
if(err != noErr)
ERR("AudioOutputUnitStop failed\n");
}
static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
typedef struct ALCcoreAudioCapture {
DERIVE_FROM_TYPE(ALCbackend);
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
ll_ringbuffer_t *ring;
} ALCcoreAudioCapture;
static void ALCcoreAudioCapture_Construct(ALCcoreAudioCapture *self, ALCdevice *device);
static void ALCcoreAudioCapture_Destruct(ALCcoreAudioCapture *self);
static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar *name);
static void ALCcoreAudioCapture_close(ALCcoreAudioCapture *self);
static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, ALCboolean, reset)
static ALCboolean ALCcoreAudioCapture_start(ALCcoreAudioCapture *self);
static void ALCcoreAudioCapture_stop(ALCcoreAudioCapture *self);
static ALCenum ALCcoreAudioCapture_captureSamples(ALCcoreAudioCapture *self, ALCvoid *buffer, ALCuint samples);
static ALCuint ALCcoreAudioCapture_availableSamples(ALCcoreAudioCapture *self);
static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCcoreAudioCapture)
DEFINE_ALCBACKEND_VTABLE(ALCcoreAudioCapture);
static void ALCcoreAudioCapture_Construct(ALCcoreAudioCapture *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCcoreAudioCapture, ALCbackend, self);
}
static void ALCcoreAudioCapture_Destruct(ALCcoreAudioCapture *self)
{
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
static OSStatus ALCcoreAudioCapture_RecordProc(void *inRefCon,
AudioUnitRenderActionFlags* UNUSED(ioActionFlags),
const AudioTimeStamp *inTimeStamp, UInt32 UNUSED(inBusNumber),
UInt32 inNumberFrames, AudioBufferList* UNUSED(ioData))
{
ALCcoreAudioCapture *self = inRefCon;
AudioUnitRenderActionFlags flags = 0;
OSStatus err;
// fill the bufferList with data from the input device
err = AudioUnitRender(self->audioUnit, &flags, inTimeStamp, 1, inNumberFrames, self->bufferList);
if(err != noErr)
{
ERR("AudioUnitRender error: %d\n", err);
return err;
}
ll_ringbuffer_write(self->ring, self->bufferList->mBuffers[0].mData, inNumberFrames);
return noErr;
}
static OSStatus ALCcoreAudioCapture_ConvertCallback(AudioConverterRef UNUSED(inAudioConverter),
UInt32 *ioNumberDataPackets, AudioBufferList *ioData,
AudioStreamPacketDescription** UNUSED(outDataPacketDescription),
void *inUserData)
{
ALCcoreAudioCapture *self = inUserData;
// Read from the ring buffer and store temporarily in a large buffer
ll_ringbuffer_read(self->ring, self->resampleBuffer, *ioNumberDataPackets);
// Set the input data
ioData->mNumberBuffers = 1;
ioData->mBuffers[0].mNumberChannels = self->format.mChannelsPerFrame;
ioData->mBuffers[0].mData = self->resampleBuffer;
ioData->mBuffers[0].mDataByteSize = (*ioNumberDataPackets) * self->format.mBytesPerFrame;
return noErr;
}
static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
AudioStreamBasicDescription requestedFormat; // The application requested format
AudioStreamBasicDescription hardwareFormat; // The hardware format
AudioStreamBasicDescription outputFormat; // The AudioUnit output format
@@ -381,12 +465,11 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
AudioObjectPropertyAddress propertyAddress;
UInt32 enableIO;
AudioComponent comp;
ca_data *data;
OSStatus err;
if(!deviceName)
deviceName = ca_device;
else if(strcmp(deviceName, ca_device) != 0)
if(!name)
name = ca_device;
else if(strcmp(name, ca_device) != 0)
return ALC_INVALID_VALUE;
desc.componentType = kAudioUnitType_Output;
@@ -403,11 +486,8 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
return ALC_INVALID_VALUE;
}
data = calloc(1, sizeof(*data));
device->ExtraData = data;
// Open the component
err = AudioComponentInstanceNew(comp, &data->audioUnit);
err = AudioComponentInstanceNew(comp, &self->audioUnit);
if(err != noErr)
{
ERR("AudioComponentInstanceNew failed\n");
@@ -416,7 +496,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
// Turn off AudioUnit output
enableIO = 0;
err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint));
err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
@@ -425,7 +505,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
// Turn on AudioUnit input
enableIO = 1;
err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint));
err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
@@ -453,7 +533,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
}
// Track the input device
err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID));
err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
@@ -461,10 +541,10 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
}
// set capture callback
input.inputProc = ca_capture_callback;
input.inputProcRefCon = device;
input.inputProc = ALCcoreAudioCapture_RecordProc;
input.inputProcRefCon = self;
err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct));
err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
@@ -472,7 +552,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
}
// Initialize the device
err = AudioUnitInitialize(data->audioUnit);
err = AudioUnitInitialize(self->audioUnit);
if(err != noErr)
{
ERR("AudioUnitInitialize failed\n");
@@ -481,7 +561,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
// Get the hardware format
propertySize = sizeof(AudioStreamBasicDescription);
err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &hardwareFormat, &propertySize);
err = AudioUnitGetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &hardwareFormat, &propertySize);
if(err != noErr || propertySize != sizeof(AudioStreamBasicDescription))
{
ERR("AudioUnitGetProperty failed\n");
@@ -528,7 +608,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
case DevFmtX51Rear:
case DevFmtX61:
case DevFmtX71:
case DevFmtBFormat3D:
case DevFmtAmbi3D:
ERR("%s not supported\n", DevFmtChannelsString(device->FmtChans));
goto error;
}
@@ -541,8 +621,8 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
requestedFormat.mFramesPerPacket = 1;
// save requested format description for later use
data->format = requestedFormat;
data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
self->format = requestedFormat;
self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
// Use intermediate format for sample rate conversion (outputFormat)
// Set sample rate to the same as hardware for resampling later
@@ -550,11 +630,11 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
outputFormat.mSampleRate = hardwareFormat.mSampleRate;
// Determine sample rate ratio for resampling
data->sampleRateRatio = outputFormat.mSampleRate / device->Frequency;
self->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));
err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, (void *)&outputFormat, sizeof(outputFormat));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
@@ -562,8 +642,8 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
}
// Set the AudioUnit output format frame count
outputFrameCount = device->UpdateSize * data->sampleRateRatio;
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount));
outputFrameCount = device->UpdateSize * self->sampleRateRatio;
err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed: %d\n", err);
@@ -571,7 +651,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
}
// Set up sample converter
err = AudioConverterNew(&outputFormat, &requestedFormat, &data->audioConverter);
err = AudioConverterNew(&outputFormat, &requestedFormat, &self->audioConverter);
if(err != noErr)
{
ERR("AudioConverterNew failed: %d\n", err);
@@ -579,71 +659,71 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
}
// Create a buffer for use in the resample callback
data->resampleBuffer = malloc(device->UpdateSize * data->frameSize * data->sampleRateRatio);
self->resampleBuffer = malloc(device->UpdateSize * self->frameSize * self->sampleRateRatio);
// Allocate buffer for the AudioUnit output
data->bufferList = allocate_buffer_list(outputFormat.mChannelsPerFrame, device->UpdateSize * data->frameSize * data->sampleRateRatio);
if(data->bufferList == NULL)
self->bufferList = allocate_buffer_list(outputFormat.mChannelsPerFrame, device->UpdateSize * self->frameSize * self->sampleRateRatio);
if(self->bufferList == NULL)
goto error;
data->ring = CreateRingBuffer(data->frameSize, (device->UpdateSize * data->sampleRateRatio) * device->NumUpdates);
if(data->ring == NULL)
goto error;
self->ring = ll_ringbuffer_create(
device->UpdateSize*self->sampleRateRatio*device->NumUpdates + 1,
self->frameSize
);
if(!self->ring) goto error;
al_string_copy_cstr(&device->DeviceName, deviceName);
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
error:
DestroyRingBuffer(data->ring);
free(data->resampleBuffer);
destroy_buffer_list(data->bufferList);
ll_ringbuffer_free(self->ring);
self->ring = NULL;
free(self->resampleBuffer);
destroy_buffer_list(self->bufferList);
if(data->audioConverter)
AudioConverterDispose(data->audioConverter);
if(data->audioUnit)
AudioComponentInstanceDispose(data->audioUnit);
free(data);
device->ExtraData = NULL;
if(self->audioConverter)
AudioConverterDispose(self->audioConverter);
if(self->audioUnit)
AudioComponentInstanceDispose(self->audioUnit);
return ALC_INVALID_VALUE;
}
static void ca_close_capture(ALCdevice *device)
static void ALCcoreAudioCapture_close(ALCcoreAudioCapture *self)
{
ca_data *data = (ca_data*)device->ExtraData;
ll_ringbuffer_free(self->ring);
self->ring = NULL;
DestroyRingBuffer(data->ring);
free(data->resampleBuffer);
destroy_buffer_list(data->bufferList);
free(self->resampleBuffer);
AudioConverterDispose(data->audioConverter);
AudioComponentInstanceDispose(data->audioUnit);
destroy_buffer_list(self->bufferList);
free(data);
device->ExtraData = NULL;
AudioConverterDispose(self->audioConverter);
AudioComponentInstanceDispose(self->audioUnit);
}
static void ca_start_capture(ALCdevice *device)
static ALCboolean ALCcoreAudioCapture_start(ALCcoreAudioCapture *self)
{
ca_data *data = (ca_data*)device->ExtraData;
OSStatus err = AudioOutputUnitStart(data->audioUnit);
OSStatus err = AudioOutputUnitStart(self->audioUnit);
if(err != noErr)
{
ERR("AudioOutputUnitStart failed\n");
return ALC_FALSE;
}
return ALC_TRUE;
}
static void ca_stop_capture(ALCdevice *device)
static void ALCcoreAudioCapture_stop(ALCcoreAudioCapture *self)
{
ca_data *data = (ca_data*)device->ExtraData;
OSStatus err = AudioOutputUnitStop(data->audioUnit);
OSStatus err = AudioOutputUnitStop(self->audioUnit);
if(err != noErr)
ERR("AudioOutputUnitStop failed\n");
}
static ALCenum ca_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
static ALCenum ALCcoreAudioCapture_captureSamples(ALCcoreAudioCapture *self, ALCvoid *buffer, ALCuint samples)
{
ca_data *data = (ca_data*)device->ExtraData;
AudioBufferList *list;
UInt32 frameCount;
OSStatus err;
@@ -657,14 +737,15 @@ static ALCenum ca_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint sa
// 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].mNumberChannels = self->format.mChannelsPerFrame;
list->mBuffers[0].mDataByteSize = samples * self->frameSize;
list->mBuffers[0].mData = buffer;
// Resample into another AudioBufferList
frameCount = samples;
err = AudioConverterFillComplexBuffer(data->audioConverter, ca_capture_conversion_callback,
device, &frameCount, list, NULL);
err = AudioConverterFillComplexBuffer(self->audioConverter,
ALCcoreAudioCapture_ConvertCallback, self, &frameCount, list, NULL
);
if(err != noErr)
{
ERR("AudioConverterFillComplexBuffer error: %d\n", err);
@@ -673,38 +754,47 @@ static ALCenum ca_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint sa
return ALC_NO_ERROR;
}
static ALCuint ca_available_samples(ALCdevice *device)
static ALCuint ALCcoreAudioCapture_availableSamples(ALCcoreAudioCapture *self)
{
ca_data *data = device->ExtraData;
return RingBufferSize(data->ring) / data->sampleRateRatio;
return ll_ringbuffer_read_space(self->ring) / self->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
};
typedef struct ALCcoreAudioBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCcoreAudioBackendFactory;
#define ALCCOREAUDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCcoreAudioBackendFactory, ALCbackendFactory) } }
ALCboolean alc_ca_init(BackendFuncs *func_list)
ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void);
static ALCboolean ALCcoreAudioBackendFactory_init(ALCcoreAudioBackendFactory *self);
static DECLARE_FORWARD(ALCcoreAudioBackendFactory, ALCbackendFactory, void, deinit)
static ALCboolean ALCcoreAudioBackendFactory_querySupport(ALCcoreAudioBackendFactory *self, ALCbackend_Type type);
static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory *self, enum DevProbe type);
static ALCbackend* ALCcoreAudioBackendFactory_createBackend(ALCcoreAudioBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCcoreAudioBackendFactory);
ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void)
{
static ALCcoreAudioBackendFactory factory = ALCCOREAUDIOBACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
static ALCboolean ALCcoreAudioBackendFactory_init(ALCcoreAudioBackendFactory* UNUSED(self))
{
*func_list = ca_funcs;
return ALC_TRUE;
}
void alc_ca_deinit(void)
static ALCboolean ALCcoreAudioBackendFactory_querySupport(ALCcoreAudioBackendFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Playback || ALCbackend_Capture)
return ALC_TRUE;
return ALC_FALSE;
}
void alc_ca_probe(enum DevProbe type)
static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory* UNUSED(self), enum DevProbe type)
{
switch(type)
{
@@ -716,3 +806,23 @@ void alc_ca_probe(enum DevProbe type)
break;
}
}
static ALCbackend* ALCcoreAudioBackendFactory_createBackend(ALCcoreAudioBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCcoreAudioPlayback *backend;
NEW_OBJ(backend, ALCcoreAudioPlayback)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
if(type == ALCbackend_Capture)
{
ALCcoreAudioCapture *backend;
NEW_OBJ(backend, ALCcoreAudioCapture)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
+120 -118
View File
@@ -123,7 +123,7 @@ static void clear_devlist(vector_DevMap *list)
{
#define DEINIT_STR(i) AL_STRING_DEINIT((i)->name)
VECTOR_FOR_EACH(DevMap, *list, DEINIT_STR);
VECTOR_RESIZE(*list, 0);
VECTOR_RESIZE(*list, 0, 0);
#undef DEINIT_STR
}
@@ -145,18 +145,18 @@ static BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHA
{
const DevMap *iter;
al_string_copy_cstr(&entry.name, DEVNAME_HEAD);
al_string_append_wcstr(&entry.name, desc);
alstr_copy_cstr(&entry.name, DEVNAME_HEAD);
alstr_append_wcstr(&entry.name, desc);
if(count != 0)
{
char str[64];
snprintf(str, sizeof(str), " #%d", count+1);
al_string_append_cstr(&entry.name, str);
alstr_append_cstr(&entry.name, str);
}
#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
#define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0)
VECTOR_FIND_IF(iter, const DevMap, *devices, MATCH_ENTRY);
if(iter == VECTOR_ITER_END(*devices)) break;
if(iter == VECTOR_END(*devices)) break;
#undef MATCH_ENTRY
count++;
}
@@ -165,7 +165,7 @@ static BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHA
hr = StringFromCLSID(guid, &guidstr);
if(SUCCEEDED(hr))
{
TRACE("Got device \"%s\", GUID \"%ls\"\n", al_string_get_cstr(entry.name), guidstr);
TRACE("Got device \"%s\", GUID \"%ls\"\n", alstr_get_cstr(entry.name), guidstr);
CoTaskMemFree(guidstr);
}
@@ -199,7 +199,7 @@ static ALCboolean ALCdsoundPlayback_start(ALCdsoundPlayback *self);
static void ALCdsoundPlayback_stop(ALCdsoundPlayback *self);
static DECLARE_FORWARD2(ALCdsoundPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ALCuint, availableSamples)
static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ALint64, getLatency)
static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCdsoundPlayback)
@@ -244,7 +244,7 @@ FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr)
return 1;
}
FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
FragSize = device->UpdateSize * FrameSize;
IDirectSoundBuffer_GetCurrentPosition(self->Buffer, &LastCursor, NULL);
@@ -299,8 +299,10 @@ FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr)
if(SUCCEEDED(err))
{
// If we have an active context, mix data directly into output buffer otherwise fill with silence
ALCdevice_Lock(device);
aluMixData(device, WritePtr1, WriteCnt1/FrameSize);
aluMixData(device, WritePtr2, WriteCnt2/FrameSize);
ALCdevice_Unlock(device);
// Unlock output buffer only when successfully locked
IDirectSoundBuffer_Unlock(self->Buffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
@@ -341,23 +343,23 @@ static ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *de
if(!deviceName && VECTOR_SIZE(PlaybackDevices) > 0)
{
deviceName = al_string_get_cstr(VECTOR_FRONT(PlaybackDevices).name);
deviceName = alstr_get_cstr(VECTOR_FRONT(PlaybackDevices).name);
guid = &VECTOR_FRONT(PlaybackDevices).guid;
}
else
{
const DevMap *iter;
#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0)
#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0)
VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
#undef MATCH_NAME
if(iter == VECTOR_ITER_END(PlaybackDevices))
if(iter == VECTOR_END(PlaybackDevices))
return ALC_INVALID_VALUE;
guid = &iter->guid;
}
hr = DS_OK;
self->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
self->NotifyEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
if(self->NotifyEvent == NULL)
hr = E_FAIL;
@@ -379,7 +381,7 @@ static ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *de
return ALC_INVALID_VALUE;
}
al_string_copy_cstr(&device->DeviceName, deviceName);
alstr_copy_cstr(&device->DeviceName, deviceName);
return ALC_NO_ERROR;
}
@@ -472,7 +474,7 @@ static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self)
case DevFmtMono:
OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;
break;
case DevFmtBFormat3D:
case DevFmtAmbi3D:
device->FmtChans = DevFmtStereo;
/*fall-through*/
case DevFmtStereo:
@@ -525,7 +527,7 @@ static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self)
retry_open:
hr = S_OK;
OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
OutputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
OutputType.Format.nSamplesPerSec = device->Frequency;
@@ -653,7 +655,8 @@ typedef struct ALCdsoundCapture {
IDirectSoundCaptureBuffer *DSCbuffer;
DWORD BufferBytes;
DWORD Cursor;
RingBuffer *Ring;
ll_ringbuffer_t *Ring;
} ALCdsoundCapture;
static void ALCdsoundCapture_Construct(ALCdsoundCapture *self, ALCdevice *device);
@@ -665,7 +668,7 @@ static ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self);
static void ALCdsoundCapture_stop(ALCdsoundCapture *self);
static ALCenum ALCdsoundCapture_captureSamples(ALCdsoundCapture *self, ALCvoid *buffer, ALCuint samples);
static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self);
static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ALint64, getLatency)
static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCdsoundCapture)
@@ -701,17 +704,17 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi
if(!deviceName && VECTOR_SIZE(CaptureDevices) > 0)
{
deviceName = al_string_get_cstr(VECTOR_FRONT(CaptureDevices).name);
deviceName = alstr_get_cstr(VECTOR_FRONT(CaptureDevices).name);
guid = &VECTOR_FRONT(CaptureDevices).guid;
}
else
{
const DevMap *iter;
#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0)
#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0)
VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
#undef MATCH_NAME
if(iter == VECTOR_ITER_END(CaptureDevices))
if(iter == VECTOR_END(CaptureDevices))
return ALC_INVALID_VALUE;
guid = &iter->guid;
}
@@ -731,99 +734,98 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi
break;
}
memset(&InputType, 0, sizeof(InputType));
switch(device->FmtChans)
{
case DevFmtMono:
InputType.dwChannelMask = SPEAKER_FRONT_CENTER;
break;
case DevFmtStereo:
InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
SPEAKER_FRONT_RIGHT;
break;
case DevFmtQuad:
InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
SPEAKER_FRONT_RIGHT |
SPEAKER_BACK_LEFT |
SPEAKER_BACK_RIGHT;
break;
case DevFmtX51:
InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
SPEAKER_FRONT_RIGHT |
SPEAKER_FRONT_CENTER |
SPEAKER_LOW_FREQUENCY |
SPEAKER_SIDE_LEFT |
SPEAKER_SIDE_RIGHT;
break;
case DevFmtX51Rear:
InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
SPEAKER_FRONT_RIGHT |
SPEAKER_FRONT_CENTER |
SPEAKER_LOW_FREQUENCY |
SPEAKER_BACK_LEFT |
SPEAKER_BACK_RIGHT;
break;
case DevFmtX61:
InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
SPEAKER_FRONT_RIGHT |
SPEAKER_FRONT_CENTER |
SPEAKER_LOW_FREQUENCY |
SPEAKER_BACK_CENTER |
SPEAKER_SIDE_LEFT |
SPEAKER_SIDE_RIGHT;
break;
case DevFmtX71:
InputType.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;
break;
case DevFmtAmbi3D:
WARN("%s capture not supported\n", DevFmtChannelsString(device->FmtChans));
return ALC_INVALID_ENUM;
}
InputType.Format.wFormatTag = WAVE_FORMAT_PCM;
InputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
InputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8;
InputType.Format.nSamplesPerSec = device->Frequency;
InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign;
InputType.Format.cbSize = 0;
InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
if(device->FmtType == DevFmtFloat)
InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
else
InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
if(InputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
{
InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
}
samples = device->UpdateSize * device->NumUpdates;
samples = maxu(samples, 100 * device->Frequency / 1000);
memset(&DSCBDescription, 0, sizeof(DSCBUFFERDESC));
DSCBDescription.dwSize = sizeof(DSCBUFFERDESC);
DSCBDescription.dwFlags = 0;
DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign;
DSCBDescription.lpwfxFormat = &InputType.Format;
//DirectSoundCapture Init code
hr = DirectSoundCaptureCreate(guid, &self->DSC, NULL);
if(SUCCEEDED(hr))
{
memset(&InputType, 0, sizeof(InputType));
switch(device->FmtChans)
{
case DevFmtMono:
InputType.dwChannelMask = SPEAKER_FRONT_CENTER;
break;
case DevFmtStereo:
InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
SPEAKER_FRONT_RIGHT;
break;
case DevFmtQuad:
InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
SPEAKER_FRONT_RIGHT |
SPEAKER_BACK_LEFT |
SPEAKER_BACK_RIGHT;
break;
case DevFmtX51:
InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
SPEAKER_FRONT_RIGHT |
SPEAKER_FRONT_CENTER |
SPEAKER_LOW_FREQUENCY |
SPEAKER_SIDE_LEFT |
SPEAKER_SIDE_RIGHT;
break;
case DevFmtX51Rear:
InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
SPEAKER_FRONT_RIGHT |
SPEAKER_FRONT_CENTER |
SPEAKER_LOW_FREQUENCY |
SPEAKER_BACK_LEFT |
SPEAKER_BACK_RIGHT;
break;
case DevFmtX61:
InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
SPEAKER_FRONT_RIGHT |
SPEAKER_FRONT_CENTER |
SPEAKER_LOW_FREQUENCY |
SPEAKER_BACK_CENTER |
SPEAKER_SIDE_LEFT |
SPEAKER_SIDE_RIGHT;
break;
case DevFmtX71:
InputType.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;
break;
case DevFmtBFormat3D:
break;
}
InputType.Format.wFormatTag = WAVE_FORMAT_PCM;
InputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
InputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8;
InputType.Format.nSamplesPerSec = device->Frequency;
InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign;
InputType.Format.cbSize = 0;
if(InputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
{
InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
if(device->FmtType == DevFmtFloat)
InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
else
InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
}
samples = device->UpdateSize * device->NumUpdates;
samples = maxu(samples, 100 * device->Frequency / 1000);
memset(&DSCBDescription, 0, sizeof(DSCBUFFERDESC));
DSCBDescription.dwSize = sizeof(DSCBUFFERDESC);
DSCBDescription.dwFlags = 0;
DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign;
DSCBDescription.lpwfxFormat = &InputType.Format;
hr = IDirectSoundCapture_CreateCaptureBuffer(self->DSC, &DSCBDescription, &self->DSCbuffer, NULL);
}
if(SUCCEEDED(hr))
{
self->Ring = CreateRingBuffer(InputType.Format.nBlockAlign, device->UpdateSize * device->NumUpdates);
self->Ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates + 1,
InputType.Format.nBlockAlign);
if(self->Ring == NULL)
hr = DSERR_OUTOFMEMORY;
}
@@ -832,7 +834,7 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi
{
ERR("Device init failed: 0x%08lx\n", hr);
DestroyRingBuffer(self->Ring);
ll_ringbuffer_free(self->Ring);
self->Ring = NULL;
if(self->DSCbuffer != NULL)
IDirectSoundCaptureBuffer_Release(self->DSCbuffer);
@@ -847,14 +849,14 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi
self->BufferBytes = DSCBDescription.dwBufferBytes;
SetDefaultWFXChannelOrder(device);
al_string_copy_cstr(&device->DeviceName, deviceName);
alstr_copy_cstr(&device->DeviceName, deviceName);
return ALC_NO_ERROR;
}
static void ALCdsoundCapture_close(ALCdsoundCapture *self)
{
DestroyRingBuffer(self->Ring);
ll_ringbuffer_free(self->Ring);
self->Ring = NULL;
if(self->DSCbuffer != NULL)
@@ -897,7 +899,7 @@ static void ALCdsoundCapture_stop(ALCdsoundCapture *self)
static ALCenum ALCdsoundCapture_captureSamples(ALCdsoundCapture *self, ALCvoid *buffer, ALCuint samples)
{
ReadRingBuffer(self->Ring, buffer, samples);
ll_ringbuffer_read(self->Ring, buffer, samples);
return ALC_NO_ERROR;
}
@@ -913,7 +915,7 @@ static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self)
if(!device->Connected)
goto done;
FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
BufferBytes = self->BufferBytes;
LastCursor = self->Cursor;
@@ -929,9 +931,9 @@ static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self)
}
if(SUCCEEDED(hr))
{
WriteRingBuffer(self->Ring, ReadPtr1, ReadCnt1/FrameSize);
ll_ringbuffer_write(self->Ring, ReadPtr1, ReadCnt1/FrameSize);
if(ReadPtr2 != NULL)
WriteRingBuffer(self->Ring, ReadPtr2, ReadCnt2/FrameSize);
ll_ringbuffer_write(self->Ring, ReadPtr2, ReadCnt2/FrameSize);
hr = IDirectSoundCaptureBuffer_Unlock(self->DSCbuffer,
ReadPtr1, ReadCnt1,
ReadPtr2, ReadCnt2);
@@ -945,14 +947,14 @@ static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self)
}
done:
return RingBufferSize(self->Ring);
return ll_ringbuffer_read_space(self->Ring);
}
static inline void AppendAllDevicesList2(const DevMap *entry)
{ AppendAllDevicesList(al_string_get_cstr(entry->name)); }
{ AppendAllDevicesList(alstr_get_cstr(entry->name)); }
static inline void AppendCaptureDeviceList2(const DevMap *entry)
{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); }
{ AppendCaptureDeviceList(alstr_get_cstr(entry->name)); }
typedef struct ALCdsoundBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
+62 -32
View File
@@ -54,6 +54,7 @@ static const ALCchar jackDevice[] = "JACK Default";
MAGIC(jack_get_ports); \
MAGIC(jack_free); \
MAGIC(jack_get_sample_rate); \
MAGIC(jack_set_error_function); \
MAGIC(jack_set_process_callback); \
MAGIC(jack_set_buffer_size_callback); \
MAGIC(jack_set_buffer_size); \
@@ -62,6 +63,7 @@ static const ALCchar jackDevice[] = "JACK Default";
static void *jack_handle;
#define MAKE_FUNC(f) static __typeof(f) * p##f
JACK_FUNCS(MAKE_FUNC);
static __typeof(jack_error_callback) * pjack_error_callback;
#undef MAKE_FUNC
#define jack_client_open pjack_client_open
@@ -78,10 +80,12 @@ JACK_FUNCS(MAKE_FUNC);
#define jack_get_ports pjack_get_ports
#define jack_free pjack_free
#define jack_get_sample_rate pjack_get_sample_rate
#define jack_set_error_function pjack_set_error_function
#define jack_set_process_callback pjack_set_process_callback
#define jack_set_buffer_size_callback pjack_set_buffer_size_callback
#define jack_set_buffer_size pjack_set_buffer_size
#define jack_get_buffer_size pjack_get_buffer_size
#define jack_error_callback (*pjack_error_callback)
#endif
@@ -94,26 +98,42 @@ static ALCboolean jack_load(void)
#ifdef HAVE_DYNLOAD
if(!jack_handle)
{
jack_handle = LoadLib("libjack.so.0");
al_string missing_funcs = AL_STRING_INIT_STATIC();
#ifdef _WIN32
#define JACKLIB "libjack.dll"
#else
#define JACKLIB "libjack.so.0"
#endif
jack_handle = LoadLib(JACKLIB);
if(!jack_handle)
{
WARN("Failed to load %s\n", JACKLIB);
return ALC_FALSE;
}
error = ALC_FALSE;
#define LOAD_FUNC(f) do { \
p##f = GetSymbol(jack_handle, #f); \
if(p##f == NULL) { \
error = ALC_TRUE; \
alstr_append_cstr(&missing_funcs, "\n" #f); \
} \
} while(0)
JACK_FUNCS(LOAD_FUNC);
#undef LOAD_FUNC
/* Optional symbols. These don't exist in all versions of JACK. */
#define LOAD_SYM(f) p##f = GetSymbol(jack_handle, #f)
LOAD_SYM(jack_error_callback);
#undef LOAD_SYM
if(error)
{
WARN("Missing expected functions:%s\n", alstr_get_cstr(missing_funcs));
CloseLib(jack_handle);
jack_handle = NULL;
return ALC_FALSE;
}
alstr_reset(&missing_funcs);
}
#endif
@@ -148,9 +168,9 @@ static ALCboolean ALCjackPlayback_start(ALCjackPlayback *self);
static void ALCjackPlayback_stop(ALCjackPlayback *self);
static DECLARE_FORWARD2(ALCjackPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCjackPlayback, ALCbackend, ALCuint, availableSamples)
static ALint64 ALCjackPlayback_getLatency(ALCjackPlayback *self);
static void ALCjackPlayback_lock(ALCjackPlayback *self);
static void ALCjackPlayback_unlock(ALCjackPlayback *self);
static ClockLatency ALCjackPlayback_getClockLatency(ALCjackPlayback *self);
static DECLARE_FORWARD(ALCjackPlayback, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCjackPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCjackPlayback)
DEFINE_ALCBACKEND_VTABLE(ALCjackPlayback);
@@ -204,15 +224,19 @@ static int ALCjackPlayback_bufferSizeNotify(jack_nframes_t numframes, void *arg)
ALCjackPlayback_lock(self);
device->UpdateSize = numframes;
device->NumUpdates = 2;
TRACE("%u update size x%u\n", device->UpdateSize, device->NumUpdates);
bufsize = device->UpdateSize;
if(ConfigValueUInt(al_string_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize))
if(ConfigValueUInt(alstr_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize))
bufsize = maxu(NextPowerOf2(bufsize), device->UpdateSize);
bufsize += device->UpdateSize;
device->NumUpdates = bufsize / device->UpdateSize;
TRACE("%u update size x%u\n", device->UpdateSize, device->NumUpdates);
ll_ringbuffer_free(self->Ring);
self->Ring = ll_ringbuffer_create(bufsize, FrameSizeFromDevFmt(device->FmtChans, device->FmtType));
self->Ring = ll_ringbuffer_create(bufsize,
FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder)
);
if(!self->Ring)
{
ERR("Failed to reallocate ringbuffer\n");
@@ -230,7 +254,7 @@ static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg)
ll_ringbuffer_data_t data[2];
jack_nframes_t total = 0;
jack_nframes_t todo;
ALuint i, c, numchans;
ALsizei i, c, numchans;
ll_ringbuffer_get_read_vector(self->Ring, data);
@@ -241,8 +265,9 @@ static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg)
todo = minu(numframes, data[0].len);
for(c = 0;c < numchans;c++)
{
for(i = 0;i < todo;i++)
out[c][i] = ((ALfloat*)data[0].buf)[i*numchans + c];
const ALfloat *restrict in = ((ALfloat*)data[0].buf) + c;
for(i = 0;(jack_nframes_t)i < todo;i++)
out[c][i] = in[i*numchans];
out[c] += todo;
}
total += todo;
@@ -252,8 +277,9 @@ static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg)
{
for(c = 0;c < numchans;c++)
{
for(i = 0;i < todo;i++)
out[c][i] = ((ALfloat*)data[1].buf)[i*numchans + c];
const ALfloat *restrict in = ((ALfloat*)data[1].buf) + c;
for(i = 0;(jack_nframes_t)i < todo;i++)
out[c][i] = in[i*numchans];
out[c] += todo;
}
total += todo;
@@ -267,7 +293,7 @@ static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg)
todo = numframes-total;
for(c = 0;c < numchans;c++)
{
for(i = 0;i < todo;i++)
for(i = 0;(jack_nframes_t)i < todo;i++)
out[c][i] = 0.0f;
}
}
@@ -355,7 +381,7 @@ static ALCenum ALCjackPlayback_open(ALCjackPlayback *self, const ALCchar *name)
jack_set_process_callback(self->Client, ALCjackPlayback_process, self);
jack_set_buffer_size_callback(self->Client, ALCjackPlayback_bufferSizeNotify, self);
al_string_copy_cstr(&device->DeviceName, name);
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
@@ -377,7 +403,7 @@ static void ALCjackPlayback_close(ALCjackPlayback *self)
static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
ALuint numchans, i;
ALsizei numchans, i;
ALuint bufsize;
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
@@ -397,14 +423,15 @@ static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self)
device->NumUpdates = 2;
bufsize = device->UpdateSize;
if(ConfigValueUInt(al_string_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize))
if(ConfigValueUInt(alstr_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize))
bufsize = maxu(NextPowerOf2(bufsize), device->UpdateSize);
bufsize += device->UpdateSize;
device->NumUpdates = bufsize / device->UpdateSize;
/* Force 32-bit float output. */
device->FmtType = DevFmtFloat;
numchans = ChannelsFromDevFmt(device->FmtChans);
numchans = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
for(i = 0;i < numchans;i++)
{
char name[64];
@@ -433,7 +460,9 @@ static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self)
}
ll_ringbuffer_free(self->Ring);
self->Ring = ll_ringbuffer_create(bufsize, FrameSizeFromDevFmt(device->FmtChans, device->FmtType));
self->Ring = ll_ringbuffer_create(bufsize,
FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder)
);
if(!self->Ring)
{
ERR("Failed to allocate ringbuffer\n");
@@ -448,7 +477,7 @@ static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self)
static ALCboolean ALCjackPlayback_start(ALCjackPlayback *self)
{
const char **ports;
ALuint i;
ALsizei i;
if(jack_activate(self->Client))
{
@@ -506,30 +535,26 @@ static void ALCjackPlayback_stop(ALCjackPlayback *self)
}
static ALint64 ALCjackPlayback_getLatency(ALCjackPlayback *self)
static ClockLatency ALCjackPlayback_getClockLatency(ALCjackPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
ALint64 latency;
ClockLatency ret;
ALCjackPlayback_lock(self);
latency = ll_ringbuffer_read_space(self->Ring);
ret.ClockTime = GetDeviceClockTime(device);
ret.Latency = ll_ringbuffer_read_space(self->Ring) * DEVICE_CLOCK_RES /
device->Frequency;
ALCjackPlayback_unlock(self);
return latency * 1000000000 / device->Frequency;
return ret;
}
static void ALCjackPlayback_lock(ALCjackPlayback *self)
static void jack_msg_handler(const char *message)
{
almtx_lock(&STATIC_CAST(ALCbackend,self)->mMutex);
WARN("%s\n", message);
}
static void ALCjackPlayback_unlock(ALCjackPlayback *self)
{
almtx_unlock(&STATIC_CAST(ALCbackend,self)->mMutex);
}
typedef struct ALCjackBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCjackBackendFactory;
@@ -537,6 +562,7 @@ typedef struct ALCjackBackendFactory {
static ALCboolean ALCjackBackendFactory_init(ALCjackBackendFactory* UNUSED(self))
{
void (*old_error_cb)(const char*);
jack_client_t *client;
jack_status_t status;
@@ -545,7 +571,11 @@ static ALCboolean ALCjackBackendFactory_init(ALCjackBackendFactory* UNUSED(self)
if(!GetConfigValueBool(NULL, "jack", "spawn-server", 0))
ClientOptions |= JackNoStartServer;
old_error_cb = (&jack_error_callback ? jack_error_callback : NULL);
jack_set_error_function(jack_msg_handler);
client = jack_client_open("alsoft", ClientOptions, &status, NULL);
jack_set_error_function(old_error_cb);
if(client == NULL)
{
WARN("jack_client_open() failed, 0x%02x\n", status);
+2 -2
View File
@@ -41,7 +41,7 @@ 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, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCloopback, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCloopback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCloopback)
@@ -59,7 +59,7 @@ static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
al_string_copy_cstr(&device->DeviceName, name);
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
+315 -102
View File
@@ -25,6 +25,7 @@
#include <stdio.h>
#include <memory.h>
#include <wtypes.h>
#include <mmdeviceapi.h>
#include <audioclient.h>
#include <cguid.h>
@@ -43,6 +44,7 @@
#include "threads.h"
#include "compat.h"
#include "alstring.h"
#include "converter.h"
#include "backends/base.h"
@@ -52,6 +54,7 @@ DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0
DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0);
DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23,0xe0, 0xc0,0xff,0xee,0x7f,0x0e, 4 );
#define MONO SPEAKER_FRONT_CENTER
#define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
@@ -62,11 +65,14 @@ DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x
#define X7DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
#define X7DOT1_WIDE (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_FRONT_LEFT_OF_CENTER|SPEAKER_FRONT_RIGHT_OF_CENTER)
#define REFTIME_PER_SEC ((REFERENCE_TIME)10000000)
#define DEVNAME_HEAD "OpenAL Soft on "
typedef struct {
al_string name;
al_string endpoint_guid; // obtained from PKEY_AudioEndpoint_GUID , set to "Unknown device GUID" if absent.
WCHAR *devid;
} DevMap;
TYPEDEF_VECTOR(DevMap, vector_DevMap)
@@ -75,11 +81,12 @@ static void clear_devlist(vector_DevMap *list)
{
#define CLEAR_DEVMAP(i) do { \
AL_STRING_DEINIT((i)->name); \
AL_STRING_DEINIT((i)->endpoint_guid); \
free((i)->devid); \
(i)->devid = NULL; \
} while(0)
VECTOR_FOR_EACH(DevMap, *list, CLEAR_DEVMAP);
VECTOR_RESIZE(*list, 0);
VECTOR_RESIZE(*list, 0, 0);
#undef CLEAR_DEVMAP
}
@@ -104,6 +111,15 @@ typedef struct {
#define WM_USER_Enumerate (WM_USER+5)
#define WM_USER_Last (WM_USER+5)
static const char MessageStr[WM_USER_Last+1-WM_USER][20] = {
"Open Device",
"Reset Device",
"Start Device",
"Stop Device",
"Close Device",
"Enumerate Devices",
};
static inline void ReturnMsgResponse(ThreadRequest *req, HRESULT res)
{
req->result = res;
@@ -119,19 +135,21 @@ static HRESULT WaitForResponse(ThreadRequest *req)
}
static void get_device_name(IMMDevice *device, al_string *name)
static void get_device_name_and_guid(IMMDevice *device, al_string *name, al_string *guid)
{
IPropertyStore *ps;
PROPVARIANT pvname;
PROPVARIANT pvguid;
HRESULT hr;
al_string_copy_cstr(name, DEVNAME_HEAD);
alstr_copy_cstr(name, DEVNAME_HEAD);
hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
if(FAILED(hr))
{
WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
al_string_append_cstr(name, "Unknown Device Name");
alstr_append_cstr(name, "Unknown Device Name");
if(guid!=NULL)alstr_copy_cstr(guid, "Unknown Device GUID");
return;
}
@@ -141,17 +159,37 @@ static void get_device_name(IMMDevice *device, al_string *name)
if(FAILED(hr))
{
WARN("GetValue Device_FriendlyName failed: 0x%08lx\n", hr);
al_string_append_cstr(name, "Unknown Device Name");
alstr_append_cstr(name, "Unknown Device Name");
}
else if(pvname.vt == VT_LPWSTR)
al_string_append_wcstr(name, pvname.pwszVal);
alstr_append_wcstr(name, pvname.pwszVal);
else
{
WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvname.vt);
al_string_append_cstr(name, "Unknown Device Name");
alstr_append_cstr(name, "Unknown Device Name");
}
PropVariantClear(&pvname);
if(guid!=NULL){
PropVariantInit(&pvguid);
hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&PKEY_AudioEndpoint_GUID, &pvguid);
if(FAILED(hr))
{
WARN("GetValue AudioEndpoint_GUID failed: 0x%08lx\n", hr);
alstr_copy_cstr(guid, "Unknown Device GUID");
}
else if(pvguid.vt == VT_LPWSTR)
alstr_copy_wcstr(guid, pvguid.pwszVal);
else
{
WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvguid.vt);
alstr_copy_cstr(guid, "Unknown Device GUID");
}
PropVariantClear(&pvguid);
}
PropVariantClear(&pvname);
IPropertyStore_Release(ps);
}
@@ -185,7 +223,7 @@ static void get_device_formfactor(IMMDevice *device, EndpointFormFactor *formfac
}
static void add_device(IMMDevice *device, LPCWSTR devid, vector_DevMap *list)
static void add_device(IMMDevice *device, const WCHAR *devid, vector_DevMap *list)
{
int count = 0;
al_string tmpname;
@@ -193,38 +231,39 @@ static void add_device(IMMDevice *device, LPCWSTR devid, vector_DevMap *list)
AL_STRING_INIT(tmpname);
AL_STRING_INIT(entry.name);
AL_STRING_INIT(entry.endpoint_guid);
entry.devid = strdupW(devid);
get_device_name(device, &tmpname);
get_device_name_and_guid(device, &tmpname, &entry.endpoint_guid);
while(1)
{
const DevMap *iter;
al_string_copy(&entry.name, tmpname);
alstr_copy(&entry.name, tmpname);
if(count != 0)
{
char str[64];
snprintf(str, sizeof(str), " #%d", count+1);
al_string_append_cstr(&entry.name, str);
alstr_append_cstr(&entry.name, str);
}
#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
#define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0)
VECTOR_FIND_IF(iter, const DevMap, *list, MATCH_ENTRY);
if(iter == VECTOR_ITER_END(*list)) break;
if(iter == VECTOR_END(*list)) break;
#undef MATCH_ENTRY
count++;
}
TRACE("Got device \"%s\", \"%ls\"\n", al_string_get_cstr(entry.name), entry.devid);
TRACE("Got device \"%s\", \"%s\", \"%ls\"\n", alstr_get_cstr(entry.name), alstr_get_cstr(entry.endpoint_guid), entry.devid);
VECTOR_PUSH_BACK(*list, entry);
AL_STRING_DEINIT(tmpname);
}
static LPWSTR get_device_id(IMMDevice *device)
static WCHAR *get_device_id(IMMDevice *device)
{
LPWSTR devid;
WCHAR *devid;
HRESULT hr;
hr = IMMDevice_GetId(device, &devid);
@@ -241,7 +280,7 @@ static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ve
{
IMMDeviceCollection *coll;
IMMDevice *defdev = NULL;
LPWSTR defdevid = NULL;
WCHAR *defdevid = NULL;
HRESULT hr;
UINT count;
UINT i;
@@ -258,11 +297,7 @@ static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ve
if(SUCCEEDED(hr) && count > 0)
{
clear_devlist(list);
if(!VECTOR_RESERVE(*list, count))
{
IMMDeviceCollection_Release(coll);
return E_OUTOFMEMORY;
}
VECTOR_RESIZE(*list, 0, count);
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, flowdir,
eMultimedia, &defdev);
@@ -277,7 +312,7 @@ static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ve
for(i = 0;i < count;++i)
{
IMMDevice *device;
LPWSTR devid;
WCHAR *devid;
hr = IMMDeviceCollection_Item(coll, i, &device);
if(FAILED(hr)) continue;
@@ -379,7 +414,11 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
TRACE("Starting message loop\n");
while(GetMessage(&msg, NULL, WM_USER_First, WM_USER_Last))
{
TRACE("Got message %u (lparam=%p, wparam=%p)\n", msg.message, (void*)msg.lParam, (void*)msg.wParam);
TRACE("Got message \"%s\" (0x%04x, lparam=%p, wparam=%p)\n",
(msg.message >= WM_USER && msg.message <= WM_USER_Last) ?
MessageStr[msg.message-WM_USER] : "Unknown",
msg.message, (void*)msg.lParam, (void*)msg.wParam
);
switch(msg.message)
{
case WM_USER_OpenDevice:
@@ -508,7 +547,7 @@ static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self);
static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self);
static DECLARE_FORWARD2(ALCmmdevPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, ALCuint, availableSamples)
static ALint64 ALCmmdevPlayback_getLatency(ALCmmdevPlayback *self);
static ClockLatency ALCmmdevPlayback_getClockLatency(ALCmmdevPlayback *self);
static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCmmdevPlayback)
@@ -606,10 +645,10 @@ FORCE_ALIGN static int ALCmmdevPlayback_mixerProc(void *arg)
hr = IAudioRenderClient_GetBuffer(self->render, len, &buffer);
if(SUCCEEDED(hr))
{
V0(device->Backend,lock)();
ALCmmdevPlayback_lock(self);
aluMixData(device, buffer, len);
self->Padding = written + len;
V0(device->Backend,unlock)();
ALCmmdevPlayback_unlock(self);
hr = IAudioRenderClient_ReleaseBuffer(self->render, len, 0);
}
if(FAILED(hr))
@@ -667,13 +706,12 @@ static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *
return ALC_TRUE;
}
static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *deviceName)
{
HRESULT hr = S_OK;
self->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
self->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
self->NotifyEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
self->MsgEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
if(self->NotifyEvent == NULL || self->MsgEvent == NULL)
{
ERR("Failed to create message events: %lu\n", GetLastError());
@@ -694,18 +732,32 @@ static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *devi
}
hr = E_FAIL;
#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0)
#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0 || \
alstr_cmp_cstr((i)->endpoint_guid, deviceName) == 0)
VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
if(iter == VECTOR_ITER_END(PlaybackDevices))
#undef MATCH_NAME
if(iter == VECTOR_END(PlaybackDevices))
{
int len;
if((len=MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, NULL, 0)) > 0)
{
WCHAR *wname = calloc(sizeof(WCHAR), len);
MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, wname, len);
#define MATCH_NAME(i) (wcscmp((i)->devid, wname) == 0)
VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
#undef MATCH_NAME
free(wname);
}
}
if(iter == VECTOR_END(PlaybackDevices))
WARN("Failed to find device name matching \"%s\"\n", deviceName);
else
{
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
self->devid = strdupW(iter->devid);
al_string_copy(&device->DeviceName, iter->name);
alstr_copy(&device->DeviceName, iter->name);
hr = S_OK;
}
#undef MATCH_NAME
}
}
@@ -761,8 +813,8 @@ static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self)
if(SUCCEEDED(hr))
{
self->client = ptr;
if(al_string_empty(device->DeviceName))
get_device_name(self->mmdev, &device->DeviceName);
if(alstr_empty(device->DeviceName))
get_device_name_and_guid(self->mmdev, &device->DeviceName, NULL);
}
if(FAILED(hr))
@@ -854,8 +906,8 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
CoTaskMemFree(wfx);
wfx = NULL;
buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
device->Frequency-1) / device->Frequency;
buf_time = ScaleCeil(device->UpdateSize*device->NumUpdates, REFTIME_PER_SEC,
device->Frequency);
if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
device->Frequency = OutputType.Format.nSamplesPerSec;
@@ -885,7 +937,7 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
OutputType.Format.nChannels = 1;
OutputType.dwChannelMask = MONO;
break;
case DevFmtBFormat3D:
case DevFmtAmbi3D:
device->FmtChans = DevFmtStereo;
/*fall-through*/
case DevFmtStereo:
@@ -1026,7 +1078,9 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
}
get_device_formfactor(self->mmdev, &formfactor);
device->IsHeadphones = (device->FmtChans == DevFmtStereo && formfactor == Headphones);
device->IsHeadphones = (device->FmtChans == DevFmtStereo &&
(formfactor == Headphones || formfactor == Headset)
);
SetDefaultWFXChannelOrder(device);
@@ -1042,7 +1096,7 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
hr = IAudioClient_GetDevicePeriod(self->client, &min_per, NULL);
if(SUCCEEDED(hr))
{
min_len = (UINT32)((min_per*device->Frequency + 10000000-1) / 10000000);
min_len = (UINT32)ScaleCeil(min_per, device->Frequency, REFTIME_PER_SEC);
/* Find the nearest multiple of the period size to the update size */
if(min_len < device->UpdateSize)
min_len *= (device->UpdateSize + min_len/2)/min_len;
@@ -1139,10 +1193,17 @@ static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self)
}
static ALint64 ALCmmdevPlayback_getLatency(ALCmmdevPlayback *self)
static ClockLatency ALCmmdevPlayback_getClockLatency(ALCmmdevPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
return (ALint64)self->Padding * 1000000000 / device->Frequency;
ClockLatency ret;
ALCmmdevPlayback_lock(self);
ret.ClockTime = GetDeviceClockTime(device);
ret.Latency = self->Padding * DEVICE_CLOCK_RES / device->Frequency;
ALCmmdevPlayback_unlock(self);
return ret;
}
@@ -1159,6 +1220,8 @@ typedef struct ALCmmdevCapture {
HANDLE MsgEvent;
ChannelConverter *ChannelConv;
SampleConverter *SampleConv;
ll_ringbuffer_t *Ring;
volatile int killNow;
@@ -1181,7 +1244,7 @@ static void ALCmmdevCapture_stop(ALCmmdevCapture *self);
static void ALCmmdevCapture_stopProxy(ALCmmdevCapture *self);
static ALCenum ALCmmdevCapture_captureSamples(ALCmmdevCapture *self, ALCvoid *buffer, ALCuint samples);
static ALuint ALCmmdevCapture_availableSamples(ALCmmdevCapture *self);
static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, ALint64, getLatency)
static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCmmdevCapture)
@@ -1206,6 +1269,8 @@ static void ALCmmdevCapture_Construct(ALCmmdevCapture *self, ALCdevice *device)
self->MsgEvent = NULL;
self->ChannelConv = NULL;
self->SampleConv = NULL;
self->Ring = NULL;
self->killNow = 0;
@@ -1216,6 +1281,9 @@ static void ALCmmdevCapture_Destruct(ALCmmdevCapture *self)
ll_ringbuffer_free(self->Ring);
self->Ring = NULL;
DestroySampleConverter(&self->SampleConv);
DestroyChannelConverter(&self->ChannelConv);
if(self->NotifyEvent != NULL)
CloseHandle(self->NotifyEvent);
self->NotifyEvent = NULL;
@@ -1235,6 +1303,8 @@ FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg)
{
ALCmmdevCapture *self = arg;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
ALfloat *samples = NULL;
size_t samplesmax = 0;
HRESULT hr;
hr = CoInitialize(NULL);
@@ -1257,33 +1327,75 @@ FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg)
hr = IAudioCaptureClient_GetNextPacketSize(self->capture, &avail);
if(FAILED(hr))
ERR("Failed to get next packet size: 0x%08lx\n", hr);
else while(avail > 0 && SUCCEEDED(hr))
else if(avail > 0)
{
UINT32 numsamples;
DWORD flags;
BYTE *data;
BYTE *rdata;
hr = IAudioCaptureClient_GetBuffer(self->capture,
&data, &numsamples, &flags, NULL, NULL
&rdata, &numsamples, &flags, NULL, NULL
);
if(FAILED(hr))
{
ERR("Failed to get capture buffer: 0x%08lx\n", hr);
break;
}
ll_ringbuffer_write(self->Ring, (char*)data, numsamples);
hr = IAudioCaptureClient_ReleaseBuffer(self->capture, numsamples);
if(FAILED(hr))
else
{
ERR("Failed to release capture buffer: 0x%08lx\n", hr);
break;
}
ll_ringbuffer_data_t data[2];
size_t dstframes = 0;
hr = IAudioCaptureClient_GetNextPacketSize(self->capture, &avail);
if(FAILED(hr))
ERR("Failed to get next packet size: 0x%08lx\n", hr);
if(self->ChannelConv)
{
if(samplesmax < numsamples)
{
size_t newmax = RoundUp(numsamples, 4096);
ALfloat *tmp = al_calloc(DEF_ALIGN, newmax*2*sizeof(ALfloat));
al_free(samples);
samples = tmp;
samplesmax = newmax;
}
ChannelConverterInput(self->ChannelConv, rdata, samples, numsamples);
rdata = (BYTE*)samples;
}
ll_ringbuffer_get_write_vector(self->Ring, data);
if(self->SampleConv)
{
const ALvoid *srcdata = rdata;
ALsizei srcframes = numsamples;
dstframes = SampleConverterInput(self->SampleConv,
&srcdata, &srcframes, data[0].buf, data[0].len
);
if(srcframes > 0 && dstframes == data[0].len && data[1].len > 0)
{
/* If some source samples remain, all of the first dest
* block was filled, and there's space in the second
* dest block, do another run for the second block.
*/
dstframes += SampleConverterInput(self->SampleConv,
&srcdata, &srcframes, data[1].buf, data[1].len
);
}
}
else
{
size_t framesize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType,
device->AmbiOrder);
ALuint len1 = minu(data[0].len, numsamples);
ALuint len2 = minu(data[1].len, numsamples-len1);
memcpy(data[0].buf, rdata, len1*framesize);
if(len2 > 0)
memcpy(data[1].buf, rdata+len1*framesize, len2*framesize);
dstframes = len1 + len2;
}
ll_ringbuffer_write_advance(self->Ring, dstframes);
hr = IAudioCaptureClient_ReleaseBuffer(self->capture, numsamples);
if(FAILED(hr)) ERR("Failed to release capture buffer: 0x%08lx\n", hr);
}
}
if(FAILED(hr))
@@ -1299,6 +1411,10 @@ FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg)
ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
}
al_free(samples);
samples = NULL;
samplesmax = 0;
CoUninitialize();
return 0;
}
@@ -1308,8 +1424,8 @@ static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *device
{
HRESULT hr = S_OK;
self->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
self->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
self->NotifyEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
self->MsgEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
if(self->NotifyEvent == NULL || self->MsgEvent == NULL)
{
ERR("Failed to create message events: %lu\n", GetLastError());
@@ -1330,18 +1446,32 @@ static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *device
}
hr = E_FAIL;
#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0)
#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0 || \
alstr_cmp_cstr((i)->endpoint_guid, deviceName) == 0)
VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
if(iter == VECTOR_ITER_END(CaptureDevices))
#undef MATCH_NAME
if(iter == VECTOR_END(CaptureDevices))
{
int len;
if((len=MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, NULL, 0)) > 0)
{
WCHAR *wname = calloc(sizeof(WCHAR), len);
MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, wname, len);
#define MATCH_NAME(i) (wcscmp((i)->devid, wname) == 0)
VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
#undef MATCH_NAME
free(wname);
}
}
if(iter == VECTOR_END(CaptureDevices))
WARN("Failed to find device name matching \"%s\"\n", deviceName);
else
{
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
self->devid = strdupW(iter->devid);
al_string_copy(&device->DeviceName, iter->name);
alstr_copy(&device->DeviceName, iter->name);
hr = S_OK;
}
#undef MATCH_NAME
}
}
@@ -1415,8 +1545,8 @@ static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self)
if(SUCCEEDED(hr))
{
self->client = ptr;
if(al_string_empty(device->DeviceName))
get_device_name(self->mmdev, &device->DeviceName);
if(alstr_empty(device->DeviceName))
get_device_name_and_guid(self->mmdev, &device->DeviceName, NULL);
}
if(FAILED(hr))
@@ -1467,6 +1597,7 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
WAVEFORMATEXTENSIBLE OutputType;
WAVEFORMATEX *wfx = NULL;
enum DevFmtType srcType;
REFERENCE_TIME buf_time;
UINT32 buffer_len;
void *ptr = NULL;
@@ -1484,8 +1615,12 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
}
self->client = ptr;
buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
device->Frequency-1) / device->Frequency;
buf_time = ScaleCeil(device->UpdateSize*device->NumUpdates, REFTIME_PER_SEC,
device->Frequency);
// Make sure buffer is at least 100ms in size
buf_time = maxu64(buf_time, REFTIME_PER_SEC/10);
device->UpdateSize = (ALuint)ScaleCeil(buf_time, device->Frequency, REFTIME_PER_SEC) /
device->NumUpdates;
OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
switch(device->FmtChans)
@@ -1519,38 +1654,33 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
OutputType.dwChannelMask = X7DOT1;
break;
case DevFmtBFormat3D:
case DevFmtAmbi3D:
return E_FAIL;
}
switch(device->FmtType)
{
/* NOTE: Signedness doesn't matter, the converter will handle it. */
case DevFmtByte:
case DevFmtUByte:
OutputType.Format.wBitsPerSample = 8;
OutputType.Samples.wValidBitsPerSample = 8;
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
break;
case DevFmtShort:
case DevFmtUShort:
OutputType.Format.wBitsPerSample = 16;
OutputType.Samples.wValidBitsPerSample = 16;
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
break;
case DevFmtInt:
case DevFmtUInt:
OutputType.Format.wBitsPerSample = 32;
OutputType.Samples.wValidBitsPerSample = 32;
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
break;
case DevFmtFloat:
OutputType.Format.wBitsPerSample = 32;
OutputType.Samples.wValidBitsPerSample = 32;
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
break;
case DevFmtByte:
case DevFmtUShort:
case DevFmtUInt:
WARN("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
return E_FAIL;
}
OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
OutputType.Format.nSamplesPerSec = device->Frequency;
OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
@@ -1568,26 +1698,107 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
return hr;
}
/* FIXME: We should do conversion/resampling if we didn't get a matching format. */
if(wfx->nSamplesPerSec != OutputType.Format.nSamplesPerSec ||
wfx->wBitsPerSample != OutputType.Format.wBitsPerSample ||
wfx->nChannels != OutputType.Format.nChannels ||
wfx->nBlockAlign != OutputType.Format.nBlockAlign)
DestroySampleConverter(&self->SampleConv);
DestroyChannelConverter(&self->ChannelConv);
if(wfx != NULL)
{
ERR("Did not get matching format, wanted: %s %s %uhz, got: %d channel(s) %d-bit %luhz\n",
DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType), device->Frequency,
wfx->nChannels, wfx->wBitsPerSample, wfx->nSamplesPerSec);
if(!(wfx->nChannels == OutputType.Format.nChannels ||
(wfx->nChannels == 1 && OutputType.Format.nChannels == 2) ||
(wfx->nChannels == 2 && OutputType.Format.nChannels == 1)))
{
ERR("Failed to get matching format, wanted: %s %s %uhz, got: %d channel%s %d-bit %luhz\n",
DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
device->Frequency, wfx->nChannels, (wfx->nChannels==1)?"":"s", wfx->wBitsPerSample,
wfx->nSamplesPerSec);
CoTaskMemFree(wfx);
return E_FAIL;
}
if(!MakeExtensible(&OutputType, wfx))
{
CoTaskMemFree(wfx);
return E_FAIL;
}
CoTaskMemFree(wfx);
wfx = NULL;
}
if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
{
if(OutputType.Format.wBitsPerSample == 8)
srcType = DevFmtUByte;
else if(OutputType.Format.wBitsPerSample == 16)
srcType = DevFmtShort;
else if(OutputType.Format.wBitsPerSample == 32)
srcType = DevFmtInt;
else
{
ERR("Unhandled integer bit depth: %d\n", OutputType.Format.wBitsPerSample);
return E_FAIL;
}
}
else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
{
if(OutputType.Format.wBitsPerSample == 32)
srcType = DevFmtFloat;
else
{
ERR("Unhandled float bit depth: %d\n", OutputType.Format.wBitsPerSample);
return E_FAIL;
}
}
else
{
ERR("Unhandled format sub-type\n");
return E_FAIL;
}
if(!MakeExtensible(&OutputType, wfx))
if(device->FmtChans == DevFmtMono && OutputType.Format.nChannels == 2)
{
CoTaskMemFree(wfx);
return E_FAIL;
self->ChannelConv = CreateChannelConverter(srcType, DevFmtStereo,
device->FmtChans);
if(!self->ChannelConv)
{
ERR("Failed to create %s stereo-to-mono converter\n", DevFmtTypeString(srcType));
return E_FAIL;
}
TRACE("Created %s stereo-to-mono converter\n", DevFmtTypeString(srcType));
/* The channel converter always outputs float, so change the input type
* for the resampler/type-converter.
*/
srcType = DevFmtFloat;
}
else if(device->FmtChans == DevFmtStereo && OutputType.Format.nChannels == 1)
{
self->ChannelConv = CreateChannelConverter(srcType, DevFmtMono,
device->FmtChans);
if(!self->ChannelConv)
{
ERR("Failed to create %s mono-to-stereo converter\n", DevFmtTypeString(srcType));
return E_FAIL;
}
TRACE("Created %s mono-to-stereo converter\n", DevFmtTypeString(srcType));
srcType = DevFmtFloat;
}
if(device->Frequency != OutputType.Format.nSamplesPerSec || device->FmtType != srcType)
{
self->SampleConv = CreateSampleConverter(
srcType, device->FmtType, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder),
OutputType.Format.nSamplesPerSec, device->Frequency
);
if(!self->SampleConv)
{
ERR("Failed to create converter for %s format, dst: %s %uhz, src: %s %luhz\n",
DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
device->Frequency, DevFmtTypeString(srcType), OutputType.Format.nSamplesPerSec);
return E_FAIL;
}
TRACE("Created converter for %s format, dst: %s %uhz, src: %s %luhz\n",
DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
device->Frequency, DevFmtTypeString(srcType), OutputType.Format.nSamplesPerSec);
}
CoTaskMemFree(wfx);
wfx = NULL;
hr = IAudioClient_Initialize(self->client,
AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
@@ -1608,7 +1819,9 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
buffer_len = maxu(device->UpdateSize*device->NumUpdates + 1, buffer_len);
ll_ringbuffer_free(self->Ring);
self->Ring = ll_ringbuffer_create(buffer_len, OutputType.Format.nBlockAlign);
self->Ring = ll_ringbuffer_create(buffer_len,
FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder)
);
if(!self->Ring)
{
ERR("Failed to allocate capture ring buffer\n");
@@ -1713,9 +1926,9 @@ ALCenum ALCmmdevCapture_captureSamples(ALCmmdevCapture *self, ALCvoid *buffer, A
static inline void AppendAllDevicesList2(const DevMap *entry)
{ AppendAllDevicesList(al_string_get_cstr(entry->name)); }
{ AppendAllDevicesList(alstr_get_cstr(entry->name)); }
static inline void AppendCaptureDeviceList2(const DevMap *entry)
{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); }
{ AppendCaptureDeviceList(alstr_get_cstr(entry->name)); }
typedef struct ALCmmdevBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
@@ -1739,7 +1952,7 @@ static BOOL MMDevApiLoad(void)
ThreadRequest req;
InitResult = E_FAIL;
req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
req.FinishedEvt = CreateEventW(NULL, FALSE, FALSE, NULL);
if(req.FinishedEvt == NULL)
ERR("Failed to create event: %lu\n", GetLastError());
else
@@ -1787,7 +2000,7 @@ static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory* UN
* stereo input, for example, and the app asks for 22050hz mono,
* initialization will fail.
*/
if(type == ALCbackend_Playback /*|| type == ALCbackend_Capture*/)
if(type == ALCbackend_Playback || type == ALCbackend_Capture)
return ALC_TRUE;
return ALC_FALSE;
}
@@ -1796,7 +2009,7 @@ static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory* UNUSED(self), e
{
ThreadRequest req = { NULL, 0 };
req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
req.FinishedEvt = CreateEventW(NULL, FALSE, FALSE, NULL);
if(req.FinishedEvt == NULL)
ERR("Failed to create event: %lu\n", GetLastError());
else
+4 -2
View File
@@ -51,7 +51,7 @@ 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, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCnullBackend)
@@ -109,7 +109,9 @@ static int ALCnullBackend_mixerProc(void *ptr)
al_nssleep(restTime);
else while(avail-done >= device->UpdateSize)
{
ALCnullBackend_lock(self);
aluMixData(device, NULL, device->UpdateSize);
ALCnullBackend_unlock(self);
done += device->UpdateSize;
}
}
@@ -128,7 +130,7 @@ static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name)
return ALC_INVALID_VALUE;
device = STATIC_CAST(ALCbackend, self)->mDevice;
al_string_copy_cstr(&device->DeviceName, name);
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
+877 -175
View File
File diff suppressed because it is too large Load Diff
+171 -123
View File
@@ -88,7 +88,9 @@ static struct oss_device oss_capture = {
#ifdef ALC_OSS_COMPAT
static void ALCossListPopulate(struct oss_device *UNUSED(playback), struct oss_device *UNUSED(capture))
#define DSP_CAP_OUTPUT 0x00020000
#define DSP_CAP_INPUT 0x00010000
static void ALCossListPopulate(struct oss_device *UNUSED(devlist), int UNUSED(type_flag))
{
}
@@ -153,7 +155,7 @@ static void ALCossListAppend(struct oss_device *list, const char *handle, size_t
TRACE("Got device \"%s\", \"%s\"\n", next->handle, next->path);
}
static void ALCossListPopulate(struct oss_device *playback, struct oss_device *capture)
static void ALCossListPopulate(struct oss_device *devlist, int type_flag)
{
struct oss_sysinfo si;
struct oss_audioinfo ai;
@@ -161,12 +163,12 @@ static void ALCossListPopulate(struct oss_device *playback, struct oss_device *c
if((fd=open("/dev/mixer", O_RDONLY)) < 0)
{
ERR("Could not open /dev/mixer\n");
TRACE("Could not open /dev/mixer: %s\n", strerror(errno));
return;
}
if(ioctl(fd, SNDCTL_SYSINFO, &si) == -1)
{
ERR("SNDCTL_SYSINFO failed: %s\n", strerror(errno));
TRACE("SNDCTL_SYSINFO failed: %s\n", strerror(errno));
goto done;
}
for(i = 0;i < si.numaudios;i++)
@@ -193,10 +195,9 @@ static void ALCossListPopulate(struct oss_device *playback, struct oss_device *c
len = strnlen(ai.name, sizeof(ai.name));
handle = ai.name;
}
if((ai.caps&DSP_CAP_INPUT) && capture != NULL)
ALCossListAppend(capture, handle, len, ai.devnode, strnlen(ai.devnode, sizeof(ai.devnode)));
if((ai.caps&DSP_CAP_OUTPUT) && playback != NULL)
ALCossListAppend(playback, handle, len, ai.devnode, strnlen(ai.devnode, sizeof(ai.devnode)));
if((ai.caps&type_flag))
ALCossListAppend(devlist, handle, len, ai.devnode,
strnlen(ai.devnode, sizeof(ai.devnode)));
}
done:
@@ -242,7 +243,7 @@ typedef struct ALCplaybackOSS {
ALubyte *mix_data;
int data_size;
volatile int killNow;
ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCplaybackOSS;
@@ -257,7 +258,7 @@ 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, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCplaybackOSS)
@@ -268,42 +269,64 @@ static int ALCplaybackOSS_mixerProc(void *ptr)
{
ALCplaybackOSS *self = (ALCplaybackOSS*)ptr;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
ALint frameSize;
struct timeval timeout;
ALubyte *write_ptr;
ALint frame_size;
ALint to_write;
ssize_t wrote;
fd_set wfds;
int sret;
SetRTPriority();
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
while(!self->killNow && device->Connected)
ALCplaybackOSS_lock(self);
while(!ATOMIC_LOAD_SEQ(&self->killNow) && device->Connected)
{
ALint len = self->data_size;
ALubyte *WritePtr = self->mix_data;
FD_ZERO(&wfds);
FD_SET(self->fd, &wfds);
timeout.tv_sec = 1;
timeout.tv_usec = 0;
aluMixData(device, WritePtr, len/frameSize);
while(len > 0 && !self->killNow)
ALCplaybackOSS_unlock(self);
sret = select(self->fd+1, NULL, &wfds, NULL, &timeout);
ALCplaybackOSS_lock(self);
if(sret < 0)
{
wrote = write(self->fd, WritePtr, len);
if(errno == EINTR)
continue;
ERR("select failed: %s\n", strerror(errno));
aluHandleDisconnect(device);
break;
}
else if(sret == 0)
{
WARN("select timeout\n");
continue;
}
write_ptr = self->mix_data;
to_write = self->data_size;
aluMixData(device, write_ptr, to_write/frame_size);
while(to_write > 0 && !ATOMIC_LOAD_SEQ(&self->killNow))
{
wrote = write(self->fd, write_ptr, to_write);
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(1000000);
continue;
if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
continue;
ERR("write failed: %s\n", strerror(errno));
aluHandleDisconnect(device);
break;
}
len -= wrote;
WritePtr += wrote;
to_write -= wrote;
write_ptr += wrote;
}
}
ALCplaybackOSS_unlock(self);
return 0;
}
@@ -313,6 +336,8 @@ static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCplaybackOSS, ALCbackend, self);
ATOMIC_INIT(&self->killNow, AL_FALSE);
}
static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name)
@@ -320,22 +345,28 @@ static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name)
struct oss_device *dev = &oss_playback;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
if(!name)
if(!name || strcmp(name, dev->handle) == 0)
name = dev->handle;
else
{
while (dev != NULL)
if(!dev->next)
{
ALCossListPopulate(&oss_playback, DSP_CAP_OUTPUT);
dev = &oss_playback;
}
while(dev != NULL)
{
if (strcmp(dev->handle, name) == 0)
break;
dev = dev->next;
}
if (dev == NULL)
if(dev == NULL)
{
WARN("Could not find \"%s\" in device list\n", name);
return ALC_INVALID_VALUE;
}
}
self->killNow = 0;
self->fd = open(dev->path, O_WRONLY);
if(self->fd == -1)
{
@@ -343,7 +374,7 @@ static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name)
return ALC_INVALID_VALUE;
}
al_string_copy_cstr(&device->DeviceName, name);
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
@@ -387,18 +418,11 @@ static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
}
periods = device->NumUpdates;
numChannels = ChannelsFromDevFmt(device->FmtChans);
frameSize = numChannels * BytesFromDevFmt(device->FmtType);
numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
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--;
frameSize = numChannels * BytesFromDevFmt(device->FmtType);
/* According to the OSS spec, 16 bytes (log2(16)) is the minimum. */
log2FragmentSize = maxi(log2i(device->UpdateSize*frameSize), 4);
numFragmentsLogSize = (periods << 16) | log2FragmentSize;
#define CHECKERR(func) if((func) < 0) { \
@@ -420,7 +444,7 @@ static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
}
#undef CHECKERR
if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels)
if((int)ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != numChannels)
{
ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
return ALC_FALSE;
@@ -436,7 +460,7 @@ static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
device->Frequency = ossSpeed;
device->UpdateSize = info.fragsize / frameSize;
device->NumUpdates = info.fragments + 1;
device->NumUpdates = info.fragments;
SetDefaultChannelOrder(device);
@@ -447,10 +471,12 @@ static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
self->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
self->data_size = device->UpdateSize * FrameSizeFromDevFmt(
device->FmtChans, device->FmtType, device->AmbiOrder
);
self->mix_data = calloc(1, self->data_size);
self->killNow = 0;
ATOMIC_STORE_SEQ(&self->killNow, AL_FALSE);
if(althrd_create(&self->thread, ALCplaybackOSS_mixerProc, self) != althrd_success)
{
free(self->mix_data);
@@ -465,10 +491,8 @@ static void ALCplaybackOSS_stop(ALCplaybackOSS *self)
{
int res;
if(self->killNow)
if(ATOMIC_EXCHANGE_SEQ(&self->killNow, AL_TRUE))
return;
self->killNow = 1;
althrd_join(self->thread, &res);
if(ioctl(self->fd, SNDCTL_DSP_RESET) != 0)
@@ -484,13 +508,9 @@ typedef struct ALCcaptureOSS {
int fd;
ALubyte *read_data;
int data_size;
ll_ringbuffer_t *ring;
RingBuffer *ring;
int doCapture;
volatile int killNow;
ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCcaptureOSS;
@@ -505,7 +525,7 @@ 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, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCcaptureOSS)
@@ -516,32 +536,55 @@ static int ALCcaptureOSS_recordProc(void *ptr)
{
ALCcaptureOSS *self = (ALCcaptureOSS*)ptr;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
int frameSize;
int amt;
struct timeval timeout;
int frame_size;
fd_set rfds;
ssize_t amt;
int sret;
SetRTPriority();
althrd_setname(althrd_current(), RECORD_THREAD_NAME);
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
while(!self->killNow)
while(!ATOMIC_LOAD_SEQ(&self->killNow))
{
amt = read(self->fd, self->read_data, self->data_size);
if(amt < 0)
ll_ringbuffer_data_t vec[2];
FD_ZERO(&rfds);
FD_SET(self->fd, &rfds);
timeout.tv_sec = 1;
timeout.tv_usec = 0;
sret = select(self->fd+1, &rfds, NULL, NULL, &timeout);
if(sret < 0)
{
ERR("read failed: %s\n", strerror(errno));
ALCcaptureOSS_lock(self);
if(errno == EINTR)
continue;
ERR("select failed: %s\n", strerror(errno));
aluHandleDisconnect(device);
ALCcaptureOSS_unlock(self);
break;
}
if(amt == 0)
else if(sret == 0)
{
al_nssleep(1000000);
WARN("select timeout\n");
continue;
}
if(self->doCapture)
WriteRingBuffer(self->ring, self->read_data, amt/frameSize);
ll_ringbuffer_get_write_vector(self->ring, vec);
if(vec[0].len > 0)
{
amt = read(self->fd, vec[0].buf, vec[0].len*frame_size);
if(amt < 0)
{
ERR("read failed: %s\n", strerror(errno));
ALCcaptureOSS_lock(self);
aluHandleDisconnect(device);
ALCcaptureOSS_unlock(self);
break;
}
ll_ringbuffer_write_advance(self->ring, amt/frame_size);
}
}
return 0;
@@ -552,6 +595,8 @@ static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCcaptureOSS, ALCbackend, self);
ATOMIC_INIT(&self->killNow, AL_FALSE);
}
static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
@@ -568,18 +613,26 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
int ossSpeed;
char *err;
if(!name)
if(!name || strcmp(name, dev->handle) == 0)
name = dev->handle;
else
{
while (dev != NULL)
if(!dev->next)
{
ALCossListPopulate(&oss_capture, DSP_CAP_INPUT);
dev = &oss_capture;
}
while(dev != NULL)
{
if (strcmp(dev->handle, name) == 0)
break;
dev = dev->next;
}
if (dev == NULL)
if(dev == NULL)
{
WARN("Could not find \"%s\" in device list\n", name);
return ALC_INVALID_VALUE;
}
}
self->fd = open(dev->path, O_RDONLY);
@@ -609,7 +662,7 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
}
periods = 4;
numChannels = ChannelsFromDevFmt(device->FmtChans);
numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
frameSize = numChannels * BytesFromDevFmt(device->FmtType);
ossSpeed = device->Frequency;
log2FragmentSize = log2i(device->UpdateSize * device->NumUpdates *
@@ -639,7 +692,7 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
}
#undef CHECKERR
if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels)
if((int)ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != numChannels)
{
ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
close(self->fd);
@@ -657,7 +710,7 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
return ALC_INVALID_VALUE;
}
self->ring = CreateRingBuffer(frameSize, device->UpdateSize * device->NumUpdates);
self->ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates + 1, frameSize);
if(!self->ring)
{
ERR("Ring buffer create failed\n");
@@ -666,60 +719,50 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
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);
alstr_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);
ll_ringbuffer_free(self->ring);
self->ring = NULL;
free(self->read_data);
self->read_data = NULL;
}
static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self)
{
self->doCapture = 1;
ATOMIC_STORE_SEQ(&self->killNow, AL_FALSE);
if(althrd_create(&self->thread, ALCcaptureOSS_recordProc, self) != althrd_success)
return ALC_FALSE;
return ALC_TRUE;
}
static void ALCcaptureOSS_stop(ALCcaptureOSS *self)
{
self->doCapture = 0;
int res;
if(ATOMIC_EXCHANGE_SEQ(&self->killNow, AL_TRUE))
return;
althrd_join(self->thread, &res);
if(ioctl(self->fd, SNDCTL_DSP_RESET) != 0)
ERR("Error resetting device: %s\n", strerror(errno));
}
static ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples)
{
ReadRingBuffer(self->ring, buffer, samples);
ll_ringbuffer_read(self->ring, buffer, samples);
return ALC_NO_ERROR;
}
static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self)
{
return RingBufferSize(self->ring);
return ll_ringbuffer_read_space(self->ring);
}
@@ -769,33 +812,38 @@ ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory* UNUSED(self),
void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProbe type)
{
struct oss_device *cur;
switch(type)
{
case ALL_DEVICE_PROBE:
{
struct oss_device *cur = &oss_playback;
ALCossListFree(cur);
ALCossListPopulate(cur, NULL);
while (cur != NULL)
ALCossListFree(&oss_playback);
ALCossListPopulate(&oss_playback, DSP_CAP_OUTPUT);
cur = &oss_playback;
while(cur != NULL)
{
AppendAllDevicesList(cur->handle);
#ifdef HAVE_STAT
struct stat buf;
if(stat(cur->path, &buf) == 0)
#endif
AppendAllDevicesList(cur->handle);
cur = cur->next;
}
}
break;
break;
case CAPTURE_DEVICE_PROBE:
{
struct oss_device *cur = &oss_capture;
ALCossListFree(cur);
ALCossListPopulate(NULL, cur);
while (cur != NULL)
ALCossListFree(&oss_capture);
ALCossListPopulate(&oss_capture, DSP_CAP_INPUT);
cur = &oss_capture;
while(cur != NULL)
{
AppendCaptureDeviceList(cur->handle);
#ifdef HAVE_STAT
struct stat buf;
if(stat(cur->path, &buf) == 0)
#endif
AppendCaptureDeviceList(cur->handle);
cur = cur->next;
}
}
break;
break;
}
}
+8 -6
View File
@@ -145,7 +145,7 @@ static ALCboolean ALCportPlayback_start(ALCportPlayback *self);
static void ALCportPlayback_stop(ALCportPlayback *self);
static DECLARE_FORWARD2(ALCportPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
static DECLARE_FORWARD(ALCportPlayback, ALCbackend, ALCuint, availableSamples)
static DECLARE_FORWARD(ALCportPlayback, ALCbackend, ALint64, getLatency)
static DECLARE_FORWARD(ALCportPlayback, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCportPlayback, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCportPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCportPlayback)
@@ -177,7 +177,9 @@ static int ALCportPlayback_WriteCallback(const void *UNUSED(inputBuffer), void *
{
ALCportPlayback *self = userData;
ALCportPlayback_lock(self);
aluMixData(STATIC_CAST(ALCbackend, self)->mDevice, outputBuffer, framesPerBuffer);
ALCportPlayback_unlock(self);
return 0;
}
@@ -243,7 +245,7 @@ retry_open:
return ALC_INVALID_VALUE;
}
al_string_copy_cstr(&device->DeviceName, name);
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
@@ -340,7 +342,7 @@ static ALCboolean ALCportCapture_start(ALCportCapture *self);
static void ALCportCapture_stop(ALCportCapture *self);
static ALCenum ALCportCapture_captureSamples(ALCportCapture *self, ALCvoid *buffer, ALCuint samples);
static ALCuint ALCportCapture_availableSamples(ALCportCapture *self);
static DECLARE_FORWARD(ALCportCapture, ALCbackend, ALint64, getLatency)
static DECLARE_FORWARD(ALCportCapture, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCportCapture, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCportCapture, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCportCapture)
@@ -397,7 +399,7 @@ static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name)
samples = device->UpdateSize * device->NumUpdates;
samples = maxu(samples, 100 * device->Frequency / 1000);
frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
self->ring = ll_ringbuffer_create(samples, frame_size);
if(self->ring == NULL) return ALC_INVALID_VALUE;
@@ -431,7 +433,7 @@ static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name)
ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
return ALC_INVALID_VALUE;
}
self->params.channelCount = ChannelsFromDevFmt(device->FmtChans);
self->params.channelCount = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
err = Pa_OpenStream(&self->stream, &self->params, NULL,
device->Frequency, paFramesPerBufferUnspecified, paNoFlag,
@@ -443,7 +445,7 @@ static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name)
return ALC_INVALID_VALUE;
}
al_string_copy_cstr(&device->DeviceName, name);
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
+206 -91
View File
@@ -182,6 +182,8 @@ static ALCboolean pulse_load(void)
#ifdef HAVE_DYNLOAD
if(!pa_handle)
{
al_string missing_funcs = AL_STRING_INIT_STATIC();
#ifdef _WIN32
#define PALIB "libpulse-0.dll"
#elif defined(__APPLE__) && defined(__MACH__)
@@ -191,12 +193,16 @@ static ALCboolean pulse_load(void)
#endif
pa_handle = LoadLib(PALIB);
if(!pa_handle)
{
WARN("Failed to load %s\n", PALIB);
return ALC_FALSE;
}
#define LOAD_FUNC(x) do { \
p##x = GetSymbol(pa_handle, #x); \
if(!(p##x)) { \
ret = ALC_FALSE; \
alstr_append_cstr(&missing_funcs, "\n" #x); \
} \
} while(0)
LOAD_FUNC(pa_context_unref);
@@ -270,9 +276,11 @@ static ALCboolean pulse_load(void)
if(ret == ALC_FALSE)
{
WARN("Missing expected functions:%s\n", alstr_get_cstr(missing_funcs));
CloseLib(pa_handle);
pa_handle = NULL;
}
alstr_reset(&missing_funcs);
}
#endif /* HAVE_DYNLOAD */
return ret;
@@ -443,7 +451,7 @@ static void clear_devlist(vector_DevMap *list)
#define DEINIT_STRS(i) (AL_STRING_DEINIT((i)->name),AL_STRING_DEINIT((i)->device_name))
VECTOR_FOR_EACH(DevMap, *list, DEINIT_STRS);
#undef DEINIT_STRS
VECTOR_RESIZE(*list, 0);
VECTOR_RESIZE(*list, 0, 0);
}
@@ -489,7 +497,7 @@ static ALCboolean ALCpulsePlayback_start(ALCpulsePlayback *self);
static void ALCpulsePlayback_stop(ALCpulsePlayback *self);
static DECLARE_FORWARD2(ALCpulsePlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
static DECLARE_FORWARD(ALCpulsePlayback, ALCbackend, ALCuint, availableSamples)
static ALint64 ALCpulsePlayback_getLatency(ALCpulsePlayback *self);
static ClockLatency ALCpulsePlayback_getClockLatency(ALCpulsePlayback *self);
static void ALCpulsePlayback_lock(ALCpulsePlayback *self);
static void ALCpulsePlayback_unlock(ALCpulsePlayback *self);
DECLARE_DEFAULT_ALLOCATORS(ALCpulsePlayback)
@@ -525,35 +533,35 @@ static void ALCpulsePlayback_deviceCallback(pa_context *UNUSED(context), const p
return;
}
#define MATCH_INFO_NAME(iter) (al_string_cmp_cstr((iter)->device_name, info->name) == 0)
#define MATCH_INFO_NAME(iter) (alstr_cmp_cstr((iter)->device_name, info->name) == 0)
VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_INFO_NAME);
if(iter != VECTOR_ITER_END(PlaybackDevices)) return;
if(iter != VECTOR_END(PlaybackDevices)) return;
#undef MATCH_INFO_NAME
AL_STRING_INIT(entry.name);
AL_STRING_INIT(entry.device_name);
al_string_copy_cstr(&entry.device_name, info->name);
alstr_copy_cstr(&entry.device_name, info->name);
count = 0;
while(1)
{
al_string_copy_cstr(&entry.name, info->description);
alstr_copy_cstr(&entry.name, info->description);
if(count != 0)
{
char str[64];
snprintf(str, sizeof(str), " #%d", count+1);
al_string_append_cstr(&entry.name, str);
alstr_append_cstr(&entry.name, str);
}
#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
#define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0)
VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_ENTRY);
if(iter == VECTOR_ITER_END(PlaybackDevices)) break;
if(iter == VECTOR_END(PlaybackDevices)) break;
#undef MATCH_ENTRY
count++;
}
TRACE("Got device \"%s\", \"%s\"\n", al_string_get_cstr(entry.name), al_string_get_cstr(entry.device_name));
TRACE("Got device \"%s\", \"%s\"\n", alstr_get_cstr(entry.name), alstr_get_cstr(entry.device_name));
VECTOR_PUSH_BACK(PlaybackDevices, entry);
}
@@ -618,6 +626,11 @@ static void ALCpulsePlayback_bufferAttrCallback(pa_stream *stream, void *pdata)
self->attr = *pa_stream_get_buffer_attr(stream);
TRACE("minreq=%d, tlength=%d, prebuf=%d\n", self->attr.minreq, self->attr.tlength, self->attr.prebuf);
/* FIXME: Update the device's UpdateSize (and/or NumUpdates) using the new
* buffer attributes? Changing UpdateSize will change the ALC_REFRESH
* property, which probably shouldn't change between device resets. But
* leaving it alone means ALC_REFRESH will be off.
*/
}
static void ALCpulsePlayback_contextStateCallback(pa_context *context, void *pdata)
@@ -729,7 +742,7 @@ static void ALCpulsePlayback_sinkNameCallback(pa_context *UNUSED(context), const
return;
}
al_string_copy_cstr(&device->DeviceName, info->description);
alstr_copy_cstr(&device->DeviceName, info->description);
}
@@ -737,9 +750,9 @@ static void ALCpulsePlayback_streamMovedCallback(pa_stream *stream, void *pdata)
{
ALCpulsePlayback *self = pdata;
al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(stream));
alstr_copy_cstr(&self->device_name, pa_stream_get_device_name(stream));
TRACE("Stream moved to %s\n", al_string_get_cstr(self->device_name));
TRACE("Stream moved to %s\n", alstr_get_cstr(self->device_name));
}
@@ -751,6 +764,13 @@ static pa_stream *ALCpulsePlayback_connectStream(const char *device_name,
pa_stream_state_t state;
pa_stream *stream;
if(!device_name)
{
device_name = getenv("ALSOFT_PULSE_DEFAULT");
if(device_name && !device_name[0])
device_name = NULL;
}
stream = pa_stream_new_with_proplist(context, "Playback Stream", spec, chanmap, prop_filter);
if(!stream)
{
@@ -789,7 +809,6 @@ static int ALCpulsePlayback_mixerProc(void *ptr)
ALCpulsePlayback *self = ptr;
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
ALuint buffer_size;
ALint update_size;
size_t frame_size;
ssize_t len;
@@ -798,18 +817,31 @@ static int ALCpulsePlayback_mixerProc(void *ptr)
pa_threaded_mainloop_lock(self->loop);
frame_size = pa_frame_size(&self->spec);
update_size = device->UpdateSize * frame_size;
/* Sanitize buffer metrics, in case we actually have less than what we
* asked for. */
buffer_size = minu(update_size*device->NumUpdates, self->attr.tlength);
update_size = minu(update_size, buffer_size/2);
do {
len = pa_stream_writable_size(self->stream) - self->attr.tlength +
buffer_size;
if(len < update_size)
while(!self->killNow && device->Connected)
{
len = pa_stream_writable_size(self->stream);
if(len < 0)
{
if(pa_stream_is_corked(self->stream) == 1)
ERR("Failed to get writable size: %ld", (long)len);
aluHandleDisconnect(device);
break;
}
/* Make sure we're going to write at least 2 'periods' (minreqs), in
* case the server increased it since starting playback. Also round up
* the number of writable periods if it's not an integer count.
*/
buffer_size = maxu((self->attr.tlength + self->attr.minreq/2) / self->attr.minreq, 2) *
self->attr.minreq;
/* NOTE: This assumes pa_stream_writable_size returns between 0 and
* tlength, else there will be more latency than intended.
*/
len = mini(len - (ssize_t)self->attr.tlength, 0) + buffer_size;
if(len < (int32_t)self->attr.minreq)
{
if(pa_stream_is_corked(self->stream))
{
pa_operation *o;
o = pa_stream_cork(self->stream, 0, NULL, NULL);
@@ -818,11 +850,12 @@ static int ALCpulsePlayback_mixerProc(void *ptr)
pa_threaded_mainloop_wait(self->loop);
continue;
}
len -= len%update_size;
len -= len%self->attr.minreq;
while(len > 0)
{
size_t newlen = len;
int ret;
void *buf;
pa_free_cb_t free_func = NULL;
@@ -834,10 +867,15 @@ static int ALCpulsePlayback_mixerProc(void *ptr)
aluMixData(device, buf, newlen/frame_size);
pa_stream_write(self->stream, buf, newlen, free_func, 0, PA_SEEK_RELATIVE);
ret = pa_stream_write(self->stream, buf, newlen, free_func, 0, PA_SEEK_RELATIVE);
if(ret != PA_OK)
{
ERR("Failed to write to stream: %d, %s\n", ret, pa_strerror(ret));
break;
}
len -= newlen;
}
} while(!self->killNow && device->Connected);
}
pa_threaded_mainloop_unlock(self->loop);
return 0;
@@ -858,12 +896,12 @@ static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name
if(VECTOR_SIZE(PlaybackDevices) == 0)
ALCpulsePlayback_probeDevices();
#define MATCH_NAME(iter) (al_string_cmp_cstr((iter)->name, name) == 0)
#define MATCH_NAME(iter) (alstr_cmp_cstr((iter)->name, name) == 0)
VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
#undef MATCH_NAME
if(iter == VECTOR_ITER_END(PlaybackDevices))
if(iter == VECTOR_END(PlaybackDevices))
return ALC_INVALID_VALUE;
pulse_name = al_string_get_cstr(iter->device_name);
pulse_name = alstr_get_cstr(iter->device_name);
dev_name = iter->name;
}
@@ -894,11 +932,11 @@ static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name
}
pa_stream_set_moved_callback(self->stream, ALCpulsePlayback_streamMovedCallback, self);
al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream));
if(al_string_empty(dev_name))
alstr_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream));
if(alstr_empty(dev_name))
{
pa_operation *o = pa_context_get_sink_info_by_name(
self->context, al_string_get_cstr(self->device_name),
self->context, alstr_get_cstr(self->device_name),
ALCpulsePlayback_sinkNameCallback, self
);
wait_for_operation(o, self->loop);
@@ -906,7 +944,7 @@ static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name
else
{
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
al_string_copy(&device->DeviceName, dev_name);
alstr_copy(&device->DeviceName, dev_name);
}
pa_threaded_mainloop_unlock(self->loop);
@@ -921,7 +959,7 @@ static void ALCpulsePlayback_close(ALCpulsePlayback *self)
self->context = NULL;
self->stream = NULL;
al_string_clear(&self->device_name);
alstr_clear(&self->device_name);
}
static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
@@ -931,7 +969,6 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
const char *mapname = NULL;
pa_channel_map chanmap;
pa_operation *o;
ALuint len;
pa_threaded_mainloop_lock(self->loop);
@@ -946,11 +983,11 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
self->stream = NULL;
}
o = pa_context_get_sink_info_by_name(self->context, al_string_get_cstr(self->device_name),
o = pa_context_get_sink_info_by_name(self->context, alstr_get_cstr(self->device_name),
ALCpulsePlayback_sinkInfoCallback, self);
wait_for_operation(o, self->loop);
if(GetConfigValueBool(al_string_get_cstr(device->DeviceName), "pulse", "fix-rate", 0) ||
if(GetConfigValueBool(alstr_get_cstr(device->DeviceName), "pulse", "fix-rate", 0) ||
!(device->Flags&DEVICE_FREQUENCY_REQUEST))
flags |= PA_STREAM_FIX_RATE;
flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;
@@ -984,7 +1021,7 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
break;
}
self->spec.rate = device->Frequency;
self->spec.channels = ChannelsFromDevFmt(device->FmtChans);
self->spec.channels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
if(pa_sample_spec_valid(&self->spec) == 0)
{
@@ -998,7 +1035,7 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
case DevFmtMono:
mapname = "mono";
break;
case DevFmtBFormat3D:
case DevFmtAmbi3D:
device->FmtChans = DevFmtStereo;
/*fall-through*/
case DevFmtStereo:
@@ -1034,9 +1071,9 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
self->attr.tlength = self->attr.minreq * maxu(device->NumUpdates, 2);
self->attr.maxlength = -1;
self->stream = ALCpulsePlayback_connectStream(al_string_get_cstr(self->device_name),
self->loop, self->context, flags,
&self->attr, &self->spec, &chanmap);
self->stream = ALCpulsePlayback_connectStream(alstr_get_cstr(self->device_name),
self->loop, self->context, flags, &self->attr, &self->spec, &chanmap
);
if(!self->stream)
{
pa_threaded_mainloop_unlock(self->loop);
@@ -1051,10 +1088,12 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
{
/* Server updated our playback rate, so modify the buffer attribs
* accordingly. */
device->NumUpdates = (ALuint)((ALdouble)device->NumUpdates / device->Frequency *
self->spec.rate + 0.5);
device->NumUpdates = (ALuint)clampd(
(ALdouble)device->NumUpdates/device->Frequency*self->spec.rate + 0.5, 2.0, 16.0
);
self->attr.minreq = device->UpdateSize * pa_frame_size(&self->spec);
self->attr.tlength = self->attr.minreq * clampu(device->NumUpdates, 2, 16);
self->attr.tlength = self->attr.minreq * device->NumUpdates;
self->attr.maxlength = -1;
self->attr.prebuf = 0;
@@ -1068,10 +1107,30 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
pa_stream_set_buffer_attr_callback(self->stream, ALCpulsePlayback_bufferAttrCallback, self);
ALCpulsePlayback_bufferAttrCallback(self->stream, self);
len = self->attr.minreq / pa_frame_size(&self->spec);
device->NumUpdates = (ALuint)((ALdouble)device->NumUpdates/len*device->UpdateSize + 0.5);
device->NumUpdates = clampu(device->NumUpdates, 2, 16);
device->UpdateSize = len;
device->NumUpdates = (ALuint)clampu64(
(self->attr.tlength + self->attr.minreq/2) / self->attr.minreq, 2, 16
);
device->UpdateSize = self->attr.minreq / pa_frame_size(&self->spec);
/* HACK: prebuf should be 0 as that's what we set it to. However on some
* systems it comes back as non-0, so we have to make sure the device will
* write enough audio to start playback. The lack of manual start control
* may have unintended consequences, but it's better than not starting at
* all.
*/
if(self->attr.prebuf != 0)
{
ALuint len = self->attr.prebuf / pa_frame_size(&self->spec);
if(len <= device->UpdateSize*device->NumUpdates)
ERR("Non-0 prebuf, %u samples (%u bytes), device has %u samples\n",
len, self->attr.prebuf, device->UpdateSize*device->NumUpdates);
else
{
ERR("Large prebuf, %u samples (%u bytes), increasing device from %u samples",
len, self->attr.prebuf, device->UpdateSize*device->NumUpdates);
device->NumUpdates = (len+device->UpdateSize-1) / device->UpdateSize;
}
}
pa_threaded_mainloop_unlock(self->loop);
return ALC_TRUE;
@@ -1113,11 +1172,14 @@ static void ALCpulsePlayback_stop(ALCpulsePlayback *self)
}
static ALint64 ALCpulsePlayback_getLatency(ALCpulsePlayback *self)
static ClockLatency ALCpulsePlayback_getClockLatency(ALCpulsePlayback *self)
{
pa_usec_t latency = 0;
ClockLatency ret;
int neg, err;
pa_threaded_mainloop_lock(self->loop);
ret.ClockTime = GetDeviceClockTime(STATIC_CAST(ALCbackend,self)->mDevice);
if((err=pa_stream_get_latency(self->stream, &latency, &neg)) != 0)
{
/* FIXME: if err = -PA_ERR_NODATA, it means we were called too soon
@@ -1126,11 +1188,14 @@ static ALint64 ALCpulsePlayback_getLatency(ALCpulsePlayback *self)
* dummy value? Either way, it shouldn't be 0. */
if(err != -PA_ERR_NODATA)
ERR("Failed to get stream latency: 0x%x\n", err);
return 0;
latency = 0;
neg = 0;
}
if(neg) latency = 0;
return (ALint64)minu64(latency, U64(0x7fffffffffffffff)/1000) * 1000;
ret.Latency = minu64(latency, U64(0xffffffffffffffff)/1000) * 1000;
pa_threaded_mainloop_unlock(self->loop);
return ret;
}
@@ -1186,7 +1251,7 @@ static ALCboolean ALCpulseCapture_start(ALCpulseCapture *self);
static void ALCpulseCapture_stop(ALCpulseCapture *self);
static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *buffer, ALCuint samples);
static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self);
static ALint64 ALCpulseCapture_getLatency(ALCpulseCapture *self);
static ClockLatency ALCpulseCapture_getClockLatency(ALCpulseCapture *self);
static void ALCpulseCapture_lock(ALCpulseCapture *self);
static void ALCpulseCapture_unlock(ALCpulseCapture *self);
DECLARE_DEFAULT_ALLOCATORS(ALCpulseCapture)
@@ -1222,35 +1287,35 @@ static void ALCpulseCapture_deviceCallback(pa_context *UNUSED(context), const pa
return;
}
#define MATCH_INFO_NAME(iter) (al_string_cmp_cstr((iter)->device_name, info->name) == 0)
#define MATCH_INFO_NAME(iter) (alstr_cmp_cstr((iter)->device_name, info->name) == 0)
VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_INFO_NAME);
if(iter != VECTOR_ITER_END(CaptureDevices)) return;
if(iter != VECTOR_END(CaptureDevices)) return;
#undef MATCH_INFO_NAME
AL_STRING_INIT(entry.name);
AL_STRING_INIT(entry.device_name);
al_string_copy_cstr(&entry.device_name, info->name);
alstr_copy_cstr(&entry.device_name, info->name);
count = 0;
while(1)
{
al_string_copy_cstr(&entry.name, info->description);
alstr_copy_cstr(&entry.name, info->description);
if(count != 0)
{
char str[64];
snprintf(str, sizeof(str), " #%d", count+1);
al_string_append_cstr(&entry.name, str);
alstr_append_cstr(&entry.name, str);
}
#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
#define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0)
VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_ENTRY);
if(iter == VECTOR_ITER_END(CaptureDevices)) break;
if(iter == VECTOR_END(CaptureDevices)) break;
#undef MATCH_ENTRY
count++;
}
TRACE("Got device \"%s\", \"%s\"\n", al_string_get_cstr(entry.name), al_string_get_cstr(entry.device_name));
TRACE("Got device \"%s\", \"%s\"\n", alstr_get_cstr(entry.name), alstr_get_cstr(entry.device_name));
VECTOR_PUSH_BACK(CaptureDevices, entry);
}
@@ -1343,7 +1408,7 @@ static void ALCpulseCapture_sourceNameCallback(pa_context *UNUSED(context), cons
return;
}
al_string_copy_cstr(&device->DeviceName, info->description);
alstr_copy_cstr(&device->DeviceName, info->description);
}
@@ -1351,9 +1416,9 @@ static void ALCpulseCapture_streamMovedCallback(pa_stream *stream, void *pdata)
{
ALCpulseCapture *self = pdata;
al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(stream));
alstr_copy_cstr(&self->device_name, pa_stream_get_device_name(stream));
TRACE("Stream moved to %s\n", al_string_get_cstr(self->device_name));
TRACE("Stream moved to %s\n", alstr_get_cstr(self->device_name));
}
@@ -1403,6 +1468,7 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
const char *pulse_name = NULL;
pa_stream_flags_t flags = 0;
const char *mapname = NULL;
pa_channel_map chanmap;
ALuint samples;
@@ -1413,13 +1479,13 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
if(VECTOR_SIZE(CaptureDevices) == 0)
ALCpulseCapture_probeDevices();
#define MATCH_NAME(iter) (al_string_cmp_cstr((iter)->name, name) == 0)
#define MATCH_NAME(iter) (alstr_cmp_cstr((iter)->name, name) == 0)
VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
#undef MATCH_NAME
if(iter == VECTOR_ITER_END(CaptureDevices))
if(iter == VECTOR_END(CaptureDevices))
return ALC_INVALID_VALUE;
pulse_name = al_string_get_cstr(iter->device_name);
al_string_copy(&device->DeviceName, iter->name);
pulse_name = alstr_get_cstr(iter->device_name);
alstr_copy(&device->DeviceName, iter->name);
}
if(!pulse_open(&self->loop, &self->context, ALCpulseCapture_contextStateCallback, self))
@@ -1427,9 +1493,6 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
pa_threaded_mainloop_lock(self->loop);
self->spec.rate = device->Frequency;
self->spec.channels = ChannelsFromDevFmt(device->FmtChans);
switch(device->FmtType)
{
case DevFmtUByte:
@@ -1452,6 +1515,44 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
goto fail;
}
switch(device->FmtChans)
{
case DevFmtMono:
mapname = "mono";
break;
case DevFmtStereo:
mapname = "front-left,front-right";
break;
case DevFmtQuad:
mapname = "front-left,front-right,rear-left,rear-right";
break;
case DevFmtX51:
mapname = "front-left,front-right,front-center,lfe,side-left,side-right";
break;
case DevFmtX51Rear:
mapname = "front-left,front-right,front-center,lfe,rear-left,rear-right";
break;
case DevFmtX61:
mapname = "front-left,front-right,front-center,lfe,rear-center,side-left,side-right";
break;
case DevFmtX71:
mapname = "front-left,front-right,front-center,lfe,rear-left,rear-right,side-left,side-right";
break;
case DevFmtAmbi3D:
ERR("%s capture samples not supported\n", DevFmtChannelsString(device->FmtChans));
pa_threaded_mainloop_unlock(self->loop);
goto fail;
}
if(!pa_channel_map_parse(&chanmap, mapname))
{
ERR("Failed to build channel map for %s\n", DevFmtChannelsString(device->FmtChans));
pa_threaded_mainloop_unlock(self->loop);
return ALC_FALSE;
}
self->spec.rate = device->Frequency;
self->spec.channels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
if(pa_sample_spec_valid(&self->spec) == 0)
{
ERR("Invalid sample format\n");
@@ -1481,9 +1582,9 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
flags |= PA_STREAM_DONT_MOVE;
TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
self->stream = ALCpulseCapture_connectStream(pulse_name, self->loop, self->context,
flags, &self->attr, &self->spec,
&chanmap);
self->stream = ALCpulseCapture_connectStream(pulse_name,
self->loop, self->context, flags, &self->attr, &self->spec, &chanmap
);
if(!self->stream)
{
pa_threaded_mainloop_unlock(self->loop);
@@ -1492,11 +1593,11 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
pa_stream_set_moved_callback(self->stream, ALCpulseCapture_streamMovedCallback, self);
pa_stream_set_state_callback(self->stream, ALCpulseCapture_streamStateCallback, self);
al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream));
if(al_string_empty(device->DeviceName))
alstr_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream));
if(alstr_empty(device->DeviceName))
{
pa_operation *o = pa_context_get_source_info_by_name(
self->context, al_string_get_cstr(self->device_name),
self->context, alstr_get_cstr(self->device_name),
ALCpulseCapture_sourceNameCallback, self
);
wait_for_operation(o, self->loop);
@@ -1521,23 +1622,26 @@ static void ALCpulseCapture_close(ALCpulseCapture *self)
self->context = NULL;
self->stream = NULL;
al_string_clear(&self->device_name);
alstr_clear(&self->device_name);
}
static ALCboolean ALCpulseCapture_start(ALCpulseCapture *self)
{
pa_operation *o;
pa_threaded_mainloop_lock(self->loop);
o = pa_stream_cork(self->stream, 0, stream_success_callback, self->loop);
wait_for_operation(o, self->loop);
pa_threaded_mainloop_unlock(self->loop);
return ALC_TRUE;
}
static void ALCpulseCapture_stop(ALCpulseCapture *self)
{
pa_operation *o;
pa_threaded_mainloop_lock(self->loop);
o = pa_stream_cork(self->stream, 1, stream_success_callback, self->loop);
wait_for_operation(o, self->loop);
pa_threaded_mainloop_unlock(self->loop);
}
static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *buffer, ALCuint samples)
@@ -1548,6 +1652,7 @@ static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *bu
/* Capture is done in fragment-sized chunks, so we loop until we get all
* that's available */
self->last_readable -= todo;
pa_threaded_mainloop_lock(self->loop);
while(todo > 0)
{
size_t rem = todo;
@@ -1587,6 +1692,7 @@ static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *bu
self->cap_len = 0;
}
}
pa_threaded_mainloop_unlock(self->loop);
if(todo > 0)
memset(buffer, ((device->FmtType==DevFmtUByte) ? 0x80 : 0), todo);
@@ -1600,7 +1706,9 @@ static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self)
if(device->Connected)
{
ssize_t got = pa_stream_readable_size(self->stream);
ssize_t got;
pa_threaded_mainloop_lock(self->loop);
got = pa_stream_readable_size(self->stream);
if(got < 0)
{
ERR("pa_stream_readable_size() failed: %s\n", pa_strerror(got));
@@ -1608,6 +1716,7 @@ static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self)
}
else if((size_t)got > self->cap_len)
readable += got - self->cap_len;
pa_threaded_mainloop_unlock(self->loop);
}
if(self->last_readable < readable)
@@ -1616,19 +1725,25 @@ static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self)
}
static ALint64 ALCpulseCapture_getLatency(ALCpulseCapture *self)
static ClockLatency ALCpulseCapture_getClockLatency(ALCpulseCapture *self)
{
pa_usec_t latency = 0;
int neg;
ClockLatency ret;
int neg, err;
if(pa_stream_get_latency(self->stream, &latency, &neg) != 0)
pa_threaded_mainloop_lock(self->loop);
ret.ClockTime = GetDeviceClockTime(STATIC_CAST(ALCbackend,self)->mDevice);
if((err=pa_stream_get_latency(self->stream, &latency, &neg)) != 0)
{
ERR("Failed to get stream latency!\n");
return 0;
ERR("Failed to get stream latency: 0x%x\n", err);
latency = 0;
neg = 0;
}
if(neg) latency = 0;
return (ALint64)minu64(latency, U64(0x7fffffffffffffff)/1000) * 1000;
ret.Latency = minu64(latency, U64(0xffffffffffffffff)/1000) * 1000;
pa_threaded_mainloop_unlock(self->loop);
return ret;
}
@@ -1732,14 +1847,14 @@ static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), e
{
case ALL_DEVICE_PROBE:
ALCpulsePlayback_probeDevices();
#define APPEND_ALL_DEVICES_LIST(e) AppendAllDevicesList(al_string_get_cstr((e)->name))
#define APPEND_ALL_DEVICES_LIST(e) AppendAllDevicesList(alstr_get_cstr((e)->name))
VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_ALL_DEVICES_LIST);
#undef APPEND_ALL_DEVICES_LIST
break;
case CAPTURE_DEVICE_PROBE:
ALCpulseCapture_probeDevices();
#define APPEND_CAPTURE_DEVICE_LIST(e) AppendCaptureDeviceList(al_string_get_cstr((e)->name))
#define APPEND_CAPTURE_DEVICE_LIST(e) AppendCaptureDeviceList(alstr_get_cstr((e)->name))
VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_CAPTURE_DEVICE_LIST);
#undef APPEND_CAPTURE_DEVICE_LIST
break;
+261 -109
View File
@@ -33,6 +33,8 @@
#include "alu.h"
#include "threads.h"
#include "backends/base.h"
typedef struct {
snd_pcm_t* pcmHandle;
@@ -117,8 +119,7 @@ static void deviceList(int type, vector_DevMap *devmap)
if(max_cards < 0)
return;
VECTOR_RESERVE(*devmap, max_cards+1);
VECTOR_RESIZE(*devmap, 0);
VECTOR_RESIZE(*devmap, 0, max_cards+1);
entry.name = strdup(qsaDevice);
entry.card = 0;
@@ -158,17 +159,40 @@ static void deviceList(int type, vector_DevMap *devmap)
}
FORCE_ALIGN static int qsa_proc_playback(void* ptr)
/* Wrappers to use an old-style backend with the new interface. */
typedef struct PlaybackWrapper {
DERIVE_FROM_TYPE(ALCbackend);
qsa_data *ExtraData;
} PlaybackWrapper;
static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device);
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 DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, lock)
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(PlaybackWrapper)
DEFINE_ALCBACKEND_VTABLE(PlaybackWrapper);
FORCE_ALIGN static int qsa_proc_playback(void *ptr)
{
ALCdevice* device=(ALCdevice*)ptr;
qsa_data* data=(qsa_data*)device->ExtraData;
char* write_ptr;
int avail;
PlaybackWrapper *self = ptr;
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
qsa_data *data = self->ExtraData;
snd_pcm_channel_status_t status;
struct sched_param param;
fd_set wfds;
int selectret;
struct timeval timeout;
char* write_ptr;
fd_set wfds;
ALint len;
int sret;
SetRTPriority();
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
@@ -178,59 +202,55 @@ FORCE_ALIGN static int qsa_proc_playback(void* ptr)
param.sched_priority=param.sched_curpriority+1;
SchedSet(0, 0, SCHED_NOCHANGE, &param);
ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
const ALint frame_size = FrameSizeFromDevFmt(
device->FmtChans, device->FmtType, device->AmbiOrder
);
while (!data->killNow)
V0(device->Backend,lock)();
while(!data->killNow)
{
ALint len=data->size;
write_ptr=data->buffer;
FD_ZERO(&wfds);
FD_SET(data->audio_fd, &wfds);
timeout.tv_sec=2;
timeout.tv_usec=0;
avail=len/frame_size;
aluMixData(device, write_ptr, avail);
while (len>0 && !data->killNow)
/* Select also works like time slice to OS */
V0(device->Backend,unlock)();
sret = select(data->audio_fd+1, NULL, &wfds, NULL, &timeout);
V0(device->Backend,lock)();
if(sret == -1)
{
FD_ZERO(&wfds);
FD_SET(data->audio_fd, &wfds);
timeout.tv_sec=2;
timeout.tv_usec=0;
ERR("select error: %s\n", strerror(errno));
aluHandleDisconnect(device);
break;
}
if(sret == 0)
{
ERR("select timeout\n");
continue;
}
/* Select also works like time slice to OS */
selectret=select(data->audio_fd+1, NULL, &wfds, NULL, &timeout);
switch (selectret)
len = data->size;
write_ptr = data->buffer;
aluMixData(device, write_ptr, len/frame_size);
while(len>0 && !data->killNow)
{
int wrote = snd_pcm_plugin_write(data->pcmHandle, write_ptr, len);
if(wrote <= 0)
{
case -1:
aluHandleDisconnect(device);
return 1;
case 0:
break;
default:
if (FD_ISSET(data->audio_fd, &wfds))
{
break;
}
break;
}
int wrote=snd_pcm_plugin_write(data->pcmHandle, write_ptr, len);
if (wrote<=0)
{
if ((errno==EAGAIN) || (errno==EWOULDBLOCK))
{
if(errno==EAGAIN || errno==EWOULDBLOCK)
continue;
}
memset(&status, 0, sizeof (status));
status.channel=SND_PCM_CHANNEL_PLAYBACK;
memset(&status, 0, sizeof(status));
status.channel = SND_PCM_CHANNEL_PLAYBACK;
snd_pcm_plugin_status(data->pcmHandle, &status);
/* we need to reinitialize the sound channel if we've underrun the buffer */
if ((status.status==SND_PCM_STATUS_UNDERRUN) ||
(status.status==SND_PCM_STATUS_READY))
if(status.status == SND_PCM_STATUS_UNDERRUN ||
status.status == SND_PCM_STATUS_READY)
{
if ((snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK))<0)
if(snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK) < 0)
{
aluHandleDisconnect(device);
break;
@@ -239,11 +259,12 @@ FORCE_ALIGN static int qsa_proc_playback(void* ptr)
}
else
{
write_ptr+=wrote;
len-=wrote;
write_ptr += wrote;
len -= wrote;
}
}
}
V0(device->Backend,unlock)();
return 0;
}
@@ -252,8 +273,9 @@ FORCE_ALIGN static int qsa_proc_playback(void* ptr)
/* Playback */
/************/
static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName)
static ALCenum qsa_open_playback(PlaybackWrapper *self, const ALCchar* deviceName)
{
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
qsa_data *data;
int card, dev;
int status;
@@ -277,7 +299,7 @@ static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName)
#define MATCH_DEVNAME(iter) ((iter)->name && strcmp(deviceName, (iter)->name)==0)
VECTOR_FIND_IF(iter, const DevMap, DeviceNameMap, MATCH_DEVNAME);
#undef MATCH_DEVNAME
if(iter == VECTOR_ITER_END(DeviceNameMap))
if(iter == VECTOR_END(DeviceNameMap))
{
free(data);
return ALC_INVALID_DEVICE;
@@ -300,15 +322,15 @@ static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName)
return ALC_INVALID_DEVICE;
}
al_string_copy_cstr(&device->DeviceName, deviceName);
device->ExtraData = data;
alstr_copy_cstr(&device->DeviceName, deviceName);
self->ExtraData = data;
return ALC_NO_ERROR;
}
static void qsa_close_playback(ALCdevice* device)
static void qsa_close_playback(PlaybackWrapper *self)
{
qsa_data* data=(qsa_data*)device->ExtraData;
qsa_data *data = self->ExtraData;
if (data->buffer!=NULL)
{
@@ -319,12 +341,13 @@ static void qsa_close_playback(ALCdevice* device)
snd_pcm_close(data->pcmHandle);
free(data);
device->ExtraData=NULL;
self->ExtraData = NULL;
}
static ALCboolean qsa_reset_playback(ALCdevice* device)
static ALCboolean qsa_reset_playback(PlaybackWrapper *self)
{
qsa_data* data=(qsa_data*)device->ExtraData;
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
qsa_data *data = self->ExtraData;
int32_t format=-1;
switch(device->FmtType)
@@ -365,14 +388,14 @@ static ALCboolean qsa_reset_playback(ALCdevice* device)
data->cparams.start_mode=SND_PCM_START_FULL;
data->cparams.stop_mode=SND_PCM_STOP_STOP;
data->cparams.buf.block.frag_size=device->UpdateSize*
ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType);
data->cparams.buf.block.frag_size=device->UpdateSize *
FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
data->cparams.buf.block.frags_max=device->NumUpdates;
data->cparams.buf.block.frags_min=device->NumUpdates;
data->cparams.format.interleave=1;
data->cparams.format.rate=device->Frequency;
data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans);
data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
data->cparams.format.format=format;
if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0)
@@ -556,7 +579,7 @@ static ALCboolean qsa_reset_playback(ALCdevice* device)
SetDefaultChannelOrder(device);
device->UpdateSize=data->csetup.buf.block.frag_size/
(ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType));
FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
device->NumUpdates=data->csetup.buf.block.frags;
data->size=data->csetup.buf.block.frag_size;
@@ -569,20 +592,20 @@ static ALCboolean qsa_reset_playback(ALCdevice* device)
return ALC_TRUE;
}
static ALCboolean qsa_start_playback(ALCdevice* device)
static ALCboolean qsa_start_playback(PlaybackWrapper *self)
{
qsa_data *data = (qsa_data*)device->ExtraData;
qsa_data *data = self->ExtraData;
data->killNow = 0;
if(althrd_create(&data->thread, qsa_proc_playback, device) != althrd_success)
if(althrd_create(&data->thread, qsa_proc_playback, self) != althrd_success)
return ALC_FALSE;
return ALC_TRUE;
}
static void qsa_stop_playback(ALCdevice* device)
static void qsa_stop_playback(PlaybackWrapper *self)
{
qsa_data *data = (qsa_data*)device->ExtraData;
qsa_data *data = self->ExtraData;
int res;
if(data->killNow)
@@ -592,12 +615,70 @@ static void qsa_stop_playback(ALCdevice* device)
althrd_join(data->thread, &res);
}
static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(PlaybackWrapper, ALCbackend, self);
self->ExtraData = NULL;
}
static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name)
{
return qsa_open_playback(self, name);
}
static void PlaybackWrapper_close(PlaybackWrapper *self)
{
qsa_close_playback(self);
}
static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self)
{
return qsa_reset_playback(self);
}
static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self)
{
return qsa_start_playback(self);
}
static void PlaybackWrapper_stop(PlaybackWrapper *self)
{
qsa_stop_playback(self);
}
/***********/
/* Capture */
/***********/
static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
typedef struct CaptureWrapper {
DERIVE_FROM_TYPE(ALCbackend);
qsa_data *ExtraData;
} CaptureWrapper;
static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device);
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 DECLARE_FORWARD(CaptureWrapper, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, lock)
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(CaptureWrapper)
DEFINE_ALCBACKEND_VTABLE(CaptureWrapper);
static ALCenum qsa_open_capture(CaptureWrapper *self, const ALCchar *deviceName)
{
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
qsa_data *data;
int card, dev;
int format=-1;
@@ -624,7 +705,7 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
#define MATCH_DEVNAME(iter) ((iter)->name && strcmp(deviceName, (iter)->name)==0)
VECTOR_FIND_IF(iter, const DevMap, CaptureNameMap, MATCH_DEVNAME);
#undef MATCH_DEVNAME
if(iter == VECTOR_ITER_END(CaptureNameMap))
if(iter == VECTOR_END(CaptureNameMap))
{
free(data);
return ALC_INVALID_DEVICE;
@@ -647,8 +728,8 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
return ALC_INVALID_DEVICE;
}
al_string_copy_cstr(&device->DeviceName, deviceName);
device->ExtraData = data;
alstr_copy_cstr(&device->DeviceName, deviceName);
self->ExtraData = data;
switch (device->FmtType)
{
@@ -688,20 +769,19 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
data->cparams.stop_mode=SND_PCM_STOP_STOP;
data->cparams.buf.block.frag_size=device->UpdateSize*
ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType);
FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
data->cparams.buf.block.frags_max=device->NumUpdates;
data->cparams.buf.block.frags_min=device->NumUpdates;
data->cparams.format.interleave=1;
data->cparams.format.rate=device->Frequency;
data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans);
data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
data->cparams.format.format=format;
if(snd_pcm_plugin_params(data->pcmHandle, &data->cparams) < 0)
{
snd_pcm_close(data->pcmHandle);
free(data);
device->ExtraData=NULL;
return ALC_INVALID_VALUE;
}
@@ -709,20 +789,20 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
return ALC_NO_ERROR;
}
static void qsa_close_capture(ALCdevice* device)
static void qsa_close_capture(CaptureWrapper *self)
{
qsa_data* data=(qsa_data*)device->ExtraData;
qsa_data *data = self->ExtraData;
if (data->pcmHandle!=NULL)
snd_pcm_close(data->pcmHandle);
free(data);
device->ExtraData=NULL;
self->ExtraData = NULL;
}
static void qsa_start_capture(ALCdevice* device)
static void qsa_start_capture(CaptureWrapper *self)
{
qsa_data* data=(qsa_data*)device->ExtraData;
qsa_data *data = self->ExtraData;
int rstatus;
if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
@@ -742,18 +822,18 @@ static void qsa_start_capture(ALCdevice* device)
snd_pcm_capture_go(data->pcmHandle);
}
static void qsa_stop_capture(ALCdevice* device)
static void qsa_stop_capture(CaptureWrapper *self)
{
qsa_data* data=(qsa_data*)device->ExtraData;
qsa_data *data = self->ExtraData;
snd_pcm_capture_flush(data->pcmHandle);
}
static ALCuint qsa_available_samples(ALCdevice* device)
static ALCuint qsa_available_samples(CaptureWrapper *self)
{
qsa_data* data=(qsa_data*)device->ExtraData;
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
qsa_data *data = self->ExtraData;
snd_pcm_channel_status_t status;
ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
ALint frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
ALint free_size;
int rstatus;
@@ -780,16 +860,17 @@ static ALCuint qsa_available_samples(ALCdevice* device)
return free_size/frame_size;
}
static ALCenum qsa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
static ALCenum qsa_capture_samples(CaptureWrapper *self, ALCvoid *buffer, ALCuint samples)
{
qsa_data* data=(qsa_data*)device->ExtraData;
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
qsa_data *data = self->ExtraData;
char* read_ptr;
snd_pcm_channel_status_t status;
fd_set rfds;
int selectret;
struct timeval timeout;
int bytes_read;
ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
ALint len=samples*frame_size;
int rstatus;
@@ -855,27 +936,65 @@ static ALCenum qsa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint s
return ALC_NO_ERROR;
}
static const BackendFuncs qsa_funcs= {
qsa_open_playback,
qsa_close_playback,
qsa_reset_playback,
qsa_start_playback,
qsa_stop_playback,
qsa_open_capture,
qsa_close_capture,
qsa_start_capture,
qsa_stop_capture,
qsa_capture_samples,
qsa_available_samples
};
ALCboolean alc_qsa_init(BackendFuncs* func_list)
static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device)
{
*func_list = qsa_funcs;
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(CaptureWrapper, ALCbackend, self);
self->ExtraData = NULL;
}
static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name)
{
return qsa_open_capture(self, name);
}
static void CaptureWrapper_close(CaptureWrapper *self)
{
qsa_close_capture(self);
}
static ALCboolean CaptureWrapper_start(CaptureWrapper *self)
{
qsa_start_capture(self);
return ALC_TRUE;
}
void alc_qsa_deinit(void)
static void CaptureWrapper_stop(CaptureWrapper *self)
{
qsa_stop_capture(self);
}
static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples)
{
return qsa_capture_samples(self, buffer, samples);
}
static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self)
{
return qsa_available_samples(self);
}
typedef struct ALCqsaBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCqsaBackendFactory;
#define ALCQSABACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCqsaBackendFactory, ALCbackendFactory) } }
static ALCboolean ALCqsaBackendFactory_init(ALCqsaBackendFactory* UNUSED(self));
static void ALCqsaBackendFactory_deinit(ALCqsaBackendFactory* UNUSED(self));
static ALCboolean ALCqsaBackendFactory_querySupport(ALCqsaBackendFactory* UNUSED(self), ALCbackend_Type type);
static void ALCqsaBackendFactory_probe(ALCqsaBackendFactory* UNUSED(self), enum DevProbe type);
static ALCbackend* ALCqsaBackendFactory_createBackend(ALCqsaBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCqsaBackendFactory);
static ALCboolean ALCqsaBackendFactory_init(ALCqsaBackendFactory* UNUSED(self))
{
return ALC_TRUE;
}
static void ALCqsaBackendFactory_deinit(ALCqsaBackendFactory* UNUSED(self))
{
#define FREE_NAME(iter) free((iter)->name)
VECTOR_FOR_EACH(DevMap, DeviceNameMap, FREE_NAME);
@@ -886,15 +1005,22 @@ void alc_qsa_deinit(void)
#undef FREE_NAME
}
void alc_qsa_probe(enum DevProbe type)
static ALCboolean ALCqsaBackendFactory_querySupport(ALCqsaBackendFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Playback || type == ALCbackend_Capture)
return ALC_TRUE;
return ALC_FALSE;
}
static void ALCqsaBackendFactory_probe(ALCqsaBackendFactory* UNUSED(self), enum DevProbe type)
{
switch (type)
{
case ALL_DEVICE_PROBE:
#define FREE_NAME(iter) free((iter)->name)
VECTOR_FOR_EACH(DevMap, DeviceNameMap, FREE_NAME);
VECTOR_RESIZE(DeviceNameMap, 0, 0);
#undef FREE_NAME
VECTOR_RESIZE(DeviceNameMap, 0);
deviceList(SND_PCM_CHANNEL_PLAYBACK, &DeviceNameMap);
#define APPEND_DEVICE(iter) AppendAllDevicesList((iter)->name)
@@ -905,8 +1031,8 @@ void alc_qsa_probe(enum DevProbe type)
case CAPTURE_DEVICE_PROBE:
#define FREE_NAME(iter) free((iter)->name)
VECTOR_FOR_EACH(DevMap, CaptureNameMap, FREE_NAME);
VECTOR_RESIZE(CaptureNameMap, 0, 0);
#undef FREE_NAME
VECTOR_RESIZE(CaptureNameMap, 0);
deviceList(SND_PCM_CHANNEL_CAPTURE, &CaptureNameMap);
#define APPEND_DEVICE(iter) AppendCaptureDeviceList((iter)->name)
@@ -915,3 +1041,29 @@ void alc_qsa_probe(enum DevProbe type)
break;
}
}
static ALCbackend* ALCqsaBackendFactory_createBackend(ALCqsaBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
PlaybackWrapper *backend;
NEW_OBJ(backend, PlaybackWrapper)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
if(type == ALCbackend_Capture)
{
CaptureWrapper *backend;
NEW_OBJ(backend, CaptureWrapper)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
ALCbackendFactory *ALCqsaBackendFactory_getFactory(void)
{
static ALCqsaBackendFactory factory = ALCQSABACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
+130 -78
View File
@@ -28,19 +28,16 @@
#include "alu.h"
#include "threads.h"
#include "backends/base.h"
#include <sndio.h>
static const ALCchar sndio_device[] = "SndIO Default";
static ALCboolean sndio_load(void)
{
return ALC_TRUE;
}
typedef struct ALCsndioBackend {
DERIVE_FROM_TYPE(ALCbackend);
typedef struct {
struct sio_hdl *sndHandle;
ALvoid *mix_data;
@@ -48,30 +45,72 @@ typedef struct {
volatile int killNow;
althrd_t thread;
} sndio_data;
} ALCsndioBackend;
static int ALCsndioBackend_mixerProc(void *ptr);
static void ALCsndioBackend_Construct(ALCsndioBackend *self, ALCdevice *device);
static void ALCsndioBackend_Destruct(ALCsndioBackend *self);
static ALCenum ALCsndioBackend_open(ALCsndioBackend *self, const ALCchar *name);
static void ALCsndioBackend_close(ALCsndioBackend *self);
static ALCboolean ALCsndioBackend_reset(ALCsndioBackend *self);
static ALCboolean ALCsndioBackend_start(ALCsndioBackend *self);
static void ALCsndioBackend_stop(ALCsndioBackend *self);
static DECLARE_FORWARD2(ALCsndioBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, ALCuint, availableSamples)
static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCsndioBackend)
DEFINE_ALCBACKEND_VTABLE(ALCsndioBackend);
static int sndio_proc(void *ptr)
static const ALCchar sndio_device[] = "SndIO Default";
static void ALCsndioBackend_Construct(ALCsndioBackend *self, ALCdevice *device)
{
ALCdevice *device = ptr;
sndio_data *data = device->ExtraData;
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCsndioBackend, ALCbackend, self);
}
static void ALCsndioBackend_Destruct(ALCsndioBackend *self)
{
if(self->sndHandle)
sio_close(self->sndHandle);
self->sndHandle = NULL;
al_free(self->mix_data);
self->mix_data = NULL;
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
static int ALCsndioBackend_mixerProc(void *ptr)
{
ALCsndioBackend *self = (ALCsndioBackend*)ptr;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
ALsizei frameSize;
size_t wrote;
SetRTPriority();
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
while(!data->killNow && device->Connected)
while(!self->killNow && device->Connected)
{
ALsizei len = data->data_size;
ALubyte *WritePtr = data->mix_data;
ALsizei len = self->data_size;
ALubyte *WritePtr = self->mix_data;
ALCsndioBackend_lock(self);
aluMixData(device, WritePtr, len/frameSize);
while(len > 0 && !data->killNow)
ALCsndioBackend_unlock(self);
while(len > 0 && !self->killNow)
{
wrote = sio_write(data->sndHandle, WritePtr, len);
wrote = sio_write(self->sndHandle, WritePtr, len);
if(wrote == 0)
{
ERR("sio_write failed\n");
@@ -90,45 +129,36 @@ static int sndio_proc(void *ptr)
}
static ALCenum sndio_open_playback(ALCdevice *device, const ALCchar *deviceName)
static ALCenum ALCsndioBackend_open(ALCsndioBackend *self, const ALCchar *name)
{
sndio_data *data;
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
if(!deviceName)
deviceName = sndio_device;
else if(strcmp(deviceName, sndio_device) != 0)
if(!name)
name = sndio_device;
else if(strcmp(name, 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)
self->sndHandle = sio_open(NULL, SIO_PLAY, 0);
if(self->sndHandle == NULL)
{
free(data);
ERR("Could not open device\n");
return ALC_INVALID_VALUE;
}
al_string_copy_cstr(&device->DeviceName, deviceName);
device->ExtraData = data;
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
static void sndio_close_playback(ALCdevice *device)
static void ALCsndioBackend_close(ALCsndioBackend *self)
{
sndio_data *data = device->ExtraData;
sio_close(data->sndHandle);
free(data);
device->ExtraData = NULL;
sio_close(self->sndHandle);
self->sndHandle = NULL;
}
static ALCboolean sndio_reset_playback(ALCdevice *device)
static ALCboolean ALCsndioBackend_reset(ALCsndioBackend *self)
{
sndio_data *data = device->ExtraData;
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
struct sio_par par;
sio_initpar(&par);
@@ -170,7 +200,7 @@ static ALCboolean sndio_reset_playback(ALCdevice *device)
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))
if(!sio_setpar(self->sndHandle, &par) || !sio_getpar(self->sndHandle, &par))
{
ERR("Failed to set device parameters\n");
return ALC_FALSE;
@@ -211,77 +241,86 @@ static ALCboolean sndio_reset_playback(ALCdevice *device)
return ALC_TRUE;
}
static ALCboolean sndio_start_playback(ALCdevice *device)
static ALCboolean ALCsndioBackend_start(ALCsndioBackend *self)
{
sndio_data *data = device->ExtraData;
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
if(!sio_start(data->sndHandle))
self->data_size = device->UpdateSize * FrameSizeFromDevFmt(
device->FmtChans, device->FmtType, device->AmbiOrder
);
al_free(self->mix_data);
self->mix_data = al_calloc(16, self->data_size);
if(!sio_start(self->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)
self->killNow = 0;
if(althrd_create(&self->thread, ALCsndioBackend_mixerProc, self) != althrd_success)
{
sio_stop(data->sndHandle);
free(data->mix_data);
data->mix_data = NULL;
sio_stop(self->sndHandle);
return ALC_FALSE;
}
return ALC_TRUE;
}
static void sndio_stop_playback(ALCdevice *device)
static void ALCsndioBackend_stop(ALCsndioBackend *self)
{
sndio_data *data = device->ExtraData;
int res;
if(data->killNow)
if(self->killNow)
return;
data->killNow = 1;
althrd_join(data->thread, &res);
self->killNow = 1;
althrd_join(self->thread, &res);
if(!sio_stop(data->sndHandle))
if(!sio_stop(self->sndHandle))
ERR("Error stopping device\n");
free(data->mix_data);
data->mix_data = NULL;
al_free(self->mix_data);
self->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
};
typedef struct ALCsndioBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCsndioBackendFactory;
#define ALCSNDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCsndioBackendFactory, ALCbackendFactory) } }
ALCboolean alc_sndio_init(BackendFuncs *func_list)
ALCbackendFactory *ALCsndioBackendFactory_getFactory(void);
static ALCboolean ALCsndioBackendFactory_init(ALCsndioBackendFactory *self);
static DECLARE_FORWARD(ALCsndioBackendFactory, ALCbackendFactory, void, deinit)
static ALCboolean ALCsndioBackendFactory_querySupport(ALCsndioBackendFactory *self, ALCbackend_Type type);
static void ALCsndioBackendFactory_probe(ALCsndioBackendFactory *self, enum DevProbe type);
static ALCbackend* ALCsndioBackendFactory_createBackend(ALCsndioBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsndioBackendFactory);
ALCbackendFactory *ALCsndioBackendFactory_getFactory(void)
{
if(!sndio_load())
return ALC_FALSE;
*func_list = sndio_funcs;
static ALCsndioBackendFactory factory = ALCSNDIOBACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
static ALCboolean ALCsndioBackendFactory_init(ALCsndioBackendFactory* UNUSED(self))
{
/* No dynamic loading */
return ALC_TRUE;
}
void alc_sndio_deinit(void)
static ALCboolean ALCsndioBackendFactory_querySupport(ALCsndioBackendFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
return ALC_TRUE;
return ALC_FALSE;
}
void alc_sndio_probe(enum DevProbe type)
static void ALCsndioBackendFactory_probe(ALCsndioBackendFactory* UNUSED(self), enum DevProbe type)
{
switch(type)
{
@@ -292,3 +331,16 @@ void alc_sndio_probe(enum DevProbe type)
break;
}
}
static ALCbackend* ALCsndioBackendFactory_createBackend(ALCsndioBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCsndioBackend *backend;
NEW_OBJ(backend, ALCsndioBackend)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
+59 -35
View File
@@ -50,7 +50,7 @@ typedef struct ALCsolarisBackend {
ALubyte *mix_data;
int data_size;
volatile int killNow;
ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCsolarisBackend;
@@ -65,7 +65,7 @@ static ALCboolean ALCsolarisBackend_start(ALCsolarisBackend *self);
static void ALCsolarisBackend_stop(ALCsolarisBackend *self);
static DECLARE_FORWARD2(ALCsolarisBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, ALCuint, availableSamples)
static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, ALint64, getLatency)
static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCsolarisBackend)
@@ -84,6 +84,7 @@ static void ALCsolarisBackend_Construct(ALCsolarisBackend *self, ALCdevice *devi
SET_VTABLE2(ALCsolarisBackend, ALCbackend, self);
self->fd = -1;
ATOMIC_INIT(&self->killNow, AL_FALSE);
}
static void ALCsolarisBackend_Destruct(ALCsolarisBackend *self)
@@ -103,43 +104,65 @@ static void ALCsolarisBackend_Destruct(ALCsolarisBackend *self)
static int ALCsolarisBackend_mixerProc(void *ptr)
{
ALCsolarisBackend *self = ptr;
ALCdevice *Device = STATIC_CAST(ALCbackend,self)->mDevice;
ALint frameSize;
int wrote;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
struct timeval timeout;
ALubyte *write_ptr;
ALint frame_size;
ALint to_write;
ssize_t wrote;
fd_set wfds;
int sret;
SetRTPriority();
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
while(!self->killNow && Device->Connected)
ALCsolarisBackend_lock(self);
while(!ATOMIC_LOAD_SEQ(&self->killNow) && device->Connected)
{
ALint len = self->data_size;
ALubyte *WritePtr = self->mix_data;
FD_ZERO(&wfds);
FD_SET(self->fd, &wfds);
timeout.tv_sec = 1;
timeout.tv_usec = 0;
aluMixData(Device, WritePtr, len/frameSize);
while(len > 0 && !self->killNow)
ALCsolarisBackend_unlock(self);
sret = select(self->fd+1, NULL, &wfds, NULL, &timeout);
ALCsolarisBackend_lock(self);
if(sret < 0)
{
wrote = write(self->fd, WritePtr, len);
if(errno == EINTR)
continue;
ERR("select failed: %s\n", strerror(errno));
aluHandleDisconnect(device);
break;
}
else if(sret == 0)
{
WARN("select timeout\n");
continue;
}
write_ptr = self->mix_data;
to_write = self->data_size;
aluMixData(device, write_ptr, to_write/frame_size);
while(to_write > 0 && !ATOMIC_LOAD_SEQ(&self->killNow))
{
wrote = write(self->fd, write_ptr, to_write);
if(wrote < 0)
{
if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
{
ERR("write failed: %s\n", strerror(errno));
ALCsolarisBackend_lock(self);
aluHandleDisconnect(Device);
ALCsolarisBackend_unlock(self);
break;
}
al_nssleep(1000000);
continue;
if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
continue;
ERR("write failed: %s\n", strerror(errno));
aluHandleDisconnect(device);
break;
}
len -= wrote;
WritePtr += wrote;
to_write -= wrote;
write_ptr += wrote;
}
}
ALCsolarisBackend_unlock(self);
return 0;
}
@@ -162,7 +185,7 @@ static ALCenum ALCsolarisBackend_open(ALCsolarisBackend *self, const ALCchar *na
}
device = STATIC_CAST(ALCbackend,self)->mDevice;
al_string_copy_cstr(&device->DeviceName, name);
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
@@ -177,8 +200,8 @@ static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
audio_info_t info;
ALuint frameSize;
int numChannels;
ALsizei frameSize;
ALsizei numChannels;
AUDIO_INITINFO(&info);
@@ -186,7 +209,7 @@ static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
if(device->FmtChans != DevFmtMono)
device->FmtChans = DevFmtStereo;
numChannels = ChannelsFromDevFmt(device->FmtChans);
numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
info.play.channels = numChannels;
switch(device->FmtType)
@@ -220,9 +243,9 @@ static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
return ALC_FALSE;
}
if(ChannelsFromDevFmt(device->FmtChans) != info.play.channels)
if(ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != (ALsizei)info.play.channels)
{
ERR("Could not set %d channels, got %d instead\n", ChannelsFromDevFmt(device->FmtChans), info.play.channels);
ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(device->FmtChans), info.play.channels);
return ALC_FALSE;
}
@@ -242,7 +265,9 @@ static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
SetDefaultChannelOrder(device);
free(self->mix_data);
self->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
self->data_size = device->UpdateSize * FrameSizeFromDevFmt(
device->FmtChans, device->FmtType, device->AmbiOrder
);
self->mix_data = calloc(1, self->data_size);
return ALC_TRUE;
@@ -250,7 +275,7 @@ static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
static ALCboolean ALCsolarisBackend_start(ALCsolarisBackend *self)
{
self->killNow = 0;
ATOMIC_STORE_SEQ(&self->killNow, AL_FALSE);
if(althrd_create(&self->thread, ALCsolarisBackend_mixerProc, self) != althrd_success)
return ALC_FALSE;
return ALC_TRUE;
@@ -260,10 +285,9 @@ static void ALCsolarisBackend_stop(ALCsolarisBackend *self)
{
int res;
if(self->killNow)
if(ATOMIC_EXCHANGE_SEQ(&self->killNow, AL_TRUE))
return;
self->killNow = 1;
althrd_join(self->thread, &res);
if(ioctl(self->fd, AUDIO_DRAIN) < 0)
+43 -30
View File
@@ -91,7 +91,7 @@ static ALCboolean ALCwaveBackend_start(ALCwaveBackend *self);
static void ALCwaveBackend_stop(ALCwaveBackend *self);
static DECLARE_FORWARD2(ALCwaveBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, ALCuint, availableSamples)
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, ALint64, getLatency)
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCwaveBackend)
@@ -127,7 +127,7 @@ static int ALCwaveBackend_mixerProc(void *ptr)
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
done = 0;
if(altimespec_get(&start, AL_TIME_UTC) != AL_TIME_UTC)
@@ -157,37 +157,41 @@ static int ALCwaveBackend_mixerProc(void *ptr)
al_nssleep(restTime);
else while(avail-done >= device->UpdateSize)
{
ALCwaveBackend_lock(self);
aluMixData(device, self->mBuffer, device->UpdateSize);
ALCwaveBackend_unlock(self);
done += device->UpdateSize;
if(!IS_LITTLE_ENDIAN)
{
ALuint bytesize = BytesFromDevFmt(device->FmtType);
ALubyte *bytes = self->mBuffer;
ALuint i;
if(bytesize == 1)
if(bytesize == 2)
{
for(i = 0;i < self->mSize;i++)
fputc(bytes[i], self->mFile);
}
else if(bytesize == 2)
{
for(i = 0;i < self->mSize;i++)
fputc(bytes[i^1], self->mFile);
ALushort *samples = self->mBuffer;
ALuint len = self->mSize / 2;
for(i = 0;i < len;i++)
{
ALushort samp = samples[i];
samples[i] = (samp>>8) | (samp<<8);
}
}
else if(bytesize == 4)
{
for(i = 0;i < self->mSize;i++)
fputc(bytes[i^3], self->mFile);
ALuint *samples = self->mBuffer;
ALuint len = self->mSize / 4;
for(i = 0;i < len;i++)
{
ALuint samp = samples[i];
samples[i] = (samp>>24) | ((samp>>8)&0x0000ff00) |
((samp<<8)&0x00ff0000) | (samp<<24);
}
}
}
else
{
fs = fwrite(self->mBuffer, frameSize, device->UpdateSize,
self->mFile);
(void)fs;
}
fs = fwrite(self->mBuffer, frameSize, device->UpdateSize, self->mFile);
(void)fs;
if(ferror(self->mFile))
{
ERR("Error writing to file\n");
@@ -224,7 +228,7 @@ static ALCenum ALCwaveBackend_open(ALCwaveBackend *self, const ALCchar *name)
}
device = STATIC_CAST(ALCbackend, self)->mDevice;
al_string_copy_cstr(&device->DeviceName, name);
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
@@ -247,7 +251,10 @@ static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self)
clearerr(self->mFile);
if(GetConfigValueBool(NULL, "wave", "bformat", 0))
device->FmtChans = DevFmtBFormat3D;
{
device->FmtChans = DevFmtAmbi3D;
device->AmbiOrder = 1;
}
switch(device->FmtType)
{
@@ -275,20 +282,23 @@ static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self)
case DevFmtX51Rear: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020; break;
case DevFmtX61: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x100 | 0x200 | 0x400; break;
case DevFmtX71: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400; break;
case DevFmtBFormat3D:
case DevFmtAmbi3D:
/* .amb output requires FuMa */
device->AmbiLayout = AmbiLayout_FuMa;
device->AmbiScale = AmbiNorm_FuMa;
isbformat = 1;
chanmask = 0;
break;
}
bits = BytesFromDevFmt(device->FmtType) * 8;
channels = ChannelsFromDevFmt(device->FmtChans);
channels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
fprintf(self->mFile, "RIFF");
fputs("RIFF", self->mFile);
fwrite32le(0xFFFFFFFF, self->mFile); // 'RIFF' header len; filled in at close
fprintf(self->mFile, "WAVE");
fputs("WAVE", self->mFile);
fprintf(self->mFile, "fmt ");
fputs("fmt ", self->mFile);
fwrite32le(40, self->mFile); // 'fmt ' header len; 40 bytes for EXTENSIBLE
// 16-bit val, format type id (extensible: 0xFFFE)
@@ -310,11 +320,12 @@ static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self)
// 32-bit val, channel mask
fwrite32le(chanmask, self->mFile);
// 16 byte GUID, sub-type format
val = fwrite(((bits==32) ? (isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) :
(isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM)), 1, 16, self->mFile);
val = fwrite((device->FmtType == DevFmtFloat) ?
(isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) :
(isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM), 1, 16, self->mFile);
(void)val;
fprintf(self->mFile, "data");
fputs("data", self->mFile);
fwrite32le(0xFFFFFFFF, self->mFile); // 'data' header len; filled in at close
if(ferror(self->mFile))
@@ -333,7 +344,9 @@ static ALCboolean ALCwaveBackend_start(ALCwaveBackend *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
self->mSize = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
self->mSize = device->UpdateSize * FrameSizeFromDevFmt(
device->FmtChans, device->FmtType, device->AmbiOrder
);
self->mBuffer = malloc(self->mSize);
if(!self->mBuffer)
{
+45 -43
View File
@@ -45,8 +45,8 @@ 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);
VECTOR_FOR_EACH(al_string, *list, alstr_reset);
VECTOR_RESIZE(*list, 0, 0);
}
@@ -58,7 +58,7 @@ static void ProbePlaybackDevices(void)
clear_devlist(&PlaybackDevices);
numdevs = waveOutGetNumDevs();
VECTOR_RESERVE(PlaybackDevices, numdevs);
VECTOR_RESIZE(PlaybackDevices, 0, numdevs);
for(i = 0;i < numdevs;i++)
{
WAVEOUTCAPSW WaveCaps;
@@ -71,23 +71,23 @@ static void ProbePlaybackDevices(void)
ALuint count = 0;
while(1)
{
al_string_copy_cstr(&dname, DEVNAME_HEAD);
al_string_append_wcstr(&dname, WaveCaps.szPname);
alstr_copy_cstr(&dname, DEVNAME_HEAD);
alstr_append_wcstr(&dname, WaveCaps.szPname);
if(count != 0)
{
char str[64];
snprintf(str, sizeof(str), " #%d", count+1);
al_string_append_cstr(&dname, str);
alstr_append_cstr(&dname, str);
}
count++;
#define MATCH_ENTRY(i) (al_string_cmp(dname, *(i)) == 0)
#define MATCH_ENTRY(i) (alstr_cmp(dname, *(i)) == 0)
VECTOR_FIND_IF(iter, const al_string, PlaybackDevices, MATCH_ENTRY);
if(iter == VECTOR_ITER_END(PlaybackDevices)) break;
if(iter == VECTOR_END(PlaybackDevices)) break;
#undef MATCH_ENTRY
}
TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i);
TRACE("Got device \"%s\", ID %u\n", alstr_get_cstr(dname), i);
}
VECTOR_PUSH_BACK(PlaybackDevices, dname);
}
@@ -101,7 +101,7 @@ static void ProbeCaptureDevices(void)
clear_devlist(&CaptureDevices);
numdevs = waveInGetNumDevs();
VECTOR_RESERVE(CaptureDevices, numdevs);
VECTOR_RESIZE(CaptureDevices, 0, numdevs);
for(i = 0;i < numdevs;i++)
{
WAVEINCAPSW WaveCaps;
@@ -114,23 +114,23 @@ static void ProbeCaptureDevices(void)
ALuint count = 0;
while(1)
{
al_string_copy_cstr(&dname, DEVNAME_HEAD);
al_string_append_wcstr(&dname, WaveCaps.szPname);
alstr_copy_cstr(&dname, DEVNAME_HEAD);
alstr_append_wcstr(&dname, WaveCaps.szPname);
if(count != 0)
{
char str[64];
snprintf(str, sizeof(str), " #%d", count+1);
al_string_append_cstr(&dname, str);
alstr_append_cstr(&dname, str);
}
count++;
#define MATCH_ENTRY(i) (al_string_cmp(dname, *(i)) == 0)
#define MATCH_ENTRY(i) (alstr_cmp(dname, *(i)) == 0)
VECTOR_FIND_IF(iter, const al_string, CaptureDevices, MATCH_ENTRY);
if(iter == VECTOR_ITER_END(CaptureDevices)) break;
if(iter == VECTOR_END(CaptureDevices)) break;
#undef MATCH_ENTRY
}
TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i);
TRACE("Got device \"%s\", ID %u\n", alstr_get_cstr(dname), i);
}
VECTOR_PUSH_BACK(CaptureDevices, dname);
}
@@ -164,7 +164,7 @@ static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self);
static void ALCwinmmPlayback_stop(ALCwinmmPlayback *self);
static DECLARE_FORWARD2(ALCwinmmPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, ALCuint, availableSamples)
static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, ALint64, getLatency)
static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCwinmmPlayback)
@@ -232,8 +232,10 @@ FORCE_ALIGN static int ALCwinmmPlayback_mixerProc(void *arg)
}
WaveHdr = ((WAVEHDR*)msg.lParam);
ALCwinmmPlayback_lock(self);
aluMixData(device, WaveHdr->lpData, WaveHdr->dwBufferLength /
self->Format.nBlockAlign);
ALCwinmmPlayback_unlock(self);
// Send buffer back to play more data
waveOutWrite(self->OutHdl, WaveHdr, sizeof(WAVEHDR));
@@ -255,14 +257,14 @@ static ALCenum ALCwinmmPlayback_open(ALCwinmmPlayback *self, const ALCchar *devi
ProbePlaybackDevices();
// Find the Device ID matching the deviceName if valid
#define MATCH_DEVNAME(iter) (!al_string_empty(*(iter)) && \
(!deviceName || al_string_cmp_cstr(*(iter), deviceName) == 0))
#define MATCH_DEVNAME(iter) (!alstr_empty(*(iter)) && \
(!deviceName || alstr_cmp_cstr(*(iter), deviceName) == 0))
VECTOR_FIND_IF(iter, const al_string, PlaybackDevices, MATCH_DEVNAME);
if(iter == VECTOR_ITER_END(PlaybackDevices))
if(iter == VECTOR_END(PlaybackDevices))
return ALC_INVALID_VALUE;
#undef MATCH_DEVNAME
DeviceID = (UINT)(iter - VECTOR_ITER_BEGIN(PlaybackDevices));
DeviceID = (UINT)(iter - VECTOR_BEGIN(PlaybackDevices));
retry_open:
memset(&self->Format, 0, sizeof(WAVEFORMATEX));
@@ -298,7 +300,7 @@ retry_open:
goto failure;
}
al_string_copy(&device->DeviceName, VECTOR_ELEM(PlaybackDevices, DeviceID));
alstr_copy(&device->DeviceName, VECTOR_ELEM(PlaybackDevices, DeviceID));
return ALC_NO_ERROR;
failure:
@@ -380,7 +382,7 @@ static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self)
// Create 4 Buffers
BufferSize = device->UpdateSize*device->NumUpdates / 4;
BufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
BufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
BufferData = calloc(4, BufferSize);
for(i = 0;i < 4;i++)
@@ -430,7 +432,7 @@ typedef struct ALCwinmmCapture {
HWAVEIN InHdl;
RingBuffer *Ring;
ll_ringbuffer_t *Ring;
WAVEFORMATEX Format;
@@ -451,7 +453,7 @@ static ALCboolean ALCwinmmCapture_start(ALCwinmmCapture *self);
static void ALCwinmmCapture_stop(ALCwinmmCapture *self);
static ALCenum ALCwinmmCapture_captureSamples(ALCwinmmCapture *self, ALCvoid *buffer, ALCuint samples);
static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self);
static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ALint64, getLatency)
static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCwinmmCapture)
@@ -514,8 +516,9 @@ static int ALCwinmmCapture_captureProc(void *arg)
break;
WaveHdr = ((WAVEHDR*)msg.lParam);
WriteRingBuffer(self->Ring, (ALubyte*)WaveHdr->lpData,
WaveHdr->dwBytesRecorded/self->Format.nBlockAlign);
ll_ringbuffer_write(self->Ring, WaveHdr->lpData,
WaveHdr->dwBytesRecorded / self->Format.nBlockAlign
);
// Send buffer back to capture more data
waveInAddBuffer(self->InHdl, WaveHdr, sizeof(WAVEHDR));
@@ -541,13 +544,13 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
ProbeCaptureDevices();
// Find the Device ID matching the deviceName if valid
#define MATCH_DEVNAME(iter) (!al_string_empty(*(iter)) && (!name || al_string_cmp_cstr(*iter, name) == 0))
#define MATCH_DEVNAME(iter) (!alstr_empty(*(iter)) && (!name || alstr_cmp_cstr(*iter, name) == 0))
VECTOR_FIND_IF(iter, const al_string, CaptureDevices, MATCH_DEVNAME);
if(iter == VECTOR_ITER_END(CaptureDevices))
if(iter == VECTOR_END(CaptureDevices))
return ALC_INVALID_VALUE;
#undef MATCH_DEVNAME
DeviceID = (UINT)(iter - VECTOR_ITER_BEGIN(CaptureDevices));
DeviceID = (UINT)(iter - VECTOR_BEGIN(CaptureDevices));
switch(device->FmtChans)
{
@@ -560,7 +563,7 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
case DevFmtX51Rear:
case DevFmtX61:
case DevFmtX71:
case DevFmtBFormat3D:
case DevFmtAmbi3D:
return ALC_INVALID_ENUM;
}
@@ -581,7 +584,7 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
memset(&self->Format, 0, sizeof(WAVEFORMATEX));
self->Format.wFormatTag = ((device->FmtType == DevFmtFloat) ?
WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM);
self->Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
self->Format.nChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
self->Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
self->Format.nBlockAlign = self->Format.wBitsPerSample *
self->Format.nChannels / 8;
@@ -603,7 +606,7 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
if(CapturedDataSize < (self->Format.nSamplesPerSec / 10))
CapturedDataSize = self->Format.nSamplesPerSec / 10;
self->Ring = CreateRingBuffer(self->Format.nBlockAlign, CapturedDataSize);
self->Ring = ll_ringbuffer_create(CapturedDataSize+1, self->Format.nBlockAlign);
if(!self->Ring) goto failure;
InitRef(&self->WaveBuffersCommitted, 0);
@@ -633,7 +636,7 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
if(althrd_create(&self->thread, ALCwinmmCapture_captureProc, self) != althrd_success)
goto failure;
al_string_copy(&device->DeviceName, VECTOR_ELEM(CaptureDevices, DeviceID));
alstr_copy(&device->DeviceName, VECTOR_ELEM(CaptureDevices, DeviceID));
return ALC_NO_ERROR;
failure:
@@ -644,8 +647,7 @@ failure:
free(BufferData);
}
if(self->Ring)
DestroyRingBuffer(self->Ring);
ll_ringbuffer_free(self->Ring);
self->Ring = NULL;
if(self->InHdl)
@@ -678,7 +680,7 @@ static void ALCwinmmCapture_close(ALCwinmmCapture *self)
}
free(buffer);
DestroyRingBuffer(self->Ring);
ll_ringbuffer_free(self->Ring);
self->Ring = NULL;
// Close the Wave device
@@ -699,25 +701,25 @@ static void ALCwinmmCapture_stop(ALCwinmmCapture *self)
static ALCenum ALCwinmmCapture_captureSamples(ALCwinmmCapture *self, ALCvoid *buffer, ALCuint samples)
{
ReadRingBuffer(self->Ring, buffer, samples);
ll_ringbuffer_read(self->Ring, buffer, samples);
return ALC_NO_ERROR;
}
static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self)
{
return RingBufferSize(self->Ring);
return ll_ringbuffer_read_space(self->Ring);
}
static inline void AppendAllDevicesList2(const al_string *name)
{
if(!al_string_empty(*name))
AppendAllDevicesList(al_string_get_cstr(*name));
if(!alstr_empty(*name))
AppendAllDevicesList(alstr_get_cstr(*name));
}
static inline void AppendCaptureDeviceList2(const al_string *name)
{
if(!al_string_empty(*name))
AppendCaptureDeviceList(al_string_get_cstr(*name));
if(!alstr_empty(*name))
AppendCaptureDeviceList(alstr_get_cstr(*name));
}
typedef struct ALCwinmmBackendFactory {
+612
View File
@@ -0,0 +1,612 @@
#include "config.h"
#include "bformatdec.h"
#include "ambdec.h"
#include "mixer_defs.h"
#include "alu.h"
#include "bool.h"
#include "threads.h"
#include "almalloc.h"
void bandsplit_init(BandSplitter *splitter, ALfloat freq_mult)
{
ALfloat w = freq_mult * F_TAU;
ALfloat cw = cosf(w);
if(cw > FLT_EPSILON)
splitter->coeff = (sinf(w) - 1.0f) / cw;
else
splitter->coeff = cw * -0.5f;
splitter->lp_z1 = 0.0f;
splitter->lp_z2 = 0.0f;
splitter->hp_z1 = 0.0f;
}
void bandsplit_clear(BandSplitter *splitter)
{
splitter->lp_z1 = 0.0f;
splitter->lp_z2 = 0.0f;
splitter->hp_z1 = 0.0f;
}
void bandsplit_process(BandSplitter *splitter, ALfloat *restrict hpout, ALfloat *restrict lpout,
const ALfloat *input, ALsizei count)
{
ALfloat coeff, d, x;
ALfloat z1, z2;
ALsizei i;
coeff = splitter->coeff*0.5f + 0.5f;
z1 = splitter->lp_z1;
z2 = splitter->lp_z2;
for(i = 0;i < count;i++)
{
x = input[i];
d = (x - z1) * coeff;
x = z1 + d;
z1 = x + d;
d = (x - z2) * coeff;
x = z2 + d;
z2 = x + d;
lpout[i] = x;
}
splitter->lp_z1 = z1;
splitter->lp_z2 = z2;
coeff = splitter->coeff;
z1 = splitter->hp_z1;
for(i = 0;i < count;i++)
{
x = input[i];
d = x - coeff*z1;
x = z1 + coeff*d;
z1 = d;
hpout[i] = x - lpout[i];
}
splitter->hp_z1 = z1;
}
void splitterap_init(SplitterAllpass *splitter, ALfloat freq_mult)
{
ALfloat w = freq_mult * F_TAU;
ALfloat cw = cosf(w);
if(cw > FLT_EPSILON)
splitter->coeff = (sinf(w) - 1.0f) / cw;
else
splitter->coeff = cw * -0.5f;
splitter->z1 = 0.0f;
}
void splitterap_clear(SplitterAllpass *splitter)
{
splitter->z1 = 0.0f;
}
void splitterap_process(SplitterAllpass *splitter, ALfloat *restrict samples, ALsizei count)
{
ALfloat coeff, d, x;
ALfloat z1;
ALsizei i;
coeff = splitter->coeff;
z1 = splitter->z1;
for(i = 0;i < count;i++)
{
x = samples[i];
d = x - coeff*z1;
x = z1 + coeff*d;
z1 = d;
samples[i] = x;
}
splitter->z1 = z1;
}
static const ALfloat UnitScale[MAX_AMBI_COEFFS] = {
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f
};
static const ALfloat SN3D2N3DScale[MAX_AMBI_COEFFS] = {
1.000000000f, /* ACN 0 (W), sqrt(1) */
1.732050808f, /* ACN 1 (Y), sqrt(3) */
1.732050808f, /* ACN 2 (Z), sqrt(3) */
1.732050808f, /* ACN 3 (X), sqrt(3) */
2.236067978f, /* ACN 4 (V), sqrt(5) */
2.236067978f, /* ACN 5 (T), sqrt(5) */
2.236067978f, /* ACN 6 (R), sqrt(5) */
2.236067978f, /* ACN 7 (S), sqrt(5) */
2.236067978f, /* ACN 8 (U), sqrt(5) */
2.645751311f, /* ACN 9 (Q), sqrt(7) */
2.645751311f, /* ACN 10 (O), sqrt(7) */
2.645751311f, /* ACN 11 (M), sqrt(7) */
2.645751311f, /* ACN 12 (K), sqrt(7) */
2.645751311f, /* ACN 13 (L), sqrt(7) */
2.645751311f, /* ACN 14 (N), sqrt(7) */
2.645751311f, /* ACN 15 (P), sqrt(7) */
};
static const ALfloat FuMa2N3DScale[MAX_AMBI_COEFFS] = {
1.414213562f, /* ACN 0 (W), sqrt(2) */
1.732050808f, /* ACN 1 (Y), sqrt(3) */
1.732050808f, /* ACN 2 (Z), sqrt(3) */
1.732050808f, /* ACN 3 (X), sqrt(3) */
1.936491673f, /* ACN 4 (V), sqrt(15)/2 */
1.936491673f, /* ACN 5 (T), sqrt(15)/2 */
2.236067978f, /* ACN 6 (R), sqrt(5) */
1.936491673f, /* ACN 7 (S), sqrt(15)/2 */
1.936491673f, /* ACN 8 (U), sqrt(15)/2 */
2.091650066f, /* ACN 9 (Q), sqrt(35/8) */
1.972026594f, /* ACN 10 (O), sqrt(35)/3 */
2.231093404f, /* ACN 11 (M), sqrt(224/45) */
2.645751311f, /* ACN 12 (K), sqrt(7) */
2.231093404f, /* ACN 13 (L), sqrt(224/45) */
1.972026594f, /* ACN 14 (N), sqrt(35)/3 */
2.091650066f, /* ACN 15 (P), sqrt(35/8) */
};
enum FreqBand {
FB_HighFreq,
FB_LowFreq,
FB_Max
};
/* These points are in AL coordinates! */
static const ALfloat Ambi3DPoints[8][3] = {
{ -0.577350269f, 0.577350269f, -0.577350269f },
{ 0.577350269f, 0.577350269f, -0.577350269f },
{ -0.577350269f, 0.577350269f, 0.577350269f },
{ 0.577350269f, 0.577350269f, 0.577350269f },
{ -0.577350269f, -0.577350269f, -0.577350269f },
{ 0.577350269f, -0.577350269f, -0.577350269f },
{ -0.577350269f, -0.577350269f, 0.577350269f },
{ 0.577350269f, -0.577350269f, 0.577350269f },
};
static const ALfloat Ambi3DDecoder[8][FB_Max][MAX_AMBI_COEFFS] = {
{ { 0.25f, 0.1443375672f, 0.1443375672f, 0.1443375672f }, { 0.125f, 0.125f, 0.125f, 0.125f } },
{ { 0.25f, -0.1443375672f, 0.1443375672f, 0.1443375672f }, { 0.125f, -0.125f, 0.125f, 0.125f } },
{ { 0.25f, 0.1443375672f, 0.1443375672f, -0.1443375672f }, { 0.125f, 0.125f, 0.125f, -0.125f } },
{ { 0.25f, -0.1443375672f, 0.1443375672f, -0.1443375672f }, { 0.125f, -0.125f, 0.125f, -0.125f } },
{ { 0.25f, 0.1443375672f, -0.1443375672f, 0.1443375672f }, { 0.125f, 0.125f, -0.125f, 0.125f } },
{ { 0.25f, -0.1443375672f, -0.1443375672f, 0.1443375672f }, { 0.125f, -0.125f, -0.125f, 0.125f } },
{ { 0.25f, 0.1443375672f, -0.1443375672f, -0.1443375672f }, { 0.125f, 0.125f, -0.125f, -0.125f } },
{ { 0.25f, -0.1443375672f, -0.1443375672f, -0.1443375672f }, { 0.125f, -0.125f, -0.125f, -0.125f } },
};
static RowMixerFunc MixMatrixRow = MixRow_C;
static alonce_flag bformatdec_inited = AL_ONCE_FLAG_INIT;
static void init_bformatdec(void)
{
MixMatrixRow = SelectRowMixer();
}
/* NOTE: BandSplitter filters are unused with single-band decoding */
typedef struct BFormatDec {
ALboolean Enabled[MAX_OUTPUT_CHANNELS];
union {
alignas(16) ALfloat Dual[MAX_OUTPUT_CHANNELS][FB_Max][MAX_AMBI_COEFFS];
alignas(16) ALfloat Single[MAX_OUTPUT_CHANNELS][MAX_AMBI_COEFFS];
} Matrix;
BandSplitter XOver[MAX_AMBI_COEFFS];
ALfloat (*Samples)[BUFFERSIZE];
/* These two alias into Samples */
ALfloat (*SamplesHF)[BUFFERSIZE];
ALfloat (*SamplesLF)[BUFFERSIZE];
alignas(16) ALfloat ChannelMix[BUFFERSIZE];
struct {
BandSplitter XOver;
ALfloat Gains[FB_Max];
} UpSampler[4];
ALsizei NumChannels;
ALboolean DualBand;
} BFormatDec;
BFormatDec *bformatdec_alloc()
{
alcall_once(&bformatdec_inited, init_bformatdec);
return al_calloc(16, sizeof(BFormatDec));
}
void bformatdec_free(BFormatDec *dec)
{
if(dec)
{
al_free(dec->Samples);
dec->Samples = NULL;
dec->SamplesHF = NULL;
dec->SamplesLF = NULL;
memset(dec, 0, sizeof(*dec));
al_free(dec);
}
}
void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALsizei chancount, ALuint srate, const ALsizei chanmap[MAX_OUTPUT_CHANNELS])
{
static const ALsizei map2DTo3D[MAX_AMBI2D_COEFFS] = {
0, 1, 3, 4, 8, 9, 15
};
const ALfloat *coeff_scale = UnitScale;
bool periphonic;
ALfloat ratio;
ALsizei i;
al_free(dec->Samples);
dec->Samples = NULL;
dec->SamplesHF = NULL;
dec->SamplesLF = NULL;
dec->NumChannels = chancount;
dec->Samples = al_calloc(16, dec->NumChannels*2 * sizeof(dec->Samples[0]));
dec->SamplesHF = dec->Samples;
dec->SamplesLF = dec->SamplesHF + dec->NumChannels;
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
dec->Enabled[i] = AL_FALSE;
for(i = 0;i < conf->NumSpeakers;i++)
dec->Enabled[chanmap[i]] = AL_TRUE;
if(conf->CoeffScale == ADS_SN3D)
coeff_scale = SN3D2N3DScale;
else if(conf->CoeffScale == ADS_FuMa)
coeff_scale = FuMa2N3DScale;
memset(dec->UpSampler, 0, sizeof(dec->UpSampler));
ratio = 400.0f / (ALfloat)srate;
for(i = 0;i < 4;i++)
bandsplit_init(&dec->UpSampler[i].XOver, ratio);
if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
{
periphonic = true;
dec->UpSampler[0].Gains[FB_HighFreq] = (dec->NumChannels > 9) ? W_SCALE3D_THIRD :
(dec->NumChannels > 4) ? W_SCALE3D_SECOND : 1.0f;
dec->UpSampler[0].Gains[FB_LowFreq] = 1.0f;
for(i = 1;i < 4;i++)
{
dec->UpSampler[i].Gains[FB_HighFreq] = (dec->NumChannels > 9) ? XYZ_SCALE3D_THIRD :
(dec->NumChannels > 4) ? XYZ_SCALE3D_SECOND : 1.0f;
dec->UpSampler[i].Gains[FB_LowFreq] = 1.0f;
}
}
else
{
periphonic = false;
dec->UpSampler[0].Gains[FB_HighFreq] = (dec->NumChannels > 5) ? W_SCALE2D_THIRD :
(dec->NumChannels > 3) ? W_SCALE2D_SECOND : 1.0f;
dec->UpSampler[0].Gains[FB_LowFreq] = 1.0f;
for(i = 1;i < 3;i++)
{
dec->UpSampler[i].Gains[FB_HighFreq] = (dec->NumChannels > 5) ? XYZ_SCALE2D_THIRD :
(dec->NumChannels > 3) ? XYZ_SCALE2D_SECOND : 1.0f;
dec->UpSampler[i].Gains[FB_LowFreq] = 1.0f;
}
dec->UpSampler[3].Gains[FB_HighFreq] = 0.0f;
dec->UpSampler[3].Gains[FB_LowFreq] = 0.0f;
}
memset(&dec->Matrix, 0, sizeof(dec->Matrix));
if(conf->FreqBands == 1)
{
dec->DualBand = AL_FALSE;
for(i = 0;i < conf->NumSpeakers;i++)
{
ALsizei chan = chanmap[i];
ALfloat gain;
ALsizei j, k;
if(!periphonic)
{
for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++)
{
ALsizei l = map2DTo3D[j];
if(j == 0) gain = conf->HFOrderGain[0];
else if(j == 1) gain = conf->HFOrderGain[1];
else if(j == 3) gain = conf->HFOrderGain[2];
else if(j == 5) gain = conf->HFOrderGain[3];
if((conf->ChanMask&(1<<l)))
dec->Matrix.Single[chan][j] = conf->HFMatrix[i][k++] / coeff_scale[l] *
gain;
}
}
else
{
for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++)
{
if(j == 0) gain = conf->HFOrderGain[0];
else if(j == 1) gain = conf->HFOrderGain[1];
else if(j == 4) gain = conf->HFOrderGain[2];
else if(j == 9) gain = conf->HFOrderGain[3];
if((conf->ChanMask&(1<<j)))
dec->Matrix.Single[chan][j] = conf->HFMatrix[i][k++] / coeff_scale[j] *
gain;
}
}
}
}
else
{
dec->DualBand = AL_TRUE;
ratio = conf->XOverFreq / (ALfloat)srate;
for(i = 0;i < MAX_AMBI_COEFFS;i++)
bandsplit_init(&dec->XOver[i], ratio);
ratio = powf(10.0f, conf->XOverRatio / 40.0f);
for(i = 0;i < conf->NumSpeakers;i++)
{
ALsizei chan = chanmap[i];
ALfloat gain;
ALsizei j, k;
if(!periphonic)
{
for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++)
{
ALsizei l = map2DTo3D[j];
if(j == 0) gain = conf->HFOrderGain[0] * ratio;
else if(j == 1) gain = conf->HFOrderGain[1] * ratio;
else if(j == 3) gain = conf->HFOrderGain[2] * ratio;
else if(j == 5) gain = conf->HFOrderGain[3] * ratio;
if((conf->ChanMask&(1<<l)))
dec->Matrix.Dual[chan][FB_HighFreq][j] = conf->HFMatrix[i][k++] /
coeff_scale[l] * gain;
}
for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++)
{
ALsizei l = map2DTo3D[j];
if(j == 0) gain = conf->LFOrderGain[0] / ratio;
else if(j == 1) gain = conf->LFOrderGain[1] / ratio;
else if(j == 3) gain = conf->LFOrderGain[2] / ratio;
else if(j == 5) gain = conf->LFOrderGain[3] / ratio;
if((conf->ChanMask&(1<<l)))
dec->Matrix.Dual[chan][FB_LowFreq][j] = conf->LFMatrix[i][k++] /
coeff_scale[l] * gain;
}
}
else
{
for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++)
{
if(j == 0) gain = conf->HFOrderGain[0] * ratio;
else if(j == 1) gain = conf->HFOrderGain[1] * ratio;
else if(j == 4) gain = conf->HFOrderGain[2] * ratio;
else if(j == 9) gain = conf->HFOrderGain[3] * ratio;
if((conf->ChanMask&(1<<j)))
dec->Matrix.Dual[chan][FB_HighFreq][j] = conf->HFMatrix[i][k++] /
coeff_scale[j] * gain;
}
for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++)
{
if(j == 0) gain = conf->LFOrderGain[0] / ratio;
else if(j == 1) gain = conf->LFOrderGain[1] / ratio;
else if(j == 4) gain = conf->LFOrderGain[2] / ratio;
else if(j == 9) gain = conf->LFOrderGain[3] / ratio;
if((conf->ChanMask&(1<<j)))
dec->Matrix.Dual[chan][FB_LowFreq][j] = conf->LFMatrix[i][k++] /
coeff_scale[j] * gain;
}
}
}
}
}
void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo)
{
ALsizei chan, i;
OutBuffer = ASSUME_ALIGNED(OutBuffer, 16);
if(dec->DualBand)
{
for(i = 0;i < dec->NumChannels;i++)
bandsplit_process(&dec->XOver[i], dec->SamplesHF[i], dec->SamplesLF[i],
InSamples[i], SamplesToDo);
for(chan = 0;chan < OutChannels;chan++)
{
if(!dec->Enabled[chan])
continue;
memset(dec->ChannelMix, 0, SamplesToDo*sizeof(ALfloat));
MixMatrixRow(dec->ChannelMix, dec->Matrix.Dual[chan][FB_HighFreq],
SAFE_CONST(ALfloatBUFFERSIZE*,dec->SamplesHF), dec->NumChannels, 0,
SamplesToDo
);
MixMatrixRow(dec->ChannelMix, dec->Matrix.Dual[chan][FB_LowFreq],
SAFE_CONST(ALfloatBUFFERSIZE*,dec->SamplesLF), dec->NumChannels, 0,
SamplesToDo
);
for(i = 0;i < SamplesToDo;i++)
OutBuffer[chan][i] += dec->ChannelMix[i];
}
}
else
{
for(chan = 0;chan < OutChannels;chan++)
{
if(!dec->Enabled[chan])
continue;
memset(dec->ChannelMix, 0, SamplesToDo*sizeof(ALfloat));
MixMatrixRow(dec->ChannelMix, dec->Matrix.Single[chan], InSamples,
dec->NumChannels, 0, SamplesToDo);
for(i = 0;i < SamplesToDo;i++)
OutBuffer[chan][i] += dec->ChannelMix[i];
}
}
}
void bformatdec_upSample(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei InChannels, ALsizei SamplesToDo)
{
ALsizei i;
/* This up-sampler leverages the differences observed in dual-band second-
* and third-order decoder matrices compared to first-order. For the same
* output channel configuration, the low-frequency matrix has identical
* coefficients in the shared input channels, while the high-frequency
* matrix has extra scalars applied to the W channel and X/Y/Z channels.
* Mixing the first-order content into the higher-order stream with the
* appropriate counter-scales applied to the HF response results in the
* subsequent higher-order decode generating the same response as a first-
* order decode.
*/
for(i = 0;i < InChannels;i++)
{
/* First, split the first-order components into low and high frequency
* bands.
*/
bandsplit_process(&dec->UpSampler[i].XOver,
dec->Samples[FB_HighFreq], dec->Samples[FB_LowFreq],
InSamples[i], SamplesToDo
);
/* Now write each band to the output. */
MixMatrixRow(OutBuffer[i], dec->UpSampler[i].Gains,
SAFE_CONST(ALfloatBUFFERSIZE*,dec->Samples), FB_Max, 0,
SamplesToDo
);
}
}
#define INVALID_UPSAMPLE_INDEX INT_MAX
static ALsizei GetACNIndex(const BFChannelConfig *chans, ALsizei numchans, ALsizei acn)
{
ALsizei i;
for(i = 0;i < numchans;i++)
{
if(chans[i].Index == acn)
return i;
}
return INVALID_UPSAMPLE_INDEX;
}
#define GetChannelForACN(b, a) GetACNIndex((b).Ambi.Map, (b).NumChannels, (a))
typedef struct AmbiUpsampler {
alignas(16) ALfloat Samples[FB_Max][BUFFERSIZE];
BandSplitter XOver[4];
ALfloat Gains[4][MAX_OUTPUT_CHANNELS][FB_Max];
} AmbiUpsampler;
AmbiUpsampler *ambiup_alloc()
{
alcall_once(&bformatdec_inited, init_bformatdec);
return al_calloc(16, sizeof(AmbiUpsampler));
}
void ambiup_free(struct AmbiUpsampler *ambiup)
{
al_free(ambiup);
}
void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device)
{
ALfloat ratio;
size_t i;
ratio = 400.0f / (ALfloat)device->Frequency;
for(i = 0;i < 4;i++)
bandsplit_init(&ambiup->XOver[i], ratio);
memset(ambiup->Gains, 0, sizeof(ambiup->Gains));
if(device->Dry.CoeffCount > 0)
{
ALfloat encgains[8][MAX_OUTPUT_CHANNELS];
ALsizei j;
size_t k;
for(i = 0;i < COUNTOF(Ambi3DPoints);i++)
{
ALfloat coeffs[MAX_AMBI_COEFFS] = { 0.0f };
CalcDirectionCoeffs(Ambi3DPoints[i], 0.0f, coeffs);
ComputePanningGains(device->Dry, coeffs, 1.0f, encgains[i]);
}
/* Combine the matrices that do the in->virt and virt->out conversions
* so we get a single in->out conversion. NOTE: the Encoder matrix
* (encgains) and output are transposed, so the input channels line up
* with the rows and the output channels line up with the columns.
*/
for(i = 0;i < 4;i++)
{
for(j = 0;j < device->Dry.NumChannels;j++)
{
ALfloat hfgain=0.0f, lfgain=0.0f;
for(k = 0;k < COUNTOF(Ambi3DDecoder);k++)
{
hfgain += Ambi3DDecoder[k][FB_HighFreq][i]*encgains[k][j];
lfgain += Ambi3DDecoder[k][FB_LowFreq][i]*encgains[k][j];
}
ambiup->Gains[i][j][FB_HighFreq] = hfgain;
ambiup->Gains[i][j][FB_LowFreq] = lfgain;
}
}
}
else
{
/* Assumes full 3D/periphonic on the input and output mixes! */
ALfloat w_scale = (device->Dry.NumChannels > 9) ? W_SCALE3D_THIRD :
(device->Dry.NumChannels > 4) ? W_SCALE3D_SECOND : 1.0f;
ALfloat xyz_scale = (device->Dry.NumChannels > 9) ? XYZ_SCALE3D_THIRD :
(device->Dry.NumChannels > 4) ? XYZ_SCALE3D_SECOND : 1.0f;
for(i = 0;i < 4;i++)
{
ALsizei index = GetChannelForACN(device->Dry, i);
if(index != INVALID_UPSAMPLE_INDEX)
{
ALfloat scale = device->Dry.Ambi.Map[index].Scale;
ambiup->Gains[i][index][FB_HighFreq] = scale * ((i==0) ? w_scale : xyz_scale);
ambiup->Gains[i][index][FB_LowFreq] = scale;
}
}
}
}
void ambiup_process(struct AmbiUpsampler *ambiup, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo)
{
ALsizei i, j;
for(i = 0;i < 4;i++)
{
bandsplit_process(&ambiup->XOver[i],
ambiup->Samples[FB_HighFreq], ambiup->Samples[FB_LowFreq],
InSamples[i], SamplesToDo
);
for(j = 0;j < OutChannels;j++)
MixMatrixRow(OutBuffer[j], ambiup->Gains[i][j],
SAFE_CONST(ALfloatBUFFERSIZE*,ambiup->Samples), FB_Max, 0,
SamplesToDo
);
}
}
+75
View File
@@ -0,0 +1,75 @@
#ifndef BFORMATDEC_H
#define BFORMATDEC_H
#include "alMain.h"
/* These are the necessary scales for first-order HF responses to play over
* higher-order 2D (non-periphonic) decoders.
*/
#define W_SCALE2D_SECOND 1.224744871f /* sqrt(1.5) */
#define XYZ_SCALE2D_SECOND 1.0f
#define W_SCALE2D_THIRD 1.414213562f /* sqrt(2) */
#define XYZ_SCALE2D_THIRD 1.082392196f
/* These are the necessary scales for first-order HF responses to play over
* higher-order 3D (periphonic) decoders.
*/
#define W_SCALE3D_SECOND 1.341640787f /* sqrt(1.8) */
#define XYZ_SCALE3D_SECOND 1.0f
#define W_SCALE3D_THIRD 1.695486018f
#define XYZ_SCALE3D_THIRD 1.136697713f
struct AmbDecConf;
struct BFormatDec;
struct AmbiUpsampler;
struct BFormatDec *bformatdec_alloc();
void bformatdec_free(struct BFormatDec *dec);
void bformatdec_reset(struct BFormatDec *dec, const struct AmbDecConf *conf, ALsizei chancount, ALuint srate, const ALsizei chanmap[MAX_OUTPUT_CHANNELS]);
/* Decodes the ambisonic input to the given output channels. */
void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo);
/* Up-samples a first-order input to the decoder's configuration. */
void bformatdec_upSample(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei InChannels, ALsizei SamplesToDo);
/* Stand-alone first-order upsampler. Kept here because it shares some stuff
* with bformatdec.
*/
struct AmbiUpsampler *ambiup_alloc();
void ambiup_free(struct AmbiUpsampler *ambiup);
void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device);
void ambiup_process(struct AmbiUpsampler *ambiup, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo);
/* Band splitter. Splits a signal into two phase-matching frequency bands. */
typedef struct BandSplitter {
ALfloat coeff;
ALfloat lp_z1;
ALfloat lp_z2;
ALfloat hp_z1;
} BandSplitter;
void bandsplit_init(BandSplitter *splitter, ALfloat freq_mult);
void bandsplit_clear(BandSplitter *splitter);
void bandsplit_process(BandSplitter *splitter, ALfloat *restrict hpout, ALfloat *restrict lpout,
const ALfloat *input, ALsizei count);
/* The all-pass portion of the band splitter. Applies the same phase shift
* without splitting the signal.
*/
typedef struct SplitterAllpass {
ALfloat coeff;
ALfloat z1;
} SplitterAllpass;
void splitterap_init(SplitterAllpass *splitter, ALfloat freq_mult);
void splitterap_clear(SplitterAllpass *splitter);
void splitterap_process(SplitterAllpass *splitter, ALfloat *restrict samples, ALsizei count);
#endif /* BFORMATDEC_H */
+56 -1
View File
@@ -129,4 +129,59 @@ 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);
void bs2b_cross_feed(struct bs2b *bs2b, float *restrict Left, float *restrict Right, int SamplesToDo)
{
float lsamples[128][2];
float rsamples[128][2];
int base;
for(base = 0;base < SamplesToDo;)
{
int todo = mini(128, SamplesToDo-base);
int i;
/* Process left input */
lsamples[0][0] = bs2b->a0_lo*Left[0] +
bs2b->b1_lo*bs2b->last_sample[0].lo;
lsamples[0][1] = bs2b->a0_hi*Left[0] +
bs2b->a1_hi*bs2b->last_sample[0].asis +
bs2b->b1_hi*bs2b->last_sample[0].hi;
for(i = 1;i < todo;i++)
{
lsamples[i][0] = bs2b->a0_lo*Left[i] +
bs2b->b1_lo*lsamples[i-1][0];
lsamples[i][1] = bs2b->a0_hi*Left[i] +
bs2b->a1_hi*Left[i-1] +
bs2b->b1_hi*lsamples[i-1][1];
}
bs2b->last_sample[0].asis = Left[i-1];
bs2b->last_sample[0].lo = lsamples[i-1][0];
bs2b->last_sample[0].hi = lsamples[i-1][1];
/* Process right input */
rsamples[0][0] = bs2b->a0_lo*Right[0] +
bs2b->b1_lo*bs2b->last_sample[1].lo;
rsamples[0][1] = bs2b->a0_hi*Right[0] +
bs2b->a1_hi*bs2b->last_sample[1].asis +
bs2b->b1_hi*bs2b->last_sample[1].hi;
for(i = 1;i < todo;i++)
{
rsamples[i][0] = bs2b->a0_lo*Right[i] +
bs2b->b1_lo*rsamples[i-1][0];
rsamples[i][1] = bs2b->a0_hi*Right[i] +
bs2b->a1_hi*Right[i-1] +
bs2b->b1_hi*rsamples[i-1][1];
}
bs2b->last_sample[1].asis = Right[i-1];
bs2b->last_sample[1].lo = rsamples[i-1][0];
bs2b->last_sample[1].hi = rsamples[i-1][1];
/* Crossfeed */
for(i = 0;i < todo;i++)
*(Left++) = lsamples[i][1] + rsamples[i][0];
for(i = 0;i < todo;i++)
*(Right++) = rsamples[i][1] + lsamples[i][0];
base += todo;
}
} /* bs2b_cross_feed */
+4100
View File
File diff suppressed because it is too large Load Diff
+25
View File
@@ -1,6 +1,8 @@
#ifndef AL_COMPAT_H
#define AL_COMPAT_H
#include "alstring.h"
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
@@ -23,10 +25,33 @@ FILE *al_fopen(const char *fname, const char *mode);
#endif
struct FileMapping {
#ifdef _WIN32
HANDLE file;
HANDLE fmap;
#else
int fd;
#endif
void *ptr;
size_t len;
};
struct FileMapping MapFileToMem(const char *fname);
void UnmapFileMem(const struct FileMapping *mapping);
al_string GetProcPath(void);
#ifdef HAVE_DYNLOAD
void *LoadLib(const char *name);
void CloseLib(void *handle);
void *GetSymbol(void *handle, const char *name);
#endif
#ifdef __ANDROID__
#define JCALL(obj, func) ((*(obj))->func((obj), EXTRACT_VCALL_ARGS
#define JCALL0(obj, func) ((*(obj))->func((obj) EXTRACT_VCALL_ARGS
/** Returns a JNIEnv*. */
void *Android_GetJNIEnv(void);
#endif
#endif /* AL_COMPAT_H */
+466
View File
@@ -0,0 +1,466 @@
#include "config.h"
#include "converter.h"
#include "mixer_defs.h"
SampleConverter *CreateSampleConverter(enum DevFmtType srcType, enum DevFmtType dstType, ALsizei numchans, ALsizei srcRate, ALsizei dstRate)
{
SampleConverter *converter;
ALsizei step;
if(numchans <= 0 || srcRate <= 0 || dstRate <= 0)
return NULL;
converter = al_calloc(16, FAM_SIZE(SampleConverter, Chan, numchans));
converter->mSrcType = srcType;
converter->mDstType = dstType;
converter->mNumChannels = numchans;
converter->mSrcTypeSize = BytesFromDevFmt(srcType);
converter->mDstTypeSize = BytesFromDevFmt(dstType);
converter->mSrcPrepCount = 0;
converter->mFracOffset = 0;
/* Have to set the mixer FPU mode since that's what the resampler code expects. */
START_MIXER_MODE();
step = fastf2i(minf((ALdouble)srcRate / dstRate, MAX_PITCH)*FRACTIONONE + 0.5f);
converter->mIncrement = maxi(step, 1);
if(converter->mIncrement == FRACTIONONE)
converter->mResample = Resample_copy32_C;
else
{
/* TODO: Allow other resamplers. */
BsincPrepare(converter->mIncrement, &converter->mState.bsinc);
converter->mResample = SelectResampler(BSincResampler);
}
END_MIXER_MODE();
return converter;
}
void DestroySampleConverter(SampleConverter **converter)
{
if(converter)
{
al_free(*converter);
*converter = NULL;
}
}
static inline ALfloat Sample_ALbyte(ALbyte val)
{ return val * (1.0f/128.0f); }
static inline ALfloat Sample_ALubyte(ALubyte val)
{ return Sample_ALbyte((ALint)val - 128); }
static inline ALfloat Sample_ALshort(ALshort val)
{ return val * (1.0f/32768.0f); }
static inline ALfloat Sample_ALushort(ALushort val)
{ return Sample_ALshort((ALint)val - 32768); }
static inline ALfloat Sample_ALint(ALint val)
{ return (val>>7) * (1.0f/16777216.0f); }
static inline ALfloat Sample_ALuint(ALuint val)
{ return Sample_ALint(val - INT_MAX - 1); }
static inline ALfloat Sample_ALfloat(ALfloat val)
{ return val; }
#define DECL_TEMPLATE(T) \
static inline void Load_##T(ALfloat *restrict dst, const T *restrict src, \
ALint srcstep, ALsizei samples) \
{ \
ALsizei i; \
for(i = 0;i < samples;i++) \
dst[i] = Sample_##T(src[i*srcstep]); \
}
DECL_TEMPLATE(ALbyte)
DECL_TEMPLATE(ALubyte)
DECL_TEMPLATE(ALshort)
DECL_TEMPLATE(ALushort)
DECL_TEMPLATE(ALint)
DECL_TEMPLATE(ALuint)
DECL_TEMPLATE(ALfloat)
#undef DECL_TEMPLATE
static void LoadSamples(ALfloat *dst, const ALvoid *src, ALint srcstep, enum DevFmtType srctype, ALsizei samples)
{
switch(srctype)
{
case DevFmtByte:
Load_ALbyte(dst, src, srcstep, samples);
break;
case DevFmtUByte:
Load_ALubyte(dst, src, srcstep, samples);
break;
case DevFmtShort:
Load_ALshort(dst, src, srcstep, samples);
break;
case DevFmtUShort:
Load_ALushort(dst, src, srcstep, samples);
break;
case DevFmtInt:
Load_ALint(dst, src, srcstep, samples);
break;
case DevFmtUInt:
Load_ALuint(dst, src, srcstep, samples);
break;
case DevFmtFloat:
Load_ALfloat(dst, src, srcstep, samples);
break;
}
}
static inline ALbyte ALbyte_Sample(ALfloat val)
{ return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
static inline ALubyte ALubyte_Sample(ALfloat val)
{ return ALbyte_Sample(val)+128; }
static inline ALshort ALshort_Sample(ALfloat val)
{ return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
static inline ALushort ALushort_Sample(ALfloat val)
{ return ALshort_Sample(val)+32768; }
static inline ALint ALint_Sample(ALfloat val)
{ return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f)) << 7; }
static inline ALuint ALuint_Sample(ALfloat val)
{ return ALint_Sample(val)+INT_MAX+1; }
static inline ALfloat ALfloat_Sample(ALfloat val)
{ return val; }
#define DECL_TEMPLATE(T) \
static inline void Store_##T(T *restrict dst, const ALfloat *restrict src, \
ALint dststep, ALsizei samples) \
{ \
ALsizei i; \
for(i = 0;i < samples;i++) \
dst[i*dststep] = T##_Sample(src[i]); \
}
DECL_TEMPLATE(ALbyte)
DECL_TEMPLATE(ALubyte)
DECL_TEMPLATE(ALshort)
DECL_TEMPLATE(ALushort)
DECL_TEMPLATE(ALint)
DECL_TEMPLATE(ALuint)
DECL_TEMPLATE(ALfloat)
#undef DECL_TEMPLATE
static void StoreSamples(ALvoid *dst, const ALfloat *src, ALint dststep, enum DevFmtType dsttype, ALsizei samples)
{
switch(dsttype)
{
case DevFmtByte:
Store_ALbyte(dst, src, dststep, samples);
break;
case DevFmtUByte:
Store_ALubyte(dst, src, dststep, samples);
break;
case DevFmtShort:
Store_ALshort(dst, src, dststep, samples);
break;
case DevFmtUShort:
Store_ALushort(dst, src, dststep, samples);
break;
case DevFmtInt:
Store_ALint(dst, src, dststep, samples);
break;
case DevFmtUInt:
Store_ALuint(dst, src, dststep, samples);
break;
case DevFmtFloat:
Store_ALfloat(dst, src, dststep, samples);
break;
}
}
ALsizei SampleConverterAvailableOut(SampleConverter *converter, ALsizei srcframes)
{
ALint prepcount = converter->mSrcPrepCount;
ALsizei increment = converter->mIncrement;
ALsizei DataPosFrac = converter->mFracOffset;
ALuint64 DataSize64;
if(prepcount < 0)
{
/* Negative prepcount means we need to skip that many input samples. */
if(-prepcount >= srcframes)
return 0;
srcframes += prepcount;
prepcount = 0;
}
if(srcframes < 1)
{
/* No output samples if there's no input samples. */
return 0;
}
if(prepcount < MAX_POST_SAMPLES+MAX_PRE_SAMPLES &&
MAX_POST_SAMPLES+MAX_PRE_SAMPLES-prepcount >= srcframes)
{
/* Not enough input samples to generate an output sample. */
return 0;
}
DataSize64 = prepcount;
DataSize64 += srcframes;
DataSize64 -= MAX_POST_SAMPLES+MAX_PRE_SAMPLES;
DataSize64 <<= FRACTIONBITS;
DataSize64 -= DataPosFrac;
/* If we have a full prep, we can generate at least one sample. */
return (ALsizei)clampu64((DataSize64 + increment-1)/increment, 1, BUFFERSIZE);
}
ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALsizei *srcframes, ALvoid *dst, ALsizei dstframes)
{
const ALsizei SrcFrameSize = converter->mNumChannels * converter->mSrcTypeSize;
const ALsizei DstFrameSize = converter->mNumChannels * converter->mDstTypeSize;
const ALsizei increment = converter->mIncrement;
ALsizei pos = 0;
START_MIXER_MODE();
while(pos < dstframes && *srcframes > 0)
{
ALfloat *restrict SrcData = ASSUME_ALIGNED(converter->mSrcSamples, 16);
ALfloat *restrict DstData = ASSUME_ALIGNED(converter->mDstSamples, 16);
ALint prepcount = converter->mSrcPrepCount;
ALsizei DataPosFrac = converter->mFracOffset;
ALuint64 DataSize64;
ALsizei DstSize;
ALint toread;
ALsizei chan;
if(prepcount < 0)
{
/* Negative prepcount means we need to skip that many input samples. */
if(-prepcount >= *srcframes)
{
converter->mSrcPrepCount = prepcount + *srcframes;
*srcframes = 0;
break;
}
*src = (const ALbyte*)*src + SrcFrameSize*-prepcount;
*srcframes += prepcount;
converter->mSrcPrepCount = 0;
continue;
}
toread = mini(*srcframes, BUFFERSIZE-(MAX_POST_SAMPLES+MAX_PRE_SAMPLES));
if(prepcount < MAX_POST_SAMPLES+MAX_PRE_SAMPLES &&
MAX_POST_SAMPLES+MAX_PRE_SAMPLES-prepcount >= toread)
{
/* Not enough input samples to generate an output sample. Store
* what we're given for later.
*/
for(chan = 0;chan < converter->mNumChannels;chan++)
LoadSamples(&converter->Chan[chan].mPrevSamples[prepcount],
(const ALbyte*)*src + converter->mSrcTypeSize*chan,
converter->mNumChannels, converter->mSrcType, toread
);
converter->mSrcPrepCount = prepcount + toread;
*srcframes = 0;
break;
}
DataSize64 = prepcount;
DataSize64 += toread;
DataSize64 -= MAX_POST_SAMPLES+MAX_PRE_SAMPLES;
DataSize64 <<= FRACTIONBITS;
DataSize64 -= DataPosFrac;
/* If we have a full prep, we can generate at least one sample. */
DstSize = (ALsizei)clampu64((DataSize64 + increment-1)/increment, 1, BUFFERSIZE);
DstSize = mini(DstSize, dstframes-pos);
for(chan = 0;chan < converter->mNumChannels;chan++)
{
const ALbyte *SrcSamples = (const ALbyte*)*src + converter->mSrcTypeSize*chan;
ALbyte *DstSamples = (ALbyte*)dst + converter->mDstTypeSize*chan;
const ALfloat *ResampledData;
ALsizei SrcDataEnd;
/* Load the previous samples into the source data first, then the
* new samples from the input buffer.
*/
memcpy(SrcData, converter->Chan[chan].mPrevSamples,
prepcount*sizeof(ALfloat));
LoadSamples(SrcData + prepcount, SrcSamples,
converter->mNumChannels, converter->mSrcType, toread
);
/* Store as many prep samples for next time as possible, given the
* number of output samples being generated.
*/
SrcDataEnd = (DataPosFrac + increment*DstSize)>>FRACTIONBITS;
if(SrcDataEnd >= prepcount+toread)
memset(converter->Chan[chan].mPrevSamples, 0,
sizeof(converter->Chan[chan].mPrevSamples));
else
{
size_t len = mini(MAX_PRE_SAMPLES+MAX_POST_SAMPLES, prepcount+toread-SrcDataEnd);
memcpy(converter->Chan[chan].mPrevSamples, &SrcData[SrcDataEnd],
len*sizeof(ALfloat));
memset(converter->Chan[chan].mPrevSamples+len, 0,
sizeof(converter->Chan[chan].mPrevSamples) - len*sizeof(ALfloat));
}
/* Now resample, and store the result in the output buffer. */
ResampledData = converter->mResample(&converter->mState,
SrcData+MAX_PRE_SAMPLES, DataPosFrac, increment,
DstData, DstSize
);
StoreSamples(DstSamples, ResampledData, converter->mNumChannels,
converter->mDstType, DstSize);
}
/* Update the number of prep samples still available, as well as the
* fractional offset.
*/
DataPosFrac += increment*DstSize;
converter->mSrcPrepCount = mini(MAX_PRE_SAMPLES+MAX_POST_SAMPLES,
prepcount+toread-(DataPosFrac>>FRACTIONBITS));
converter->mFracOffset = DataPosFrac & FRACTIONMASK;
/* Update the src and dst pointers in case there's still more to do. */
*src = (const ALbyte*)*src + SrcFrameSize*(DataPosFrac>>FRACTIONBITS);
*srcframes -= mini(*srcframes, (DataPosFrac>>FRACTIONBITS));
dst = (ALbyte*)dst + DstFrameSize*DstSize;
pos += DstSize;
}
END_MIXER_MODE();
return pos;
}
ChannelConverter *CreateChannelConverter(enum DevFmtType srcType, enum DevFmtChannels srcChans, enum DevFmtChannels dstChans)
{
ChannelConverter *converter;
if(srcChans != dstChans && !((srcChans == DevFmtMono && dstChans == DevFmtStereo) ||
(srcChans == DevFmtStereo && dstChans == DevFmtMono)))
return NULL;
converter = al_calloc(DEF_ALIGN, sizeof(*converter));
converter->mSrcType = srcType;
converter->mSrcChans = srcChans;
converter->mDstChans = dstChans;
return converter;
}
void DestroyChannelConverter(ChannelConverter **converter)
{
if(converter)
{
al_free(*converter);
*converter = NULL;
}
}
#define DECL_TEMPLATE(T) \
static void Mono2Stereo##T(ALfloat *restrict dst, const T *src, ALsizei frames)\
{ \
ALsizei i; \
for(i = 0;i < frames;i++) \
dst[i*2 + 1] = dst[i*2 + 0] = Sample_##T(src[i]) * 0.707106781187f; \
} \
\
static void Stereo2Mono##T(ALfloat *restrict dst, const T *src, ALsizei frames)\
{ \
ALsizei i; \
for(i = 0;i < frames;i++) \
dst[i] = (Sample_##T(src[i*2 + 0])+Sample_##T(src[i*2 + 1])) * \
0.707106781187f; \
}
DECL_TEMPLATE(ALbyte)
DECL_TEMPLATE(ALubyte)
DECL_TEMPLATE(ALshort)
DECL_TEMPLATE(ALushort)
DECL_TEMPLATE(ALint)
DECL_TEMPLATE(ALuint)
DECL_TEMPLATE(ALfloat)
#undef DECL_TEMPLATE
void ChannelConverterInput(ChannelConverter *converter, const ALvoid *src, ALfloat *dst, ALsizei frames)
{
if(converter->mSrcChans == converter->mDstChans)
{
LoadSamples(dst, src, 1, converter->mSrcType,
frames*ChannelsFromDevFmt(converter->mSrcChans, 0));
return;
}
if(converter->mSrcChans == DevFmtStereo && converter->mDstChans == DevFmtMono)
{
switch(converter->mSrcType)
{
case DevFmtByte:
Stereo2MonoALbyte(dst, src, frames);
break;
case DevFmtUByte:
Stereo2MonoALubyte(dst, src, frames);
break;
case DevFmtShort:
Stereo2MonoALshort(dst, src, frames);
break;
case DevFmtUShort:
Stereo2MonoALushort(dst, src, frames);
break;
case DevFmtInt:
Stereo2MonoALint(dst, src, frames);
break;
case DevFmtUInt:
Stereo2MonoALuint(dst, src, frames);
break;
case DevFmtFloat:
Stereo2MonoALfloat(dst, src, frames);
break;
}
}
else /*if(converter->mSrcChans == DevFmtMono && converter->mDstChans == DevFmtStereo)*/
{
switch(converter->mSrcType)
{
case DevFmtByte:
Mono2StereoALbyte(dst, src, frames);
break;
case DevFmtUByte:
Mono2StereoALubyte(dst, src, frames);
break;
case DevFmtShort:
Mono2StereoALshort(dst, src, frames);
break;
case DevFmtUShort:
Mono2StereoALushort(dst, src, frames);
break;
case DevFmtInt:
Mono2StereoALint(dst, src, frames);
break;
case DevFmtUInt:
Mono2StereoALuint(dst, src, frames);
break;
case DevFmtFloat:
Mono2StereoALfloat(dst, src, frames);
break;
}
}
}
+55
View File
@@ -0,0 +1,55 @@
#ifndef CONVERTER_H
#define CONVERTER_H
#include "alMain.h"
#include "alu.h"
#ifdef __cpluspluc
extern "C" {
#endif
typedef struct SampleConverter {
enum DevFmtType mSrcType;
enum DevFmtType mDstType;
ALsizei mNumChannels;
ALsizei mSrcTypeSize;
ALsizei mDstTypeSize;
ALint mSrcPrepCount;
ALsizei mFracOffset;
ALsizei mIncrement;
InterpState mState;
ResamplerFunc mResample;
alignas(16) ALfloat mSrcSamples[BUFFERSIZE];
alignas(16) ALfloat mDstSamples[BUFFERSIZE];
struct {
alignas(16) ALfloat mPrevSamples[MAX_PRE_SAMPLES+MAX_POST_SAMPLES];
} Chan[];
} SampleConverter;
SampleConverter *CreateSampleConverter(enum DevFmtType srcType, enum DevFmtType dstType, ALsizei numchans, ALsizei srcRate, ALsizei dstRate);
void DestroySampleConverter(SampleConverter **converter);
ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALsizei *srcframes, ALvoid *dst, ALsizei dstframes);
ALsizei SampleConverterAvailableOut(SampleConverter *converter, ALsizei srcframes);
typedef struct ChannelConverter {
enum DevFmtType mSrcType;
enum DevFmtChannels mSrcChans;
enum DevFmtChannels mDstChans;
} ChannelConverter;
ChannelConverter *CreateChannelConverter(enum DevFmtType srcType, enum DevFmtChannels srcChans, enum DevFmtChannels dstChans);
void DestroyChannelConverter(ChannelConverter **converter);
void ChannelConverterInput(ChannelConverter *converter, const ALvoid *src, ALfloat *dst, ALsizei frames);
#ifdef __cpluspluc
}
#endif
#endif /* CONVERTER_H */
-270
View File
@@ -1,270 +0,0 @@
/**
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 20hz (no amplitude) to
* 20khz (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[MAX_OUTPUT_CHANNELS];
/* 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;
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;
ComputeAmbientGains(device, slot->Gain, state->Gain);
}
static ALvoid ALautowahState_process(ALautowahState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE], ALuint NumChannels)
{
ALuint it, kt;
ALuint base;
for(base = 0;base < SamplesToDo;)
{
ALfloat temps[256];
ALuint td = minu(256, SamplesToDo-base);
ALfloat gain = state->GainCtrl;
for(it = 0;it < td;it++)
{
ALfloat smp = SamplesIn[it+base];
ALfloat a[3], b[3];
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_TAU * 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);
b[0] = (1.0f - cosf(w0)) / 2.0f;
b[1] = 1.0f - cosf(w0);
b[2] = (1.0f - cosf(w0)) / 2.0f;
a[0] = 1.0f + alpha;
a[1] = -2.0f * cosf(w0);
a[2] = 1.0f - alpha;
state->LowPass.a1 = a[1] / a[0];
state->LowPass.a2 = a[2] / a[0];
state->LowPass.b1 = b[1] / a[0];
state->LowPass.b2 = b[2] / a[0];
state->LowPass.input_gain = b[0] / a[0];
temps[it] = ALfilterState_processSingle(&state->LowPass, smp);
}
state->GainCtrl = gain;
for(kt = 0;kt < NumChannels;kt++)
{
ALfloat gain = state->Gain[kt];
if(!(fabsf(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);
+115 -104
View File
@@ -39,9 +39,9 @@ typedef struct ALchorusState {
DERIVE_FROM_TYPE(ALeffectState);
ALfloat *SampleBuffer[2];
ALuint BufferLength;
ALuint offset;
ALuint lfo_range;
ALsizei BufferLength;
ALsizei offset;
ALsizei lfo_range;
ALfloat lfo_scale;
ALint lfo_disp;
@@ -55,27 +55,51 @@ typedef struct ALchorusState {
ALfloat feedback;
} ALchorusState;
static ALvoid ALchorusState_Destruct(ALchorusState *state)
static ALvoid ALchorusState_Destruct(ALchorusState *state);
static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device);
static ALvoid ALchorusState_update(ALchorusState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
DECLARE_DEFAULT_ALLOCATORS(ALchorusState)
DEFINE_ALEFFECTSTATE_VTABLE(ALchorusState);
static void ALchorusState_Construct(ALchorusState *state)
{
free(state->SampleBuffer[0]);
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
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;
}
static ALvoid ALchorusState_Destruct(ALchorusState *state)
{
al_free(state->SampleBuffer[0]);
state->SampleBuffer[0] = NULL;
state->SampleBuffer[1] = NULL;
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device)
{
ALuint maxlen;
ALuint it;
ALsizei maxlen;
ALsizei it;
maxlen = fastf2u(AL_CHORUS_MAX_DELAY * 3.0f * Device->Frequency) + 1;
maxlen = fastf2i(AL_CHORUS_MAX_DELAY * 2.0f * Device->Frequency) + 1;
maxlen = NextPowerOf2(maxlen);
if(maxlen != state->BufferLength)
{
void *temp;
temp = realloc(state->SampleBuffer[0], maxlen * sizeof(ALfloat) * 2);
void *temp = al_calloc(16, maxlen * sizeof(ALfloat) * 2);
if(!temp) return AL_FALSE;
al_free(state->SampleBuffer[0]);
state->SampleBuffer[0] = temp;
state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen;
@@ -91,15 +115,14 @@ static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Dev
return AL_TRUE;
}
static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, const ALeffectslot *Slot)
static ALvoid ALchorusState_update(ALchorusState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
{
static const ALfloat left_dir[3] = { -1.0f, 0.0f, 0.0f };
static const ALfloat right_dir[3] = { 1.0f, 0.0f, 0.0f };
ALfloat frequency = (ALfloat)Device->Frequency;
ALfloat coeffs[MAX_AMBI_COEFFS];
ALfloat rate;
ALint phase;
switch(Slot->EffectProps.Chorus.Waveform)
switch(props->Chorus.Waveform)
{
case AL_CHORUS_WAVEFORM_TRIANGLE:
state->waveform = CWF_Triangle;
@@ -108,16 +131,19 @@ static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, cons
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);
state->feedback = props->Chorus.Feedback;
state->delay = fastf2i(props->Chorus.Delay * frequency);
/* The LFO depth is scaled to be relative to the sample delay. */
state->depth = props->Chorus.Depth * state->delay;
/* Gains for left and right sides */
ComputeDirectionalGains(Device, left_dir, Slot->Gain, state->Gain[0]);
ComputeDirectionalGains(Device, right_dir, Slot->Gain, state->Gain[1]);
CalcAngleCoeffs(-F_PI_2, 0.0f, 0.0f, coeffs);
ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[0]);
CalcAngleCoeffs( F_PI_2, 0.0f, 0.0f, coeffs);
ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[1]);
phase = Slot->EffectProps.Chorus.Phase;
rate = Slot->EffectProps.Chorus.Rate;
phase = props->Chorus.Phase;
rate = props->Chorus.Rate;
if(!(rate > 0.0f))
{
state->lfo_scale = 0.0f;
@@ -127,7 +153,7 @@ static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, cons
else
{
/* Calculate LFO coefficient */
state->lfo_range = fastf2u(frequency/rate + 0.5f);
state->lfo_range = fastf2i(frequency/rate + 0.5f);
switch(state->waveform)
{
case CWF_Triangle:
@@ -139,115 +165,108 @@ static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, cons
}
/* Calculate lfo phase displacement */
state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f));
if(phase >= 0)
state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f));
else
state->lfo_disp = fastf2i(state->lfo_range * ((360+phase)/360.0f));
}
}
static inline void Triangle(ALint *delay_left, ALint *delay_right, ALuint offset, const ALchorusState *state)
static void GetTriangleDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
const ALsizei todo)
{
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;
ALsizei i;
for(i = 0;i < todo;i++)
{
delays[i] = fastf2i((1.0f - fabsf(2.0f - lfo_scale*offset)) * depth) + delay;
offset = (offset+1)%lfo_range;
}
}
static inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALuint offset, const ALchorusState *state)
static void GetSinusoidDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
const ALsizei todo)
{
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;
ALsizei i;
for(i = 0;i < todo;i++)
{
delays[i] = fastf2i(sinf(lfo_scale*offset) * depth) + delay;
offset = (offset+1)%lfo_range;
}
}
#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 NumChannels)
static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
ALuint it, kt;
ALuint base;
ALfloat *restrict leftbuf = state->SampleBuffer[0];
ALfloat *restrict rightbuf = state->SampleBuffer[1];
const ALsizei bufmask = state->BufferLength-1;
const ALfloat feedback = state->feedback;
ALsizei offset = state->offset;
ALsizei i, c;
ALsizei base;
for(base = 0;base < SamplesToDo;)
{
const ALsizei todo = mini(128, SamplesToDo-base);
ALfloat temps[128][2];
ALuint td = minu(128, SamplesToDo-base);
ALint moddelays[2][128];
switch(state->waveform)
{
case CWF_Triangle:
ProcessTriangle(state, td, SamplesIn+base, temps);
GetTriangleDelays(moddelays[0], offset%state->lfo_range, state->lfo_range,
state->lfo_scale, state->depth, state->delay, todo);
GetTriangleDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range,
state->lfo_range, state->lfo_scale, state->depth, state->delay,
todo);
break;
case CWF_Sinusoid:
ProcessSinusoid(state, td, SamplesIn+base, temps);
GetSinusoidDelays(moddelays[0], offset%state->lfo_range, state->lfo_range,
state->lfo_scale, state->depth, state->delay, todo);
GetSinusoidDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range,
state->lfo_range, state->lfo_scale, state->depth, state->delay,
todo);
break;
}
for(kt = 0;kt < NumChannels;kt++)
for(i = 0;i < todo;i++)
{
ALfloat gain = state->Gain[0][kt];
leftbuf[offset&bufmask] = SamplesIn[0][base+i];
temps[i][0] = leftbuf[(offset-moddelays[0][i])&bufmask] * feedback;
leftbuf[offset&bufmask] += temps[i][0];
rightbuf[offset&bufmask] = SamplesIn[0][base+i];
temps[i][1] = rightbuf[(offset-moddelays[1][i])&bufmask] * feedback;
rightbuf[offset&bufmask] += temps[i][1];
offset++;
}
for(c = 0;c < NumChannels;c++)
{
ALfloat gain = state->Gain[0][c];
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
{
for(it = 0;it < td;it++)
SamplesOut[kt][it+base] += temps[it][0] * gain;
for(i = 0;i < todo;i++)
SamplesOut[c][i+base] += temps[i][0] * gain;
}
gain = state->Gain[1][kt];
gain = state->Gain[1][c];
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
{
for(it = 0;it < td;it++)
SamplesOut[kt][it+base] += temps[it][1] * gain;
for(i = 0;i < todo;i++)
SamplesOut[c][i+base] += temps[i][1] * gain;
}
}
base += td;
base += todo;
}
state->offset = offset;
}
DECLARE_DEFAULT_ALLOCATORS(ALchorusState)
DEFINE_ALEFFECTSTATE_VTABLE(ALchorusState);
typedef struct ALchorusStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
@@ -257,16 +276,8 @@ static ALeffectState *ALchorusStateFactory_create(ALchorusStateFactory *UNUSED(f
{
ALchorusState *state;
state = ALchorusState_New(sizeof(*state));
NEW_OBJ0(state, ALchorusState)();
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);
}
+78 -41
View File
@@ -31,7 +31,7 @@ typedef struct ALcompressorState {
DERIVE_FROM_TYPE(ALeffectState);
/* Effect gains for each channel */
ALfloat Gain[MAX_OUTPUT_CHANNELS];
ALfloat Gain[MAX_EFFECT_CHANNELS][MAX_OUTPUT_CHANNELS];
/* Effect parameters */
ALboolean Enabled;
@@ -40,8 +40,29 @@ typedef struct ALcompressorState {
ALfloat GainCtrl;
} ALcompressorState;
static ALvoid ALcompressorState_Destruct(ALcompressorState *UNUSED(state))
static ALvoid ALcompressorState_Destruct(ALcompressorState *state);
static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device);
static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props);
static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
DECLARE_DEFAULT_ALLOCATORS(ALcompressorState)
DEFINE_ALEFFECTSTATE_VTABLE(ALcompressorState);
static void ALcompressorState_Construct(ALcompressorState *state)
{
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
SET_VTABLE2(ALcompressorState, ALeffectState, state);
state->Enabled = AL_TRUE;
state->AttackRate = 0.0f;
state->ReleaseRate = 0.0f;
state->GainCtrl = 1.0f;
}
static ALvoid ALcompressorState_Destruct(ALcompressorState *state)
{
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device)
@@ -55,85 +76,107 @@ static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdev
return AL_TRUE;
}
static ALvoid ALcompressorState_update(ALcompressorState *state, ALCdevice *device, const ALeffectslot *slot)
static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props)
{
state->Enabled = slot->EffectProps.Compressor.OnOff;
ALuint i;
ComputeAmbientGains(device, slot->Gain, state->Gain);
state->Enabled = props->Compressor.OnOff;
STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
for(i = 0;i < 4;i++)
ComputeFirstOrderGains(device->FOAOut, IdentityMatrixf.m[i],
slot->Params.Gain, state->Gain[i]);
}
static ALvoid ALcompressorState_process(ALcompressorState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE], ALuint NumChannels)
static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
ALuint it, kt;
ALuint base;
ALsizei i, j, k;
ALsizei base;
for(base = 0;base < SamplesToDo;)
{
ALfloat temps[256];
ALuint td = minu(256, SamplesToDo-base);
ALfloat temps[64][4];
ALsizei td = mini(64, SamplesToDo-base);
/* Load samples into the temp buffer first. */
for(j = 0;j < 4;j++)
{
for(i = 0;i < td;i++)
temps[i][j] = SamplesIn[j][i+base];
}
if(state->Enabled)
{
ALfloat output, smp, amplitude;
ALfloat gain = state->GainCtrl;
ALfloat output, amplitude;
for(it = 0;it < td;it++)
for(i = 0;i < td;i++)
{
smp = SamplesIn[it+base];
amplitude = fabsf(smp);
/* Roughly calculate the maximum amplitude from the 4-channel
* signal, and attack or release the gain control to reach it.
*/
amplitude = fabsf(temps[i][0]);
amplitude = maxf(amplitude + fabsf(temps[i][1]),
maxf(amplitude + fabsf(temps[i][2]),
amplitude + fabsf(temps[i][3])));
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;
/* Apply the inverse of the gain control to normalize/compress
* the volume. */
output = 1.0f / clampf(gain, 0.5f, 2.0f);
for(j = 0;j < 4;j++)
temps[i][j] *= output;
}
state->GainCtrl = gain;
}
else
{
ALfloat output, smp, amplitude;
ALfloat gain = state->GainCtrl;
ALfloat output, amplitude;
for(it = 0;it < td;it++)
for(i = 0;i < td;i++)
{
smp = SamplesIn[it+base];
/* Same as above, except the amplitude is forced to 1. This
* helps ensure smooth gain changes when the compressor is
* turned on and off.
*/
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;
output = 1.0f / clampf(gain, 0.5f, 2.0f);
for(j = 0;j < 4;j++)
temps[i][j] *= output;
}
state->GainCtrl = gain;
}
for(kt = 0;kt < NumChannels;kt++)
/* Now mix to the output. */
for(j = 0;j < 4;j++)
{
ALfloat gain = state->Gain[kt];
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
continue;
for(k = 0;k < NumChannels;k++)
{
ALfloat gain = state->Gain[j][k];
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
continue;
for(it = 0;it < td;it++)
SamplesOut[kt][base+it] += gain * temps[it];
for(i = 0;i < td;i++)
SamplesOut[k][base+i] += gain * temps[i][j];
}
}
base += td;
}
}
DECLARE_DEFAULT_ALLOCATORS(ALcompressorState)
DEFINE_ALEFFECTSTATE_VTABLE(ALcompressorState);
typedef struct ALcompressorStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
@@ -143,14 +186,8 @@ static ALeffectState *ALcompressorStateFactory_create(ALcompressorStateFactory *
{
ALcompressorState *state;
state = ALcompressorState_New(sizeof(*state));
NEW_OBJ0(state, ALcompressorState)();
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);
}
+49 -24
View File
@@ -35,9 +35,29 @@ typedef struct ALdedicatedState {
ALfloat gains[MAX_OUTPUT_CHANNELS];
} ALdedicatedState;
static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state);
static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *state, ALCdevice *device);
static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCdevice *device, const ALeffectslot *Slot, const ALeffectProps *props);
static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
DECLARE_DEFAULT_ALLOCATORS(ALdedicatedState)
static ALvoid ALdedicatedState_Destruct(ALdedicatedState *UNUSED(state))
DEFINE_ALEFFECTSTATE_VTABLE(ALdedicatedState);
static void ALdedicatedState_Construct(ALdedicatedState *state)
{
ALsizei s;
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
SET_VTABLE2(ALdedicatedState, ALeffectState, state);
for(s = 0;s < MAX_OUTPUT_CHANNELS;s++)
state->gains[s] = 0.0f;
}
static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state)
{
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *UNUSED(state), ALCdevice *UNUSED(device))
@@ -45,7 +65,7 @@ static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *UNUSED(state),
return AL_TRUE;
}
static ALvoid ALdedicatedState_update(ALdedicatedState *state, ALCdevice *device, const ALeffectslot *Slot)
static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCdevice *device, const ALeffectslot *Slot, const ALeffectProps *props)
{
ALfloat Gain;
ALuint i;
@@ -53,47 +73,57 @@ static ALvoid ALdedicatedState_update(ALdedicatedState *state, ALCdevice *device
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
state->gains[i] = 0.0f;
Gain = Slot->Gain * Slot->EffectProps.Dedicated.Gain;
if(Slot->EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
Gain = Slot->Params.Gain * props->Dedicated.Gain;
if(Slot->Params.EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
{
int idx;
if((idx=GetChannelIdxByName(device, LFE)) != -1)
if((idx=GetChannelIdxByName(device->RealOut, LFE)) != -1)
{
STATIC_CAST(ALeffectState,state)->OutBuffer = device->RealOut.Buffer;
STATIC_CAST(ALeffectState,state)->OutChannels = device->RealOut.NumChannels;
state->gains[idx] = Gain;
}
}
else if(Slot->EffectType == AL_EFFECT_DEDICATED_DIALOGUE)
else if(Slot->Params.EffectType == AL_EFFECT_DEDICATED_DIALOGUE)
{
int idx;
/* Dialog goes to the front-center speaker if it exists, otherwise it
* plays from the front-center location. */
if((idx=GetChannelIdxByName(device, FrontCenter)) != -1)
if((idx=GetChannelIdxByName(device->RealOut, FrontCenter)) != -1)
{
STATIC_CAST(ALeffectState,state)->OutBuffer = device->RealOut.Buffer;
STATIC_CAST(ALeffectState,state)->OutChannels = device->RealOut.NumChannels;
state->gains[idx] = Gain;
}
else
{
static const ALfloat front_dir[3] = { 0.0f, 0.0f, -1.0f };
ComputeDirectionalGains(device, front_dir, Gain, state->gains);
ALfloat coeffs[MAX_AMBI_COEFFS];
CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
STATIC_CAST(ALeffectState,state)->OutBuffer = device->Dry.Buffer;
STATIC_CAST(ALeffectState,state)->OutChannels = device->Dry.NumChannels;
ComputePanningGains(device->Dry, coeffs, Gain, state->gains);
}
}
}
static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
const ALfloat *gains = state->gains;
ALuint i, c;
ALsizei i, c;
SamplesIn = ASSUME_ALIGNED(SamplesIn, 16);
SamplesOut = ASSUME_ALIGNED(SamplesOut, 16);
for(c = 0;c < NumChannels;c++)
{
if(!(fabsf(gains[c]) > GAIN_SILENCE_THRESHOLD))
const ALfloat gain = state->gains[c];
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
continue;
for(i = 0;i < SamplesToDo;i++)
SamplesOut[c][i] += SamplesIn[i] * gains[c];
SamplesOut[c][i] += SamplesIn[0][i] * gain;
}
}
DECLARE_DEFAULT_ALLOCATORS(ALdedicatedState)
DEFINE_ALEFFECTSTATE_VTABLE(ALdedicatedState);
typedef struct ALdedicatedStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
@@ -102,14 +132,9 @@ typedef struct ALdedicatedStateFactory {
ALeffectState *ALdedicatedStateFactory_create(ALdedicatedStateFactory *UNUSED(factory))
{
ALdedicatedState *state;
ALsizei s;
state = ALdedicatedState_New(sizeof(*state));
NEW_OBJ0(state, ALdedicatedState)();
if(!state) return NULL;
SET_VTABLE2(ALdedicatedState, ALeffectState, state);
for(s = 0;s < MAX_OUTPUT_CHANNELS;s++)
state->gains[s] = 0.0f;
return STATIC_CAST(ALeffectState, state);
}
+75 -73
View File
@@ -43,8 +43,27 @@ typedef struct ALdistortionState {
ALfloat edge_coeff;
} ALdistortionState;
static ALvoid ALdistortionState_Destruct(ALdistortionState *UNUSED(state))
static ALvoid ALdistortionState_Destruct(ALdistortionState *state);
static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *state, ALCdevice *device);
static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
DECLARE_DEFAULT_ALLOCATORS(ALdistortionState)
DEFINE_ALEFFECTSTATE_VTABLE(ALdistortionState);
static void ALdistortionState_Construct(ALdistortionState *state)
{
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
SET_VTABLE2(ALdistortionState, ALeffectState, state);
ALfilterState_clear(&state->lowpass);
ALfilterState_clear(&state->bandpass);
}
static ALvoid ALdistortionState_Destruct(ALdistortionState *state)
{
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *UNUSED(state), ALCdevice *UNUSED(device))
@@ -52,104 +71,95 @@ static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *UNUSED(state)
return AL_TRUE;
}
static ALvoid ALdistortionState_update(ALdistortionState *state, ALCdevice *Device, const ALeffectslot *Slot)
static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
{
ALfloat frequency = (ALfloat)Device->Frequency;
ALfloat bandwidth;
ALfloat cutoff;
ALfloat edge;
/* Store distorted signal attenuation settings */
state->attenuation = Slot->EffectProps.Distortion.Gain;
/* Store distorted signal attenuation settings. */
state->attenuation = props->Distortion.Gain;
/* Store waveshaper edge settings */
edge = sinf(Slot->EffectProps.Distortion.Edge * (F_PI_2));
/* Store waveshaper edge settings. */
edge = sinf(props->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 */
cutoff = props->Distortion.LowpassCutoff;
/* Bandwidth value is constant in octaves. */
bandwidth = (cutoff / 2.0f) / (cutoff * 0.67f);
/* Multiply sampling frequency by the amount of oversampling done during
* processing.
*/
ALfilterState_setParams(&state->lowpass, ALfilterType_LowPass, 1.0f,
cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(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);
cutoff = props->Distortion.EQCenter;
/* Convert bandwidth in Hz to octaves. */
bandwidth = props->Distortion.EQBandwidth / (cutoff * 0.67f);
ALfilterState_setParams(&state->bandpass, ALfilterType_BandPass, 1.0f,
cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
);
ComputeAmbientGains(Device, Slot->Gain, state->Gain);
ComputeAmbientGains(Device->Dry, Slot->Params.Gain, state->Gain);
}
static ALvoid ALdistortionState_process(ALdistortionState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
const ALfloat fc = state->edge_coeff;
ALuint base;
ALuint it;
ALuint ot;
ALuint kt;
ALsizei it, kt;
ALsizei base;
for(base = 0;base < SamplesToDo;)
{
float oversample_buffer[64][4];
ALuint td = minu(64, SamplesToDo-base);
float buffer[2][64 * 4];
ALsizei td = mini(64, SamplesToDo-base);
/* 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. */
/* 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 */
/* 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;
/* Multiply the sample by the amount of oversampling to maintain
* the signal's power.
*/
buffer[0][it*4 + 0] = SamplesIn[0][it+base] * 4.0f;
buffer[0][it*4 + 1] = 0.0f;
buffer[0][it*4 + 2] = 0.0f;
buffer[0][it*4 + 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++)
/* 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.
*/
ALfilterState_process(&state->lowpass, buffer[1], buffer[0], td*4);
/* 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(it = 0;it < td*4;it++)
{
for(ot = 0;ot < 4;ot++)
{
ALfloat smp;
smp = ALfilterState_processSingle(&state->lowpass, oversample_buffer[it][ot]);
ALfloat smp = buffer[1][it];
/* Restore signal power by multiplying sample by amount of oversampling */
oversample_buffer[it][ot] = smp * 4.0f;
}
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));
buffer[0][it] = smp;
}
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;
}
}
/* Third step, do bandpass filtering of distorted signal. */
ALfilterState_process(&state->bandpass, buffer[1], buffer[0], td*4);
for(kt = 0;kt < NumChannels;kt++)
{
@@ -161,17 +171,13 @@ static ALvoid ALdistortionState_process(ALdistortionState *state, ALuint Samples
continue;
for(it = 0;it < td;it++)
SamplesOut[kt][base+it] += gain * oversample_buffer[it][0];
SamplesOut[kt][base+it] += gain * buffer[1][it*4];
}
base += td;
}
}
DECLARE_DEFAULT_ALLOCATORS(ALdistortionState)
DEFINE_ALEFFECTSTATE_VTABLE(ALdistortionState);
typedef struct ALdistortionStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
@@ -181,12 +187,8 @@ static ALeffectState *ALdistortionStateFactory_create(ALdistortionStateFactory *
{
ALdistortionState *state;
state = ALdistortionState_New(sizeof(*state));
NEW_OBJ0(state, ALdistortionState)();
if(!state) return NULL;
SET_VTABLE2(ALdistortionState, ALeffectState, state);
ALfilterState_clear(&state->lowpass);
ALfilterState_clear(&state->bandpass);
return STATIC_CAST(ALeffectState, state);
}
+81 -50
View File
@@ -34,14 +34,14 @@ typedef struct ALechoState {
DERIVE_FROM_TYPE(ALeffectState);
ALfloat *SampleBuffer;
ALuint BufferLength;
ALsizei BufferLength;
// The echo is two tap. The delay is the number of samples from before the
// current offset
struct {
ALuint delay;
ALsizei delay;
} Tap[2];
ALuint Offset;
ALsizei Offset;
/* The panning gains for the two taps */
ALfloat Gain[2][MAX_OUTPUT_CHANNELS];
@@ -50,28 +50,53 @@ typedef struct ALechoState {
ALfilterState Filter;
} ALechoState;
static ALvoid ALechoState_Destruct(ALechoState *state);
static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device);
static ALvoid ALechoState_update(ALechoState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
DECLARE_DEFAULT_ALLOCATORS(ALechoState)
DEFINE_ALEFFECTSTATE_VTABLE(ALechoState);
static void ALechoState_Construct(ALechoState *state)
{
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
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);
}
static ALvoid ALechoState_Destruct(ALechoState *state)
{
free(state->SampleBuffer);
al_free(state->SampleBuffer);
state->SampleBuffer = NULL;
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device)
{
ALuint maxlen, i;
ALsizei 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 = fastf2i(AL_ECHO_MAX_DELAY * Device->Frequency) + 1;
maxlen += fastf2i(AL_ECHO_MAX_LRDELAY * Device->Frequency) + 1;
maxlen = NextPowerOf2(maxlen);
if(maxlen != state->BufferLength)
{
void *temp;
temp = realloc(state->SampleBuffer, maxlen * sizeof(ALfloat));
void *temp = al_calloc(16, maxlen * sizeof(ALfloat));
if(!temp) return AL_FALSE;
al_free(state->SampleBuffer);
state->SampleBuffer = temp;
state->BufferLength = maxlen;
}
@@ -81,50 +106,60 @@ static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device)
return AL_TRUE;
}
static ALvoid ALechoState_update(ALechoState *state, ALCdevice *Device, const ALeffectslot *Slot)
static ALvoid ALechoState_update(ALechoState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
{
ALfloat pandir[3] = { 0.0f, 0.0f, 0.0f };
ALuint frequency = Device->Frequency;
ALfloat gain, lrpan;
ALfloat coeffs[MAX_AMBI_COEFFS];
ALfloat gain, lrpan, spread;
state->Tap[0].delay = fastf2u(Slot->EffectProps.Echo.Delay * frequency) + 1;
state->Tap[1].delay = fastf2u(Slot->EffectProps.Echo.LRDelay * frequency);
state->Tap[0].delay = fastf2i(props->Echo.Delay * frequency) + 1;
state->Tap[1].delay = fastf2i(props->Echo.LRDelay * frequency);
state->Tap[1].delay += state->Tap[0].delay;
lrpan = Slot->EffectProps.Echo.Spread;
spread = props->Echo.Spread;
if(spread < 0.0f) lrpan = -1.0f;
else lrpan = 1.0f;
/* Convert echo spread (where 0 = omni, +/-1 = directional) to coverage
* spread (where 0 = point, tau = omni).
*/
spread = asinf(1.0f - fabsf(spread))*4.0f;
state->FeedGain = Slot->EffectProps.Echo.Feedback;
state->FeedGain = props->Echo.Feedback;
gain = minf(1.0f - Slot->EffectProps.Echo.Damping, 0.01f);
gain = maxf(1.0f - props->Echo.Damping, 0.0625f); /* Limit -24dB */
ALfilterState_setParams(&state->Filter, ALfilterType_HighShelf,
gain, LOWPASSFREQREF/frequency,
calc_rcpQ_from_slope(gain, 0.75f));
calc_rcpQ_from_slope(gain, 1.0f));
gain = Slot->Gain;
gain = Slot->Params.Gain;
/* First tap panning */
pandir[0] = -lrpan;
ComputeDirectionalGains(Device, pandir, gain, state->Gain[0]);
CalcAngleCoeffs(-F_PI_2*lrpan, 0.0f, spread, coeffs);
ComputePanningGains(Device->Dry, coeffs, gain, state->Gain[0]);
/* Second tap panning */
pandir[0] = +lrpan;
ComputeDirectionalGains(Device, pandir, gain, state->Gain[1]);
CalcAngleCoeffs( F_PI_2*lrpan, 0.0f, spread, coeffs);
ComputePanningGains(Device->Dry, coeffs, gain, state->Gain[1]);
}
static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
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;
const ALsizei mask = state->BufferLength-1;
const ALsizei tap1 = state->Tap[0].delay;
const ALsizei tap2 = state->Tap[1].delay;
ALsizei offset = state->Offset;
ALfloat x[2], y[2], in, out;
ALsizei base, k;
ALsizei i;
x[0] = state->Filter.x[0];
x[1] = state->Filter.x[1];
y[0] = state->Filter.y[0];
y[1] = state->Filter.y[1];
for(base = 0;base < SamplesToDo;)
{
ALfloat temps[128][2];
ALuint td = minu(128, SamplesToDo-base);
ALsizei td = mini(128, SamplesToDo-base);
for(i = 0;i < td;i++)
{
@@ -135,8 +170,14 @@ static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const
// 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;
in = temps[i][1] + SamplesIn[0][i+base];
out = in*state->Filter.b0 +
x[0]*state->Filter.b1 + x[1]*state->Filter.b2 -
y[0]*state->Filter.a1 - y[1]*state->Filter.a2;
x[1] = x[0]; x[0] = in;
y[1] = y[0]; y[0] = out;
state->SampleBuffer[offset&mask] = out * state->FeedGain;
offset++;
}
@@ -159,14 +200,14 @@ static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const
base += td;
}
state->Filter.x[0] = x[0];
state->Filter.x[1] = x[1];
state->Filter.y[0] = y[0];
state->Filter.y[1] = y[1];
state->Offset = offset;
}
DECLARE_DEFAULT_ALLOCATORS(ALechoState)
DEFINE_ALEFFECTSTATE_VTABLE(ALechoState);
typedef struct ALechoStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
@@ -176,18 +217,8 @@ ALeffectState *ALechoStateFactory_create(ALechoStateFactory *UNUSED(factory))
{
ALechoState *state;
state = ALechoState_New(sizeof(*state));
NEW_OBJ0(state, ALechoState)();
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);
}
+93 -54
View File
@@ -71,18 +71,50 @@
* filter coefficients" by Robert Bristow-Johnson *
* http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt */
/* The maximum number of sample frames per update. */
#define MAX_UPDATE_SAMPLES 256
typedef struct ALequalizerState {
DERIVE_FROM_TYPE(ALeffectState);
/* Effect gains for each channel */
ALfloat Gain[MAX_OUTPUT_CHANNELS];
ALfloat Gain[MAX_EFFECT_CHANNELS][MAX_OUTPUT_CHANNELS];
/* Effect parameters */
ALfilterState filter[4];
ALfilterState filter[4][MAX_EFFECT_CHANNELS];
ALfloat SampleBuffer[4][MAX_EFFECT_CHANNELS][MAX_UPDATE_SAMPLES];
} ALequalizerState;
static ALvoid ALequalizerState_Destruct(ALequalizerState *UNUSED(state))
static ALvoid ALequalizerState_Destruct(ALequalizerState *state);
static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *state, ALCdevice *device);
static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props);
static ALvoid ALequalizerState_process(ALequalizerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
DECLARE_DEFAULT_ALLOCATORS(ALequalizerState)
DEFINE_ALEFFECTSTATE_VTABLE(ALequalizerState);
static void ALequalizerState_Construct(ALequalizerState *state)
{
int it, ft;
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
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++)
{
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
ALfilterState_clear(&state->filter[it][ft]);
}
}
static ALvoid ALequalizerState_Destruct(ALequalizerState *state)
{
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *UNUSED(state), ALCdevice *UNUSED(device))
@@ -90,82 +122,96 @@ static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *UNUSED(state),
return AL_TRUE;
}
static ALvoid ALequalizerState_update(ALequalizerState *state, ALCdevice *device, const ALeffectslot *slot)
static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props)
{
ALfloat frequency = (ALfloat)device->Frequency;
ALfloat gain, freq_mult;
ALuint i;
ComputeAmbientGains(device, slot->Gain, state->Gain);
STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
ComputeFirstOrderGains(device->FOAOut, IdentityMatrixf.m[i],
slot->Params.Gain, state->Gain[i]);
/* Calculate coefficients for the each type of filter. Note that the shelf
* filters' gain is for the reference frequency, which is the centerpoint
* of the transition band.
*/
gain = sqrtf(slot->EffectProps.Equalizer.LowGain);
freq_mult = slot->EffectProps.Equalizer.LowCutoff/frequency;
ALfilterState_setParams(&state->filter[0], ALfilterType_LowShelf,
gain = maxf(sqrtf(props->Equalizer.LowGain), 0.0625f); /* Limit -24dB */
freq_mult = props->Equalizer.LowCutoff/frequency;
ALfilterState_setParams(&state->filter[0][0], ALfilterType_LowShelf,
gain, freq_mult, calc_rcpQ_from_slope(gain, 0.75f)
);
/* Copy the filter coefficients for the other input channels. */
for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
ALfilterState_copyParams(&state->filter[0][i], &state->filter[0][0]);
gain = slot->EffectProps.Equalizer.Mid1Gain;
freq_mult = slot->EffectProps.Equalizer.Mid1Center/frequency;
ALfilterState_setParams(&state->filter[1], ALfilterType_Peaking,
gain, freq_mult, calc_rcpQ_from_bandwidth(freq_mult, slot->EffectProps.Equalizer.Mid1Width)
gain = maxf(props->Equalizer.Mid1Gain, 0.0625f);
freq_mult = props->Equalizer.Mid1Center/frequency;
ALfilterState_setParams(&state->filter[1][0], ALfilterType_Peaking,
gain, freq_mult, calc_rcpQ_from_bandwidth(
freq_mult, props->Equalizer.Mid1Width
)
);
for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
ALfilterState_copyParams(&state->filter[1][i], &state->filter[1][0]);
gain = slot->EffectProps.Equalizer.Mid2Gain;
freq_mult = slot->EffectProps.Equalizer.Mid2Center/frequency;
ALfilterState_setParams(&state->filter[2], ALfilterType_Peaking,
gain, freq_mult, calc_rcpQ_from_bandwidth(freq_mult, slot->EffectProps.Equalizer.Mid2Width)
gain = maxf(props->Equalizer.Mid2Gain, 0.0625f);
freq_mult = props->Equalizer.Mid2Center/frequency;
ALfilterState_setParams(&state->filter[2][0], ALfilterType_Peaking,
gain, freq_mult, calc_rcpQ_from_bandwidth(
freq_mult, props->Equalizer.Mid2Width
)
);
for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
ALfilterState_copyParams(&state->filter[2][i], &state->filter[2][0]);
gain = sqrtf(slot->EffectProps.Equalizer.HighGain);
freq_mult = slot->EffectProps.Equalizer.HighCutoff/frequency;
ALfilterState_setParams(&state->filter[3], ALfilterType_HighShelf,
gain = maxf(sqrtf(props->Equalizer.HighGain), 0.0625f);
freq_mult = props->Equalizer.HighCutoff/frequency;
ALfilterState_setParams(&state->filter[3][0], ALfilterType_HighShelf,
gain, freq_mult, calc_rcpQ_from_slope(gain, 0.75f)
);
for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
ALfilterState_copyParams(&state->filter[3][i], &state->filter[3][0]);
}
static ALvoid ALequalizerState_process(ALequalizerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
static ALvoid ALequalizerState_process(ALequalizerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
ALuint base;
ALuint it;
ALuint kt;
ALuint ft;
ALfloat (*Samples)[MAX_EFFECT_CHANNELS][MAX_UPDATE_SAMPLES] = state->SampleBuffer;
ALsizei it, kt, ft;
ALsizei base;
for(base = 0;base < SamplesToDo;)
{
ALfloat temps[256];
ALuint td = minu(256, SamplesToDo-base);
ALsizei td = mini(MAX_UPDATE_SAMPLES, SamplesToDo-base);
for(it = 0;it < td;it++)
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
ALfilterState_process(&state->filter[0][ft], Samples[0][ft], &SamplesIn[ft][base], td);
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
ALfilterState_process(&state->filter[1][ft], Samples[1][ft], Samples[0][ft], td);
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
ALfilterState_process(&state->filter[2][ft], Samples[2][ft], Samples[1][ft], td);
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
ALfilterState_process(&state->filter[3][ft], Samples[3][ft], Samples[2][ft], td);
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
{
ALfloat smp = SamplesIn[base+it];
for(kt = 0;kt < NumChannels;kt++)
{
ALfloat gain = state->Gain[ft][kt];
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
continue;
for(ft = 0;ft < 4;ft++)
smp = ALfilterState_processSingle(&state->filter[ft], smp);
temps[it] = smp;
}
for(kt = 0;kt < NumChannels;kt++)
{
ALfloat gain = state->Gain[kt];
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
continue;
for(it = 0;it < td;it++)
SamplesOut[kt][base+it] += gain * temps[it];
for(it = 0;it < td;it++)
SamplesOut[kt][base+it] += gain * Samples[3][ft][it];
}
}
base += td;
}
}
DECLARE_DEFAULT_ALLOCATORS(ALequalizerState)
DEFINE_ALEFFECTSTATE_VTABLE(ALequalizerState);
typedef struct ALequalizerStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
@@ -174,16 +220,9 @@ typedef struct ALequalizerStateFactory {
ALeffectState *ALequalizerStateFactory_create(ALequalizerStateFactory *UNUSED(factory))
{
ALequalizerState *state;
int it;
state = ALequalizerState_New(sizeof(*state));
NEW_OBJ0(state, ALequalizerState)();
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);
}
+115 -105
View File
@@ -39,9 +39,9 @@ typedef struct ALflangerState {
DERIVE_FROM_TYPE(ALeffectState);
ALfloat *SampleBuffer[2];
ALuint BufferLength;
ALuint offset;
ALuint lfo_range;
ALsizei BufferLength;
ALsizei offset;
ALsizei lfo_range;
ALfloat lfo_scale;
ALint lfo_disp;
@@ -55,27 +55,51 @@ typedef struct ALflangerState {
ALfloat feedback;
} ALflangerState;
static ALvoid ALflangerState_Destruct(ALflangerState *state)
static ALvoid ALflangerState_Destruct(ALflangerState *state);
static ALboolean ALflangerState_deviceUpdate(ALflangerState *state, ALCdevice *Device);
static ALvoid ALflangerState_update(ALflangerState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
static ALvoid ALflangerState_process(ALflangerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
DECLARE_DEFAULT_ALLOCATORS(ALflangerState)
DEFINE_ALEFFECTSTATE_VTABLE(ALflangerState);
static void ALflangerState_Construct(ALflangerState *state)
{
free(state->SampleBuffer[0]);
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
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;
}
static ALvoid ALflangerState_Destruct(ALflangerState *state)
{
al_free(state->SampleBuffer[0]);
state->SampleBuffer[0] = NULL;
state->SampleBuffer[1] = NULL;
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
static ALboolean ALflangerState_deviceUpdate(ALflangerState *state, ALCdevice *Device)
{
ALuint maxlen;
ALuint it;
ALsizei maxlen;
ALsizei it;
maxlen = fastf2u(AL_FLANGER_MAX_DELAY * 3.0f * Device->Frequency) + 1;
maxlen = fastf2i(AL_FLANGER_MAX_DELAY * 2.0f * Device->Frequency) + 1;
maxlen = NextPowerOf2(maxlen);
if(maxlen != state->BufferLength)
{
void *temp;
temp = realloc(state->SampleBuffer[0], maxlen * sizeof(ALfloat) * 2);
void *temp = al_calloc(16, maxlen * sizeof(ALfloat) * 2);
if(!temp) return AL_FALSE;
al_free(state->SampleBuffer[0]);
state->SampleBuffer[0] = temp;
state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen;
@@ -91,15 +115,14 @@ static ALboolean ALflangerState_deviceUpdate(ALflangerState *state, ALCdevice *D
return AL_TRUE;
}
static ALvoid ALflangerState_update(ALflangerState *state, ALCdevice *Device, const ALeffectslot *Slot)
static ALvoid ALflangerState_update(ALflangerState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
{
static const ALfloat left_dir[3] = { -1.0f, 0.0f, 0.0f };
static const ALfloat right_dir[3] = { 1.0f, 0.0f, 0.0f };
ALfloat frequency = (ALfloat)Device->Frequency;
ALfloat coeffs[MAX_AMBI_COEFFS];
ALfloat rate;
ALint phase;
switch(Slot->EffectProps.Flanger.Waveform)
switch(props->Flanger.Waveform)
{
case AL_FLANGER_WAVEFORM_TRIANGLE:
state->waveform = FWF_Triangle;
@@ -108,16 +131,19 @@ static ALvoid ALflangerState_update(ALflangerState *state, ALCdevice *Device, co
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);
state->feedback = props->Flanger.Feedback;
state->delay = fastf2i(props->Flanger.Delay * frequency);
/* The LFO depth is scaled to be relative to the sample delay. */
state->depth = props->Flanger.Depth * state->delay;
/* Gains for left and right sides */
ComputeDirectionalGains(Device, left_dir, Slot->Gain, state->Gain[0]);
ComputeDirectionalGains(Device, right_dir, Slot->Gain, state->Gain[1]);
CalcAngleCoeffs(-F_PI_2, 0.0f, 0.0f, coeffs);
ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[0]);
CalcAngleCoeffs( F_PI_2, 0.0f, 0.0f, coeffs);
ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[1]);
phase = Slot->EffectProps.Flanger.Phase;
rate = Slot->EffectProps.Flanger.Rate;
phase = props->Flanger.Phase;
rate = props->Flanger.Rate;
if(!(rate > 0.0f))
{
state->lfo_scale = 0.0f;
@@ -127,7 +153,7 @@ static ALvoid ALflangerState_update(ALflangerState *state, ALCdevice *Device, co
else
{
/* Calculate LFO coefficient */
state->lfo_range = fastf2u(frequency/rate + 0.5f);
state->lfo_range = fastf2i(frequency/rate + 0.5f);
switch(state->waveform)
{
case FWF_Triangle:
@@ -139,115 +165,107 @@ static ALvoid ALflangerState_update(ALflangerState *state, ALCdevice *Device, co
}
/* Calculate lfo phase displacement */
state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f));
if(phase >= 0)
state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f));
else
state->lfo_disp = fastf2i(state->lfo_range * ((360+phase)/360.0f));
}
}
static inline void Triangle(ALint *delay_left, ALint *delay_right, ALuint offset, const ALflangerState *state)
static void GetTriangleDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
const ALsizei todo)
{
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;
ALsizei i;
for(i = 0;i < todo;i++)
{
delays[i] = fastf2i((1.0f - fabsf(2.0f - lfo_scale*offset)) * depth) + delay;
offset = (offset+1)%lfo_range;
}
}
static inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALuint offset, const ALflangerState *state)
static void GetSinusoidDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
const ALsizei todo)
{
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;
ALsizei i;
for(i = 0;i < todo;i++)
{
delays[i] = fastf2i(sinf(lfo_scale*offset) * depth) + delay;
offset = (offset+1)%lfo_range;
}
}
#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 NumChannels)
static ALvoid ALflangerState_process(ALflangerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
ALuint it, kt;
ALuint base;
ALfloat *restrict leftbuf = state->SampleBuffer[0];
ALfloat *restrict rightbuf = state->SampleBuffer[1];
const ALsizei bufmask = state->BufferLength-1;
const ALfloat feedback = state->feedback;
ALsizei offset = state->offset;
ALsizei i, c;
ALsizei base;
for(base = 0;base < SamplesToDo;)
{
const ALsizei todo = mini(128, SamplesToDo-base);
ALfloat temps[128][2];
ALuint td = minu(128, SamplesToDo-base);
ALint moddelays[2][128];
switch(state->waveform)
{
case FWF_Triangle:
ProcessTriangle(state, td, SamplesIn+base, temps);
GetTriangleDelays(moddelays[0], offset%state->lfo_range, state->lfo_range,
state->lfo_scale, state->depth, state->delay, todo);
GetTriangleDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range,
state->lfo_range, state->lfo_scale, state->depth, state->delay,
todo);
break;
case FWF_Sinusoid:
ProcessSinusoid(state, td, SamplesIn+base, temps);
GetSinusoidDelays(moddelays[0], offset%state->lfo_range, state->lfo_range,
state->lfo_scale, state->depth, state->delay, todo);
GetSinusoidDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range,
state->lfo_range, state->lfo_scale, state->depth, state->delay,
todo);
break;
}
for(kt = 0;kt < NumChannels;kt++)
for(i = 0;i < todo;i++)
{
ALfloat gain = state->Gain[0][kt];
leftbuf[offset&bufmask] = SamplesIn[0][base+i];
temps[i][0] = leftbuf[(offset-moddelays[0][i])&bufmask] * feedback;
leftbuf[offset&bufmask] += temps[i][0];
rightbuf[offset&bufmask] = SamplesIn[0][base+i];
temps[i][1] = rightbuf[(offset-moddelays[1][i])&bufmask] * feedback;
rightbuf[offset&bufmask] += temps[i][1];
offset++;
}
for(c = 0;c < NumChannels;c++)
{
ALfloat gain = state->Gain[0][c];
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
{
for(it = 0;it < td;it++)
SamplesOut[kt][it+base] += temps[it][0] * gain;
for(i = 0;i < todo;i++)
SamplesOut[c][i+base] += temps[i][0] * gain;
}
gain = state->Gain[1][kt];
gain = state->Gain[1][c];
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
{
for(it = 0;it < td;it++)
SamplesOut[kt][it+base] += temps[it][1] * gain;
for(i = 0;i < todo;i++)
SamplesOut[c][i+base] += temps[i][1] * gain;
}
}
base += td;
base += todo;
}
state->offset = offset;
}
DECLARE_DEFAULT_ALLOCATORS(ALflangerState)
DEFINE_ALEFFECTSTATE_VTABLE(ALflangerState);
typedef struct ALflangerStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
@@ -257,16 +275,8 @@ ALeffectState *ALflangerStateFactory_create(ALflangerStateFactory *UNUSED(factor
{
ALflangerState *state;
state = ALflangerState_New(sizeof(*state));
NEW_OBJ0(state, ALflangerState)();
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);
}
+99 -89
View File
@@ -33,78 +33,55 @@
typedef struct ALmodulatorState {
DERIVE_FROM_TYPE(ALeffectState);
enum {
SINUSOID,
SAWTOOTH,
SQUARE
} Waveform;
void (*Process)(ALfloat*, const ALfloat*, ALsizei, const ALsizei, ALsizei);
ALuint index;
ALuint step;
ALsizei index;
ALsizei step;
ALfloat Gain[MAX_OUTPUT_CHANNELS];
ALfloat Gain[MAX_EFFECT_CHANNELS][MAX_OUTPUT_CHANNELS];
ALfilterState Filter;
ALfilterState Filter[MAX_EFFECT_CHANNELS];
} ALmodulatorState;
static ALvoid ALmodulatorState_Destruct(ALmodulatorState *state);
static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *state, ALCdevice *device);
static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
DECLARE_DEFAULT_ALLOCATORS(ALmodulatorState)
DEFINE_ALEFFECTSTATE_VTABLE(ALmodulatorState);
#define WAVEFORM_FRACBITS 24
#define WAVEFORM_FRACONE (1<<WAVEFORM_FRACBITS)
#define WAVEFORM_FRACMASK (WAVEFORM_FRACONE-1)
static inline ALfloat Sin(ALuint index)
static inline ALfloat Sin(ALsizei index)
{
return sinf(index*(F_TAU/WAVEFORM_FRACONE) - F_PI)*0.5f + 0.5f;
}
static inline ALfloat Saw(ALuint index)
static inline ALfloat Saw(ALsizei index)
{
return (ALfloat)index / WAVEFORM_FRACONE;
}
static inline ALfloat Square(ALuint index)
static inline ALfloat Square(ALsizei 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], ALuint NumChannels) \
static void Modulate##func(ALfloat *restrict dst, const ALfloat *restrict src,\
ALsizei index, const ALsizei step, ALsizei todo) \
{ \
const ALuint step = state->step; \
ALuint index = state->index; \
ALuint base; \
\
for(base = 0;base < SamplesToDo;) \
ALsizei i; \
for(i = 0;i < todo;i++) \
{ \
ALfloat temps[256]; \
ALuint td = minu(256, SamplesToDo-base); \
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 < NumChannels;k++) \
{ \
ALfloat gain = state->Gain[k]; \
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) \
continue; \
\
for(i = 0;i < td;i++) \
SamplesOut[k][base+i] += gain * temps[i]; \
} \
\
base += td; \
index += step; \
index &= WAVEFORM_FRACMASK; \
dst[i] = src[i] * func(index); \
} \
state->index = index; \
}
DECL_TEMPLATE(Sin)
@@ -114,8 +91,23 @@ DECL_TEMPLATE(Square)
#undef DECL_TEMPLATE
static ALvoid ALmodulatorState_Destruct(ALmodulatorState *UNUSED(state))
static void ALmodulatorState_Construct(ALmodulatorState *state)
{
ALuint i;
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
SET_VTABLE2(ALmodulatorState, ALeffectState, state);
state->index = 0;
state->step = 1;
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
ALfilterState_clear(&state->Filter[i]);
}
static ALvoid ALmodulatorState_Destruct(ALmodulatorState *state)
{
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *UNUSED(state), ALCdevice *UNUSED(device))
@@ -123,55 +115,79 @@ static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *UNUSED(state),
return AL_TRUE;
}
static ALvoid ALmodulatorState_update(ALmodulatorState *state, ALCdevice *Device, const ALeffectslot *Slot)
static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
{
ALfloat cw, a;
ALsizei i;
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;
if(props->Modulator.Waveform == AL_RING_MODULATOR_SINUSOID)
state->Process = ModulateSin;
else if(props->Modulator.Waveform == AL_RING_MODULATOR_SAWTOOTH)
state->Process = ModulateSaw;
else /*if(Slot->Params.EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SQUARE)*/
state->Process = ModulateSquare;
state->step = fastf2u(Slot->EffectProps.Modulator.Frequency*WAVEFORM_FRACONE /
state->step = fastf2i(props->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_TAU * Slot->EffectProps.Modulator.HighPassCutoff / Device->Frequency);
cw = cosf(F_TAU * props->Modulator.HighPassCutoff / Device->Frequency);
a = (2.0f-cw) - sqrtf(powf(2.0f-cw, 2.0f) - 1.0f);
state->Filter.a1 = -a;
state->Filter.a2 = 0.0f;
state->Filter.b1 = -a;
state->Filter.b2 = 0.0f;
state->Filter.input_gain = a;
ComputeAmbientGains(Device, Slot->Gain, state->Gain);
}
static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
{
switch(state->Waveform)
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
{
case SINUSOID:
ProcessSin(state, SamplesToDo, SamplesIn, SamplesOut, NumChannels);
break;
case SAWTOOTH:
ProcessSaw(state, SamplesToDo, SamplesIn, SamplesOut, NumChannels);
break;
case SQUARE:
ProcessSquare(state, SamplesToDo, SamplesIn, SamplesOut, NumChannels);
break;
state->Filter[i].b0 = a;
state->Filter[i].b1 = -a;
state->Filter[i].b2 = 0.0f;
state->Filter[i].a1 = -a;
state->Filter[i].a2 = 0.0f;
}
STATIC_CAST(ALeffectState,state)->OutBuffer = Device->FOAOut.Buffer;
STATIC_CAST(ALeffectState,state)->OutChannels = Device->FOAOut.NumChannels;
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
ComputeFirstOrderGains(Device->FOAOut, IdentityMatrixf.m[i],
Slot->Params.Gain, state->Gain[i]);
}
DECLARE_DEFAULT_ALLOCATORS(ALmodulatorState)
static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
const ALsizei step = state->step;
ALsizei index = state->index;
ALsizei base;
DEFINE_ALEFFECTSTATE_VTABLE(ALmodulatorState);
for(base = 0;base < SamplesToDo;)
{
ALfloat temps[2][128];
ALsizei td = mini(128, SamplesToDo-base);
ALsizei i, j, k;
for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
{
ALfilterState_process(&state->Filter[j], temps[0], &SamplesIn[j][base], td);
state->Process(temps[1], temps[0], index, step, td);
for(k = 0;k < NumChannels;k++)
{
ALfloat gain = state->Gain[j][k];
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
continue;
for(i = 0;i < td;i++)
SamplesOut[k][base+i] += gain * temps[1][i];
}
}
for(i = 0;i < td;i++)
{
index += step;
index &= WAVEFORM_FRACMASK;
}
base += td;
}
state->index = index;
}
typedef struct ALmodulatorStateFactory {
@@ -182,14 +198,8 @@ static ALeffectState *ALmodulatorStateFactory_create(ALmodulatorStateFactory *UN
{
ALmodulatorState *state;
state = ALmodulatorState_New(sizeof(*state));
NEW_OBJ0(state, ALmodulatorState)();
if(!state) return NULL;
SET_VTABLE2(ALmodulatorState, ALeffectState, state);
state->index = 0;
state->step = 1;
ALfilterState_clear(&state->Filter);
return STATIC_CAST(ALeffectState, state);
}
+32 -15
View File
@@ -13,12 +13,35 @@ typedef struct ALnullState {
DERIVE_FROM_TYPE(ALeffectState);
} ALnullState;
/* Forward-declare "virtual" functions to define the vtable with. */
static ALvoid ALnullState_Destruct(ALnullState *state);
static ALboolean ALnullState_deviceUpdate(ALnullState *state, ALCdevice *device);
static ALvoid ALnullState_update(ALnullState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props);
static ALvoid ALnullState_process(ALnullState *state, ALsizei samplesToDo, const ALfloatBUFFERSIZE*restrict samplesIn, ALfloatBUFFERSIZE*restrict samplesOut, ALsizei NumChannels);
static void *ALnullState_New(size_t size);
static void ALnullState_Delete(void *ptr);
/* Define the ALeffectState vtable for this type. */
DEFINE_ALEFFECTSTATE_VTABLE(ALnullState);
/* This constructs the effect state. It's called when the object is first
* created. Make sure to call the parent Construct function first, and set the
* vtable!
*/
static void ALnullState_Construct(ALnullState *state)
{
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
SET_VTABLE2(ALnullState, ALeffectState, state);
}
/* This destructs (not free!) the effect state. It's called only when the
* effect slot is no longer used.
* effect slot is no longer used. Make sure to call the parent Destruct
* function before returning!
*/
static ALvoid ALnullState_Destruct(ALnullState* UNUSED(state))
static ALvoid ALnullState_Destruct(ALnullState *state)
{
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
/* This updates the device-dependant effect state. This is called on
@@ -33,7 +56,7 @@ static ALboolean ALnullState_deviceUpdate(ALnullState* UNUSED(state), ALCdevice*
/* 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))
static ALvoid ALnullState_update(ALnullState* UNUSED(state), const ALCdevice* UNUSED(device), const ALeffectslot* UNUSED(slot), const ALeffectProps* UNUSED(props))
{
}
@@ -41,29 +64,26 @@ static ALvoid ALnullState_update(ALnullState* UNUSED(state), ALCdevice* UNUSED(d
* 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), ALfloatBUFFERSIZE*restrict UNUSED(samplesOut), ALuint UNUSED(NumChannels))
static ALvoid ALnullState_process(ALnullState* UNUSED(state), ALsizei UNUSED(samplesToDo), const ALfloatBUFFERSIZE*restrict UNUSED(samplesIn), ALfloatBUFFERSIZE*restrict UNUSED(samplesOut), ALsizei UNUSED(NumChannels))
{
}
/* This allocates memory to store the object, before it gets constructed.
* DECLARE_DEFAULT_ALLOCATORS can be used to declate a default method.
* DECLARE_DEFAULT_ALLOCATORS can be used to declare a default method.
*/
static void *ALnullState_New(size_t size)
{
return malloc(size);
return al_malloc(16, 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.
* DECLARE_DEFAULT_ALLOCATORS can be used to declare a default method.
*/
static void ALnullState_Delete(void *ptr)
{
free(ptr);
al_free(ptr);
}
/* Define the forwards and the ALeffectState vtable for this type. */
DEFINE_ALEFFECTSTATE_VTABLE(ALnullState);
typedef struct ALnullStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
@@ -74,10 +94,8 @@ ALeffectState *ALnullStateFactory_create(ALnullStateFactory *UNUSED(factory))
{
ALnullState *state;
state = ALnullState_New(sizeof(*state));
NEW_OBJ0(state, ALnullState)();
if(!state) return NULL;
/* Set vtables for inherited types. */
SET_VTABLE2(ALnullState, ALeffectState, state);
return STATIC_CAST(ALeffectState, state);
}
@@ -88,7 +106,6 @@ DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALnullStateFactory);
ALeffectStateFactory *ALnullStateFactory_getFactory(void)
{
static ALnullStateFactory NullFactory = { { GET_VTABLE2(ALnullStateFactory, ALeffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &NullFactory);
}
+1620 -1016
View File
File diff suppressed because it is too large Load Diff
+477 -821
View File
File diff suppressed because it is too large Load Diff
+789 -535
View File
File diff suppressed because it is too large Load Diff
+33 -21
View File
@@ -4,37 +4,49 @@
#include "AL/al.h"
#include "AL/alc.h"
#include "alMain.h"
#include "alstring.h"
#include "atomic.h"
enum DevFmtChannels;
struct Hrtf;
/* The maximum number of virtual speakers used to generate HRTF coefficients
* for decoding B-Format.
*/
#define HRTF_AMBI_MAX_CHANNELS 16
typedef struct HrtfEntry {
al_string name;
al_string filename;
const struct Hrtf *hrtf;
} HrtfEntry;
TYPEDEF_VECTOR(HrtfEntry, vector_HrtfEntry)
struct HrtfEntry;
struct Hrtf {
RefCount ref;
ALuint sampleRate;
ALsizei irSize;
ALubyte evCount;
const ALubyte *azCount;
const ALushort *evOffset;
const ALfloat (*coeffs)[2];
const ALubyte (*delays)[2];
};
#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)
void FreeHrtfs(void);
vector_HrtfEntry EnumerateHrtf(const_al_string devname);
void FreeHrtfList(vector_HrtfEntry *list);
vector_EnumeratedHrtf EnumerateHrtf(const_al_string devname);
void FreeHrtfList(vector_EnumeratedHrtf *list);
struct Hrtf *GetLoadedHrtf(struct HrtfEntry *entry);
void Hrtf_IncRef(struct Hrtf *hrtf);
void Hrtf_DecRef(struct Hrtf *hrtf);
ALuint GetHrtfSampleRate(const struct Hrtf *Hrtf);
ALuint GetHrtfIrSize(const struct Hrtf *Hrtf);
void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat spread, ALfloat (*coeffs)[2], ALsizei *delays);
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);
void GetBFormatHrtfCoeffs(const struct Hrtf *Hrtf, const ALuint num_chans, ALfloat (**coeffs_list)[2], ALuint **delay_list);
/**
* Produces HRTF filter coefficients for decoding B-Format, given a set of
* virtual speaker positions and HF/LF matrices for decoding to them. The
* returned coefficients are ordered and scaled according to the matrices.
* Returns the maximum impulse-response length of the generated coefficients.
*/
ALsizei BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsizei NumChannels, const ALfloat (*restrict AmbiPoints)[2], const ALfloat (*restrict AmbiMatrix)[2][MAX_AMBI_COEFFS], ALsizei AmbiCount);
#endif /* ALC_HRTF_H */
+255
View File
@@ -0,0 +1,255 @@
#include "config.h"
#include <math.h>
#include "alu.h"
#include "almalloc.h"
#define RMS_WINDOW_SIZE (1<<7)
#define RMS_WINDOW_MASK (RMS_WINDOW_SIZE-1)
#define RMS_VALUE_MAX (1<<24)
#define LOOKAHEAD_SIZE (1<<13)
#define LOOKAHEAD_MASK (LOOKAHEAD_SIZE-1)
static_assert(RMS_VALUE_MAX < (UINT_MAX / RMS_WINDOW_SIZE), "RMS_VALUE_MAX is too big");
typedef struct Compressor {
ALfloat PreGain;
ALfloat PostGain;
ALboolean SummedLink;
ALfloat AttackMin;
ALfloat AttackMax;
ALfloat ReleaseMin;
ALfloat ReleaseMax;
ALfloat Ratio;
ALfloat Threshold;
ALfloat Knee;
ALuint SampleRate;
ALuint RmsSum;
ALuint *RmsWindow;
ALsizei RmsIndex;
ALfloat Envelope[BUFFERSIZE];
ALfloat EnvLast;
} Compressor;
/* Multichannel compression is linked via one of two modes:
*
* Summed - Absolute sum of all channels.
* Maxed - Absolute maximum of any channel.
*/
static void SumChannels(Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
ALfloat (*restrict OutBuffer)[BUFFERSIZE])
{
ALsizei c, i;
for(i = 0;i < SamplesToDo;i++)
Comp->Envelope[i] = 0.0f;
for(c = 0;c < NumChans;c++)
{
for(i = 0;i < SamplesToDo;i++)
Comp->Envelope[i] += OutBuffer[c][i];
}
for(i = 0;i < SamplesToDo;i++)
Comp->Envelope[i] = fabsf(Comp->Envelope[i]);
}
static void MaxChannels(Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
ALfloat (*restrict OutBuffer)[BUFFERSIZE])
{
ALsizei c, i;
for(i = 0;i < SamplesToDo;i++)
Comp->Envelope[i] = 0.0f;
for(c = 0;c < NumChans;c++)
{
for(i = 0;i < SamplesToDo;i++)
Comp->Envelope[i] = maxf(Comp->Envelope[i], fabsf(OutBuffer[c][i]));
}
}
/* Envelope detection/sensing can be done via:
*
* RMS - Rectangular windowed root mean square of linking stage.
* Peak - Implicit output from linking stage.
*/
static void RmsDetection(Compressor *Comp, const ALsizei SamplesToDo)
{
ALuint sum = Comp->RmsSum;
ALuint *window = Comp->RmsWindow;
ALsizei index = Comp->RmsIndex;
ALsizei i;
for(i = 0;i < SamplesToDo;i++)
{
ALfloat sig = Comp->Envelope[i];
sum -= window[index];
window[index] = fastf2i(minf(sig * sig * 65536.0f, RMS_VALUE_MAX));
sum += window[index];
index = (index + 1) & RMS_WINDOW_MASK;
Comp->Envelope[i] = sqrtf(sum / 65536.0f / RMS_WINDOW_SIZE);
}
Comp->RmsSum = sum;
Comp->RmsIndex = index;
}
/* This isn't a very sophisticated envelope follower, but it gets the job
* done. First, it operates at logarithmic scales to keep transitions
* appropriate for human hearing. Second, it can apply adaptive (automated)
* attack/release adjustments based on the signal.
*/
static void FollowEnvelope(Compressor *Comp, const ALsizei SamplesToDo)
{
ALfloat attackMin = Comp->AttackMin;
ALfloat attackMax = Comp->AttackMax;
ALfloat releaseMin = Comp->ReleaseMin;
ALfloat releaseMax = Comp->ReleaseMax;
ALfloat last = Comp->EnvLast;
ALsizei i;
for(i = 0;i < SamplesToDo;i++)
{
ALfloat env = maxf(-6.0f, log10f(Comp->Envelope[i]));
ALfloat slope = minf(1.0f, fabsf(env - last) / 4.5f);
if(env > last)
last = minf(env, last + lerp(attackMin, attackMax, 1.0f - (slope * slope)));
else
last = maxf(env, last + lerp(releaseMin, releaseMax, 1.0f - (slope * slope)));
Comp->Envelope[i] = last;
}
Comp->EnvLast = last;
}
/* The envelope is converted to control gain with an optional soft knee. */
static void EnvelopeGain(Compressor *Comp, const ALsizei SamplesToDo, const ALfloat Slope)
{
const ALfloat threshold = Comp->Threshold;
const ALfloat knee = Comp->Knee;
ALsizei i;
if(!(knee > 0.0f))
{
for(i = 0;i < SamplesToDo;i++)
{
ALfloat gain = Slope * (threshold - Comp->Envelope[i]);
Comp->Envelope[i] = powf(10.0f, minf(0.0f, gain));
}
}
else
{
const ALfloat lower = threshold - (0.5f * knee);
const ALfloat upper = threshold + (0.5f * knee);
const ALfloat m = 0.5f * Slope / knee;
for(i = 0;i < SamplesToDo;i++)
{
ALfloat env = Comp->Envelope[i];
ALfloat gain;
if(env > lower && env < upper)
gain = m * (env - lower) * (lower - env);
else
gain = Slope * (threshold - env);
Comp->Envelope[i] = powf(10.0f, minf(0.0f, gain));
}
}
}
Compressor *CompressorInit(const ALfloat PreGainDb, const ALfloat PostGainDb,
const ALboolean SummedLink, const ALboolean RmsSensing,
const ALfloat AttackTimeMin, const ALfloat AttackTimeMax,
const ALfloat ReleaseTimeMin, const ALfloat ReleaseTimeMax,
const ALfloat Ratio, const ALfloat ThresholdDb,
const ALfloat KneeDb, const ALuint SampleRate)
{
Compressor *Comp;
size_t size;
ALsizei i;
size = sizeof(*Comp);
if(RmsSensing)
size += sizeof(Comp->RmsWindow[0]) * RMS_WINDOW_SIZE;
Comp = al_calloc(16, size);
Comp->PreGain = powf(10.0f, PreGainDb / 20.0f);
Comp->PostGain = powf(10.0f, PostGainDb / 20.0f);
Comp->SummedLink = SummedLink;
Comp->AttackMin = 1.0f / maxf(0.000001f, AttackTimeMin * SampleRate * logf(10.0f));
Comp->AttackMax = 1.0f / maxf(0.000001f, AttackTimeMax * SampleRate * logf(10.0f));
Comp->ReleaseMin = -1.0f / maxf(0.000001f, ReleaseTimeMin * SampleRate * logf(10.0f));
Comp->ReleaseMax = -1.0f / maxf(0.000001f, ReleaseTimeMax * SampleRate * logf(10.0f));
Comp->Ratio = Ratio;
Comp->Threshold = ThresholdDb / 20.0f;
Comp->Knee = maxf(0.0f, KneeDb / 20.0f);
Comp->SampleRate = SampleRate;
Comp->RmsSum = 0;
if(RmsSensing)
Comp->RmsWindow = (ALuint*)(Comp+1);
else
Comp->RmsWindow = NULL;
Comp->RmsIndex = 0;
for(i = 0;i < BUFFERSIZE;i++)
Comp->Envelope[i] = 0.0f;
Comp->EnvLast = -6.0f;
return Comp;
}
ALuint GetCompressorSampleRate(const Compressor *Comp)
{
return Comp->SampleRate;
}
void ApplyCompression(Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
ALfloat (*restrict OutBuffer)[BUFFERSIZE])
{
ALsizei c, i;
if(Comp->PreGain != 1.0f)
{
for(c = 0;c < NumChans;c++)
{
for(i = 0;i < SamplesToDo;i++)
OutBuffer[c][i] *= Comp->PreGain;
}
}
if(Comp->SummedLink)
SumChannels(Comp, NumChans, SamplesToDo, OutBuffer);
else
MaxChannels(Comp, NumChans, SamplesToDo, OutBuffer);
if(Comp->RmsWindow)
RmsDetection(Comp, SamplesToDo);
FollowEnvelope(Comp, SamplesToDo);
if(Comp->Ratio > 0.0f)
EnvelopeGain(Comp, SamplesToDo, 1.0f - (1.0f / Comp->Ratio));
else
EnvelopeGain(Comp, SamplesToDo, 1.0f);
if(Comp->PostGain != 1.0f)
{
for(i = 0;i < SamplesToDo;i++)
Comp->Envelope[i] *= Comp->PostGain;
}
for(c = 0;c < NumChans;c++)
{
for(i = 0;i < SamplesToDo;i++)
OutBuffer[c][i] *= Comp->Envelope[i];
}
}
+289 -256
View File
@@ -41,65 +41,83 @@
static_assert((INT_MAX>>FRACTIONBITS)/MAX_PITCH > BUFFERSIZE,
"MAX_PITCH and/or BUFFERSIZE are too large for FRACTIONBITS!");
extern inline void InitiatePositionArrays(ALuint frac, ALuint increment, ALuint *frac_arr, ALuint *pos_arr, ALuint size);
alignas(16) union ResamplerCoeffs ResampleCoeffs;
extern inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALint *restrict pos_arr, ALsizei size);
enum Resampler {
PointResampler,
LinearResampler,
FIR4Resampler,
FIR8Resampler,
BSincResampler,
ResamplerDefault = LinearResampler
};
/* FIR8 requires 3 extra samples before the current position, and 4 after. */
static_assert(MAX_PRE_SAMPLES >= 3, "MAX_PRE_SAMPLES must be at least 3!");
static_assert(MAX_POST_SAMPLES >= 4, "MAX_POST_SAMPLES must be at least 4!");
/* BSinc requires up to 11 extra samples before the current position, and 12 after. */
static_assert(MAX_PRE_SAMPLES >= 11, "MAX_PRE_SAMPLES must be at least 11!");
static_assert(MAX_POST_SAMPLES >= 12, "MAX_POST_SAMPLES must be at least 12!");
static HrtfMixerFunc MixHrtfSamples = MixHrtf_C;
enum Resampler ResamplerDefault = LinearResampler;
static MixerFunc MixSamples = Mix_C;
static ResamplerFunc ResampleSamples = Resample_point32_C;
static HrtfMixerFunc MixHrtfSamples = MixHrtf_C;
HrtfMixerBlendFunc MixHrtfBlendSamples = MixHrtfBlend_C;
static inline HrtfMixerFunc SelectHrtfMixer(void)
MixerFunc SelectMixer(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
#ifdef HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
return Mix_SSE;
#endif
return Mix_C;
}
static inline ResamplerFunc SelectResampler(enum Resampler resampler)
RowMixerFunc SelectRowMixer(void)
{
#ifdef HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
return MixRow_Neon;
#endif
#ifdef HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
return MixRow_SSE;
#endif
return MixRow_C;
}
static inline HrtfMixerFunc SelectHrtfMixer(void)
{
#ifdef HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
return MixHrtf_Neon;
#endif
#ifdef HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
return MixHrtf_SSE;
#endif
return MixHrtf_C;
}
static inline HrtfMixerBlendFunc SelectHrtfBlendMixer(void)
{
#ifdef HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
return MixHrtfBlend_Neon;
#endif
#ifdef HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
return MixHrtfBlend_SSE;
#endif
return MixHrtfBlend_C;
}
ResamplerFunc SelectResampler(enum Resampler resampler)
{
switch(resampler)
{
case PointResampler:
return Resample_point32_C;
case LinearResampler:
#ifdef HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
return Resample_lerp32_Neon;
#endif
#ifdef HAVE_SSE4_1
if((CPUCapFlags&CPU_CAP_SSE4_1))
return Resample_lerp32_SSE41;
@@ -110,6 +128,10 @@ static inline ResamplerFunc SelectResampler(enum Resampler resampler)
#endif
return Resample_lerp32_C;
case FIR4Resampler:
#ifdef HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
return Resample_fir4_32_Neon;
#endif
#ifdef HAVE_SSE4_1
if((CPUCapFlags&CPU_CAP_SSE4_1))
return Resample_fir4_32_SSE41;
@@ -119,17 +141,11 @@ static inline ResamplerFunc SelectResampler(enum Resampler resampler)
return Resample_fir4_32_SSE3;
#endif
return Resample_fir4_32_C;
case FIR8Resampler:
#ifdef HAVE_SSE4_1
if((CPUCapFlags&CPU_CAP_SSE4_1))
return Resample_fir8_32_SSE41;
#endif
#ifdef HAVE_SSE3
if((CPUCapFlags&CPU_CAP_SSE3))
return Resample_fir8_32_SSE3;
#endif
return Resample_fir8_32_C;
case BSincResampler:
#ifdef HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
return Resample_bsinc32_Neon;
#endif
#ifdef HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
return Resample_bsinc32_SSE;
@@ -141,162 +157,55 @@ static inline ResamplerFunc SelectResampler(enum Resampler resampler)
}
/* The sinc resampler makes use of a Kaiser window to limit the needed sample
* points to 4 and 8, respectively.
*/
#ifndef M_PI
#define M_PI (3.14159265358979323846)
#endif
static inline double Sinc(double x)
{
if(x == 0.0) return 1.0;
return sin(x*M_PI) / (x*M_PI);
}
/* The zero-order modified Bessel function of the first kind, used for the
* Kaiser window.
*
* I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k)
* = sum_{k=0}^inf ((x / 2)^k / k!)^2
*/
static double BesselI_0(double x)
{
double term, sum, x2, y, last_sum;
int k;
/* Start at k=1 since k=0 is trivial. */
term = 1.0;
sum = 1.0;
x2 = x / 2.0;
k = 1;
/* Let the integration converge until the term of the sum is no longer
* significant.
*/
do {
y = x2 / k;
k ++;
last_sum = sum;
term *= y * y;
sum += term;
} while(sum != last_sum);
return sum;
}
/* Calculate a Kaiser window from the given beta value and a normalized k
* [-1, 1].
*
* w(k) = { I_0(B sqrt(1 - k^2)) / I_0(B), -1 <= k <= 1
* { 0, elsewhere.
*
* Where k can be calculated as:
*
* k = i / l, where -l <= i <= l.
*
* or:
*
* k = 2 i / M - 1, where 0 <= i <= M.
*/
static inline double Kaiser(double b, double k)
{
if(k <= -1.0 || k >= 1.0) return 0.0;
return BesselI_0(b * sqrt(1.0 - (k*k))) / BesselI_0(b);
}
static inline double CalcKaiserBeta(double rejection)
{
if(rejection > 50.0)
return 0.1102 * (rejection - 8.7);
if(rejection >= 21.0)
return (0.5842 * pow(rejection - 21.0, 0.4)) +
(0.07886 * (rejection - 21.0));
return 0.0;
}
static float SincKaiser(double r, double x)
{
/* Limit rippling to -60dB. */
return (float)(Kaiser(CalcKaiserBeta(60.0), x / r) * Sinc(x));
}
void aluInitMixer(void)
{
enum Resampler resampler = ResamplerDefault;
const char *str;
ALuint i;
if(ConfigValueStr(NULL, NULL, "resampler", &str))
{
if(strcasecmp(str, "point") == 0 || strcasecmp(str, "none") == 0)
resampler = PointResampler;
ResamplerDefault = PointResampler;
else if(strcasecmp(str, "linear") == 0)
resampler = LinearResampler;
ResamplerDefault = LinearResampler;
else if(strcasecmp(str, "sinc4") == 0)
resampler = FIR4Resampler;
else if(strcasecmp(str, "sinc8") == 0)
resampler = FIR8Resampler;
ResamplerDefault = FIR4Resampler;
else if(strcasecmp(str, "bsinc") == 0)
resampler = BSincResampler;
else if(strcasecmp(str, "cubic") == 0)
ResamplerDefault = BSincResampler;
else if(strcasecmp(str, "cubic") == 0 || strcasecmp(str, "sinc8") == 0)
{
WARN("Resampler option \"cubic\" is deprecated, using sinc4\n");
resampler = FIR4Resampler;
WARN("Resampler option \"%s\" is deprecated, using sinc4\n", str);
ResamplerDefault = FIR4Resampler;
}
else
{
char *end;
long n = strtol(str, &end, 0);
if(*end == '\0' && (n == PointResampler || n == LinearResampler || n == FIR4Resampler))
resampler = n;
ResamplerDefault = n;
else
WARN("Invalid resampler: %s\n", str);
}
}
if(resampler == FIR8Resampler)
for(i = 0;i < FRACTIONONE;i++)
{
ALdouble mu = (ALdouble)i / FRACTIONONE;
ResampleCoeffs.FIR8[i][0] = SincKaiser(4.0, mu - -3.0);
ResampleCoeffs.FIR8[i][1] = SincKaiser(4.0, mu - -2.0);
ResampleCoeffs.FIR8[i][2] = SincKaiser(4.0, mu - -1.0);
ResampleCoeffs.FIR8[i][3] = SincKaiser(4.0, mu - 0.0);
ResampleCoeffs.FIR8[i][4] = SincKaiser(4.0, mu - 1.0);
ResampleCoeffs.FIR8[i][5] = SincKaiser(4.0, mu - 2.0);
ResampleCoeffs.FIR8[i][6] = SincKaiser(4.0, mu - 3.0);
ResampleCoeffs.FIR8[i][7] = SincKaiser(4.0, mu - 4.0);
}
else if(resampler == FIR4Resampler)
for(i = 0;i < FRACTIONONE;i++)
{
ALdouble mu = (ALdouble)i / FRACTIONONE;
ResampleCoeffs.FIR4[i][0] = SincKaiser(2.0, mu - -1.0);
ResampleCoeffs.FIR4[i][1] = SincKaiser(2.0, mu - 0.0);
ResampleCoeffs.FIR4[i][2] = SincKaiser(2.0, mu - 1.0);
ResampleCoeffs.FIR4[i][3] = SincKaiser(2.0, mu - 2.0);
}
MixHrtfBlendSamples = SelectHrtfBlendMixer();
MixHrtfSamples = SelectHrtfMixer();
MixSamples = SelectMixer();
ResampleSamples = SelectResampler(resampler);
}
static inline ALfloat Sample_ALbyte(ALbyte val)
{ return val * (1.0f/127.0f); }
{ return val * (1.0f/128.0f); }
static inline ALfloat Sample_ALshort(ALshort val)
{ return val * (1.0f/32767.0f); }
{ return val * (1.0f/32768.0f); }
static inline ALfloat Sample_ALfloat(ALfloat val)
{ return val; }
#define DECL_TEMPLATE(T) \
static inline void Load_##T(ALfloat *dst, const T *src, ALuint srcstep, ALuint samples)\
static inline void Load_##T(ALfloat *dst, const T *src, ALint srcstep, ALsizei samples)\
{ \
ALuint i; \
ALsizei i; \
for(i = 0;i < samples;i++) \
dst[i] = Sample_##T(src[i*srcstep]); \
}
@@ -307,7 +216,7 @@ DECL_TEMPLATE(ALfloat)
#undef DECL_TEMPLATE
static void LoadSamples(ALfloat *dst, const ALvoid *src, ALuint srcstep, enum FmtType srctype, ALuint samples)
static void LoadSamples(ALfloat *dst, const ALvoid *src, ALint srcstep, enum FmtType srctype, ALsizei samples)
{
switch(srctype)
{
@@ -323,9 +232,9 @@ static void LoadSamples(ALfloat *dst, const ALvoid *src, ALuint srcstep, enum Fm
}
}
static inline void SilenceSamples(ALfloat *dst, ALuint samples)
static inline void SilenceSamples(ALfloat *dst, ALsizei samples)
{
ALuint i;
ALsizei i;
for(i = 0;i < samples;i++)
dst[i] = 0.0f;
}
@@ -333,9 +242,9 @@ static inline void SilenceSamples(ALfloat *dst, ALuint samples)
static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter,
ALfloat *restrict dst, const ALfloat *restrict src,
ALuint numsamples, enum ActiveFilters type)
ALsizei numsamples, enum ActiveFilters type)
{
ALuint i;
ALsizei i;
switch(type)
{
case AF_None:
@@ -356,7 +265,7 @@ static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter
for(i = 0;i < numsamples;)
{
ALfloat temp[256];
ALuint todo = minu(256, numsamples-i);
ALsizei todo = mini(256, numsamples-i);
ALfilterState_process(lpfilter, temp, src+i, todo);
ALfilterState_process(hpfilter, dst+i, temp, todo);
@@ -368,39 +277,45 @@ static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter
}
ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint SamplesToDo)
ALboolean MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALsizei SamplesToDo)
{
ResamplerFunc Resample;
ALbufferlistitem *BufferListItem;
ALuint DataPosInt, DataPosFrac;
ALboolean Looping;
ALuint increment;
ALenum State;
ALuint OutPos;
ALuint NumChannels;
ALuint SampleSize;
ALbufferlistitem *BufferLoopItem;
ALsizei NumChannels, SampleSize;
ResamplerFunc Resample;
ALsizei DataPosInt;
ALsizei DataPosFrac;
ALint64 DataSize64;
ALuint IrSize;
ALuint chan, j;
ALint increment;
ALsizei Counter;
ALsizei OutPos;
ALsizei IrSize;
bool isplaying;
bool firstpass;
ALsizei chan;
ALsizei send;
/* Get source info */
State = Source->state;
BufferListItem = ATOMIC_LOAD(&Source->current_buffer);
DataPosInt = Source->position;
DataPosFrac = Source->position_fraction;
Looping = Source->Looping;
NumChannels = Source->NumChannels;
SampleSize = Source->SampleSize;
isplaying = true; /* Will only be called while playing. */
DataPosInt = ATOMIC_LOAD(&voice->position, almemory_order_acquire);
DataPosFrac = ATOMIC_LOAD(&voice->position_fraction, almemory_order_relaxed);
BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
BufferLoopItem = ATOMIC_LOAD(&voice->loop_buffer, almemory_order_relaxed);
NumChannels = voice->NumChannels;
SampleSize = voice->SampleSize;
increment = voice->Step;
IrSize = (Device->Hrtf ? GetHrtfIrSize(Device->Hrtf) : 0);
IrSize = (Device->HrtfHandle ? Device->HrtfHandle->irSize : 0);
Resample = ((increment == FRACTIONONE && DataPosFrac == 0) ?
Resample_copy32_C : ResampleSamples);
Resample_copy32_C : voice->Resampler);
Counter = (voice->Flags&VOICE_IS_FADING) ? SamplesToDo : 0;
firstpass = true;
OutPos = 0;
do {
ALuint SrcBufferSize, DstBufferSize;
ALsizei SrcBufferSize, DstBufferSize;
/* Figure out how many buffer samples will be needed */
DataSize64 = SamplesToDo-OutPos;
@@ -409,7 +324,7 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
DataSize64 >>= FRACTIONBITS;
DataSize64 += MAX_POST_SAMPLES+MAX_PRE_SAMPLES;
SrcBufferSize = (ALuint)mini64(DataSize64, BUFFERSIZE);
SrcBufferSize = (ALsizei)mini64(DataSize64, BUFFERSIZE);
/* Figure out how many samples we can actually mix from this. */
DataSize64 = SrcBufferSize;
@@ -417,8 +332,8 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
DataSize64 <<= FRACTIONBITS;
DataSize64 -= DataPosFrac;
DstBufferSize = (ALuint)((DataSize64+(increment-1)) / increment);
DstBufferSize = minu(DstBufferSize, (SamplesToDo-OutPos));
DstBufferSize = (ALsizei)((DataSize64+(increment-1)) / increment);
DstBufferSize = mini(DstBufferSize, (SamplesToDo-OutPos));
/* Some mixers like having a multiple of 4, so try to give that unless
* this is the last update. */
@@ -429,7 +344,7 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
{
const ALfloat *ResampledData;
ALfloat *SrcData = Device->SourceData;
ALuint SrcDataSize;
ALsizei SrcDataSize;
/* Load the previous samples into the source data first. */
memcpy(SrcData, voice->PrevSamples[chan], MAX_PRE_SAMPLES*sizeof(ALfloat));
@@ -439,23 +354,22 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
{
const ALbuffer *ALBuffer = BufferListItem->buffer;
const ALubyte *Data = ALBuffer->data;
ALuint DataSize;
ALuint pos;
ALsizei DataSize;
/* Offset buffer data to current channel */
Data += chan*SampleSize;
/* If current pos is beyond the loop range, do not loop */
if(Looping == AL_FALSE || DataPosInt >= (ALuint)ALBuffer->LoopEnd)
if(!BufferLoopItem || DataPosInt >= ALBuffer->LoopEnd)
{
Looping = AL_FALSE;
BufferLoopItem = NULL;
/* Load what's left to play from the source buffer, and
* clear the rest of the temp buffer */
pos = DataPosInt;
DataSize = minu(SrcBufferSize - SrcDataSize, ALBuffer->SampleLen - pos);
DataSize = minu(SrcBufferSize - SrcDataSize,
ALBuffer->SampleLen - DataPosInt);
LoadSamples(&SrcData[SrcDataSize], &Data[pos * NumChannels*SampleSize],
LoadSamples(&SrcData[SrcDataSize], &Data[DataPosInt * NumChannels*SampleSize],
NumChannels, ALBuffer->FmtType, DataSize);
SrcDataSize += DataSize;
@@ -464,23 +378,21 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
}
else
{
ALuint LoopStart = ALBuffer->LoopStart;
ALuint LoopEnd = ALBuffer->LoopEnd;
ALsizei LoopStart = ALBuffer->LoopStart;
ALsizei LoopEnd = ALBuffer->LoopEnd;
/* Load what's left of this loop iteration, then load
* repeats of the loop section */
pos = DataPosInt;
DataSize = LoopEnd - pos;
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
DataSize = minu(SrcBufferSize - SrcDataSize, LoopEnd - DataPosInt);
LoadSamples(&SrcData[SrcDataSize], &Data[pos * NumChannels*SampleSize],
LoadSamples(&SrcData[SrcDataSize], &Data[DataPosInt * NumChannels*SampleSize],
NumChannels, ALBuffer->FmtType, DataSize);
SrcDataSize += DataSize;
DataSize = LoopEnd-LoopStart;
while(SrcBufferSize > SrcDataSize)
{
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
DataSize = mini(SrcBufferSize - SrcDataSize, DataSize);
LoadSamples(&SrcData[SrcDataSize], &Data[LoopStart * NumChannels*SampleSize],
NumChannels, ALBuffer->FmtType, DataSize);
@@ -492,7 +404,7 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
{
/* Crawl the buffer queue to fill in the temp buffer */
ALbufferlistitem *tmpiter = BufferListItem;
ALuint pos = DataPosInt;
ALsizei pos = DataPosInt;
while(tmpiter && SrcBufferSize > SrcDataSize)
{
@@ -500,7 +412,7 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
if((ALBuffer=tmpiter->buffer) != NULL)
{
const ALubyte *Data = ALBuffer->data;
ALuint DataSize = ALBuffer->SampleLen;
ALsizei DataSize = ALBuffer->SampleLen;
/* Skip the data already played */
if(DataSize <= pos)
@@ -517,9 +429,9 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
SrcDataSize += DataSize;
}
}
tmpiter = tmpiter->next;
if(!tmpiter && Looping)
tmpiter = ATOMIC_LOAD(&Source->queue);
tmpiter = ATOMIC_LOAD(&tmpiter->next, almemory_order_acquire);
if(!tmpiter && BufferLoopItem)
tmpiter = BufferLoopItem;
else if(!tmpiter)
{
SilenceSamples(&SrcData[SrcDataSize], SrcBufferSize - SrcDataSize);
@@ -535,43 +447,164 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
);
/* Now resample, then filter and mix to the appropriate outputs. */
ResampledData = Resample(&voice->SincState,
ResampledData = Resample(&voice->ResampleState,
&SrcData[MAX_PRE_SAMPLES], DataPosFrac, increment,
Device->ResampledData, DstBufferSize
);
{
DirectParams *parms = &voice->Direct;
DirectParams *parms = &voice->Direct.Params[chan];
const ALfloat *samples;
samples = DoFilters(
&parms->Filters[chan].LowPass, &parms->Filters[chan].HighPass,
Device->FilteredData, ResampledData, DstBufferSize,
parms->Filters[chan].ActiveType
&parms->LowPass, &parms->HighPass, Device->FilteredData,
ResampledData, DstBufferSize, voice->Direct.FilterType
);
if(!voice->IsHrtf)
MixSamples(samples, parms->OutChannels, parms->OutBuffer, parms->Gains[chan],
parms->Counter, OutPos, DstBufferSize);
if(!(voice->Flags&VOICE_HAS_HRTF))
{
if(!Counter)
memcpy(parms->Gains.Current, parms->Gains.Target,
sizeof(parms->Gains.Current));
if(!(voice->Flags&VOICE_HAS_NFC))
MixSamples(samples, voice->Direct.Channels, voice->Direct.Buffer,
parms->Gains.Current, parms->Gains.Target, Counter, OutPos,
DstBufferSize
);
else
{
ALfloat *nfcsamples = Device->NFCtrlData;
ALsizei chanoffset = 0;
MixSamples(samples,
voice->Direct.ChannelsPerOrder[0], voice->Direct.Buffer,
parms->Gains.Current, parms->Gains.Target, Counter, OutPos,
DstBufferSize
);
chanoffset += voice->Direct.ChannelsPerOrder[0];
#define APPLY_NFC_MIX(order) \
if(voice->Direct.ChannelsPerOrder[order] > 0) \
{ \
NfcFilterUpdate##order(&parms->NFCtrlFilter[order-1], nfcsamples, \
samples, DstBufferSize); \
MixSamples(nfcsamples, voice->Direct.ChannelsPerOrder[order], \
voice->Direct.Buffer+chanoffset, parms->Gains.Current+chanoffset, \
parms->Gains.Target+chanoffset, Counter, OutPos, DstBufferSize \
); \
chanoffset += voice->Direct.ChannelsPerOrder[order]; \
}
APPLY_NFC_MIX(1)
APPLY_NFC_MIX(2)
APPLY_NFC_MIX(3)
#undef APPLY_NFC_MIX
}
}
else
MixHrtfSamples(parms->OutBuffer, samples, parms->Counter, voice->Offset,
OutPos, IrSize, &parms->Hrtf[chan].Params,
&parms->Hrtf[chan].State, DstBufferSize);
{
MixHrtfParams hrtfparams;
ALsizei fademix = 0;
int lidx, ridx;
lidx = GetChannelIdxByName(Device->RealOut, FrontLeft);
ridx = GetChannelIdxByName(Device->RealOut, FrontRight);
assert(lidx != -1 && ridx != -1);
if(!Counter)
{
/* No fading, just overwrite the old HRTF params. */
parms->Hrtf.Old = parms->Hrtf.Target;
}
else if(!(parms->Hrtf.Old.Gain > GAIN_SILENCE_THRESHOLD))
{
/* The old HRTF params are silent, so overwrite the old
* coefficients with the new, and reset the old gain to
* 0. The future mix will then fade from silence.
*/
parms->Hrtf.Old = parms->Hrtf.Target;
parms->Hrtf.Old.Gain = 0.0f;
}
else if(firstpass)
{
ALfloat gain;
/* Fade between the coefficients over 128 samples. */
fademix = mini(DstBufferSize, 128);
/* The new coefficients need to fade in completely
* since they're replacing the old ones. To keep the
* gain fading consistent, interpolate between the old
* and new target gains given how much of the fade time
* this mix handles.
*/
gain = lerp(parms->Hrtf.Old.Gain, parms->Hrtf.Target.Gain,
minf(1.0f, (ALfloat)fademix/Counter));
hrtfparams.Coeffs = SAFE_CONST(ALfloat2*,parms->Hrtf.Target.Coeffs);
hrtfparams.Delay[0] = parms->Hrtf.Target.Delay[0];
hrtfparams.Delay[1] = parms->Hrtf.Target.Delay[1];
hrtfparams.Gain = 0.0f;
hrtfparams.GainStep = gain / (ALfloat)fademix;
MixHrtfBlendSamples(
voice->Direct.Buffer[lidx], voice->Direct.Buffer[ridx],
samples, voice->Offset, OutPos, IrSize, &parms->Hrtf.Old,
&hrtfparams, &parms->Hrtf.State, fademix
);
/* Update the old parameters with the result. */
parms->Hrtf.Old = parms->Hrtf.Target;
if(fademix < Counter)
parms->Hrtf.Old.Gain = hrtfparams.Gain;
}
if(fademix < DstBufferSize)
{
ALsizei todo = DstBufferSize - fademix;
ALfloat gain = parms->Hrtf.Target.Gain;
/* Interpolate the target gain if the gain fading lasts
* longer than this mix.
*/
if(Counter > DstBufferSize)
gain = lerp(parms->Hrtf.Old.Gain, gain,
(ALfloat)todo/(Counter-fademix));
hrtfparams.Coeffs = SAFE_CONST(ALfloat2*,parms->Hrtf.Target.Coeffs);
hrtfparams.Delay[0] = parms->Hrtf.Target.Delay[0];
hrtfparams.Delay[1] = parms->Hrtf.Target.Delay[1];
hrtfparams.Gain = parms->Hrtf.Old.Gain;
hrtfparams.GainStep = (gain - parms->Hrtf.Old.Gain) / (ALfloat)todo;
MixHrtfSamples(
voice->Direct.Buffer[lidx], voice->Direct.Buffer[ridx],
samples+fademix, voice->Offset+fademix, OutPos+fademix, IrSize,
&hrtfparams, &parms->Hrtf.State, todo
);
/* Store the interpolated gain or the final target gain
* depending if the fade is done.
*/
if(DstBufferSize < Counter)
parms->Hrtf.Old.Gain = gain;
else
parms->Hrtf.Old.Gain = parms->Hrtf.Target.Gain;
}
}
}
for(j = 0;j < Device->NumAuxSends;j++)
for(send = 0;send < Device->NumAuxSends;send++)
{
SendParams *parms = &voice->Send[j];
SendParams *parms = &voice->Send[send].Params[chan];
const ALfloat *samples;
if(!parms->OutBuffer)
if(!voice->Send[send].Buffer)
continue;
samples = DoFilters(
&parms->Filters[chan].LowPass, &parms->Filters[chan].HighPass,
Device->FilteredData, ResampledData, DstBufferSize,
parms->Filters[chan].ActiveType
&parms->LowPass, &parms->HighPass, Device->FilteredData,
ResampledData, DstBufferSize, voice->Send[send].FilterType
);
if(!Counter)
memcpy(parms->Gains.Current, parms->Gains.Target,
sizeof(parms->Gains.Current));
MixSamples(samples, voice->Send[send].Channels, voice->Send[send].Buffer,
parms->Gains.Current, parms->Gains.Target, Counter, OutPos, DstBufferSize
);
MixSamples(samples, 1, parms->OutBuffer, &parms->Gains[chan],
parms->Counter, OutPos, DstBufferSize);
}
}
/* Update positions */
@@ -581,17 +614,16 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
OutPos += DstBufferSize;
voice->Offset += DstBufferSize;
voice->Direct.Counter = maxu(voice->Direct.Counter, DstBufferSize) - DstBufferSize;
for(j = 0;j < Device->NumAuxSends;j++)
voice->Send[j].Counter = maxu(voice->Send[j].Counter, DstBufferSize) - DstBufferSize;
Counter = maxi(DstBufferSize, Counter) - DstBufferSize;
firstpass = false;
/* Handle looping sources */
while(1)
{
const ALbuffer *ALBuffer;
ALuint DataSize = 0;
ALuint LoopStart = 0;
ALuint LoopEnd = 0;
ALsizei DataSize = 0;
ALsizei LoopStart = 0;
ALsizei LoopEnd = 0;
if((ALBuffer=BufferListItem->buffer) != NULL)
{
@@ -602,7 +634,7 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
break;
}
if(Looping && Source->SourceType == AL_STATIC)
if(BufferLoopItem && Source->SourceType == AL_STATIC)
{
assert(LoopEnd > LoopStart);
DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart;
@@ -612,14 +644,13 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
if(DataSize > DataPosInt)
break;
if(!(BufferListItem=BufferListItem->next))
BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
if(!BufferListItem)
{
if(Looping)
BufferListItem = ATOMIC_LOAD(&Source->queue);
else
BufferListItem = BufferLoopItem;
if(!BufferListItem)
{
State = AL_STOPPED;
BufferListItem = NULL;
isplaying = false;
DataPosInt = 0;
DataPosFrac = 0;
break;
@@ -628,11 +659,13 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
DataPosInt -= DataSize;
}
} while(State == AL_PLAYING && OutPos < SamplesToDo);
} while(isplaying && OutPos < SamplesToDo);
voice->Flags |= VOICE_IS_FADING;
/* Update source info */
Source->state = State;
ATOMIC_STORE(&Source->current_buffer, BufferListItem);
Source->position = DataPosInt;
Source->position_fraction = DataPosFrac;
ATOMIC_STORE(&voice->position, DataPosInt, almemory_order_relaxed);
ATOMIC_STORE(&voice->position_fraction, DataPosFrac, almemory_order_relaxed);
ATOMIC_STORE(&voice->current_buffer, BufferListItem, almemory_order_release);
return isplaying;
}
+100 -73
View File
@@ -8,18 +8,17 @@
#include "alAuxEffectSlot.h"
static inline ALfloat point32(const ALfloat *vals, ALuint UNUSED(frac))
static inline ALfloat point32(const ALfloat *restrict vals, ALsizei UNUSED(frac))
{ return vals[0]; }
static inline ALfloat lerp32(const ALfloat *vals, ALuint frac)
static inline ALfloat lerp32(const ALfloat *restrict vals, ALsizei frac)
{ return lerp(vals[0], vals[1], frac * (1.0f/FRACTIONONE)); }
static inline ALfloat fir4_32(const ALfloat *vals, ALuint frac)
static inline ALfloat fir4_32(const ALfloat *restrict vals, ALsizei frac)
{ return resample_fir4(vals[-1], vals[0], vals[1], vals[2], frac); }
static inline ALfloat fir8_32(const ALfloat *vals, ALuint frac)
{ return resample_fir8(vals[-3], vals[-2], vals[-1], vals[0], vals[1], vals[2], vals[3], vals[4], frac); }
const ALfloat *Resample_copy32_C(const BsincState* UNUSED(state), const ALfloat *src, ALuint UNUSED(frac),
ALuint UNUSED(increment), ALfloat *restrict dst, ALuint numsamples)
const ALfloat *Resample_copy32_C(const InterpState* UNUSED(state),
const ALfloat *restrict src, ALsizei UNUSED(frac), ALint UNUSED(increment),
ALfloat *restrict dst, ALsizei numsamples)
{
#if defined(HAVE_SSE) || defined(HAVE_NEON)
/* Avoid copying the source data if it's aligned like the destination. */
@@ -31,11 +30,11 @@ const ALfloat *Resample_copy32_C(const BsincState* UNUSED(state), const ALfloat
}
#define DECL_TEMPLATE(Sampler) \
const ALfloat *Resample_##Sampler##_C(const BsincState* UNUSED(state), \
const ALfloat *src, ALuint frac, ALuint increment, \
ALfloat *restrict dst, ALuint numsamples) \
const ALfloat *Resample_##Sampler##_C(const InterpState* UNUSED(state), \
const ALfloat *restrict src, ALsizei frac, ALint increment, \
ALfloat *restrict dst, ALsizei numsamples) \
{ \
ALuint i; \
ALsizei i; \
for(i = 0;i < numsamples;i++) \
{ \
dst[i] = Sampler(src, frac); \
@@ -50,21 +49,20 @@ const ALfloat *Resample_##Sampler##_C(const BsincState* UNUSED(state), \
DECL_TEMPLATE(point32)
DECL_TEMPLATE(lerp32)
DECL_TEMPLATE(fir4_32)
DECL_TEMPLATE(fir8_32)
#undef DECL_TEMPLATE
const ALfloat *Resample_bsinc32_C(const BsincState *state, const ALfloat *src, ALuint frac,
ALuint increment, ALfloat *restrict dst, ALuint dstlen)
const ALfloat *Resample_bsinc32_C(const InterpState *state, const ALfloat *restrict src,
ALsizei frac, ALint increment, ALfloat *restrict dst,
ALsizei dstlen)
{
const ALfloat *fil, *scd, *phd, *spd;
const ALfloat sf = state->sf;
const ALuint m = state->m;
const ALint l = state->l;
ALuint j_f, pi, i;
const ALfloat sf = state->bsinc.sf;
const ALsizei m = state->bsinc.m;
ALsizei j_f, pi, i;
ALfloat pf, r;
ALint j_s;
src += state->bsinc.l;
for(i = 0;i < dstlen;i++)
{
// Calculate the phase index and factor.
@@ -73,16 +71,15 @@ const ALfloat *Resample_bsinc32_C(const BsincState *state, const ALfloat *src, A
pf = (frac & ((1<<FRAC_PHASE_BITDIFF)-1)) * (1.0f/(1<<FRAC_PHASE_BITDIFF));
#undef FRAC_PHASE_BITDIFF
fil = state->coeffs[pi].filter;
scd = state->coeffs[pi].scDelta;
phd = state->coeffs[pi].phDelta;
spd = state->coeffs[pi].spDelta;
fil = ASSUME_ALIGNED(state->bsinc.coeffs[pi].filter, 16);
scd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].scDelta, 16);
phd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].phDelta, 16);
spd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].spDelta, 16);
// Apply the scale and phase interpolated filter.
r = 0.0f;
for(j_f = 0,j_s = l;j_f < m;j_f++,j_s++)
r += (fil[j_f] + sf*scd[j_f] + pf*(phd[j_f] + sf*spd[j_f])) *
src[j_s];
for(j_f = 0;j_f < m;j_f++)
r += (fil[j_f] + sf*scd[j_f] + pf*(phd[j_f] + sf*spd[j_f])) * src[j_f];
dst[i] = r;
frac += increment;
@@ -93,84 +90,93 @@ const ALfloat *Resample_bsinc32_C(const BsincState *state, const ALfloat *src, A
}
void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples)
void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALsizei numsamples)
{
ALuint i;
for(i = 0;i < numsamples;i++)
*(dst++) = ALfilterState_processSingle(filter, *(src++));
}
static inline void SetupCoeffs(ALfloat (*restrict OutCoeffs)[2],
const HrtfParams *hrtfparams,
ALuint IrSize, ALuint Counter)
{
ALuint c;
for(c = 0;c < IrSize;c++)
ALsizei i;
if(numsamples > 1)
{
OutCoeffs[c][0] = hrtfparams->Coeffs[c][0] - (hrtfparams->CoeffStep[c][0]*Counter);
OutCoeffs[c][1] = hrtfparams->Coeffs[c][1] - (hrtfparams->CoeffStep[c][1]*Counter);
dst[0] = filter->b0 * src[0] +
filter->b1 * filter->x[0] +
filter->b2 * filter->x[1] -
filter->a1 * filter->y[0] -
filter->a2 * filter->y[1];
dst[1] = filter->b0 * src[1] +
filter->b1 * src[0] +
filter->b2 * filter->x[0] -
filter->a1 * dst[0] -
filter->a2 * filter->y[0];
for(i = 2;i < numsamples;i++)
dst[i] = filter->b0 * src[i] +
filter->b1 * src[i-1] +
filter->b2 * src[i-2] -
filter->a1 * dst[i-1] -
filter->a2 * dst[i-2];
filter->x[0] = src[i-1];
filter->x[1] = src[i-2];
filter->y[0] = dst[i-1];
filter->y[1] = dst[i-2];
}
else if(numsamples == 1)
{
dst[0] = filter->b0 * src[0] +
filter->b1 * filter->x[0] +
filter->b2 * filter->x[1] -
filter->a1 * filter->y[0] -
filter->a2 * filter->y[1];
filter->x[1] = filter->x[0];
filter->x[0] = src[0];
filter->y[1] = filter->y[0];
filter->y[0] = dst[0];
}
}
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],
static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
const ALsizei IrSize,
const ALfloat (*restrict Coeffs)[2],
ALfloat left, ALfloat right)
{
ALuint c;
ALsizei c;
for(c = 0;c < IrSize;c++)
{
const ALuint off = (Offset+c)&HRIR_MASK;
const ALsizei off = (Offset+c)&HRIR_MASK;
Values[off][0] += Coeffs[c][0] * left;
Values[off][1] += Coeffs[c][1] * right;
}
}
#define MixHrtf MixHrtf_C
#define MixHrtfBlend MixHrtfBlend_C
#define MixDirectHrtf MixDirectHrtf_C
#include "mixer_inc.c"
#undef MixHrtf
void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize)
void Mix_C(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
ALsizei BufferSize)
{
ALfloat gain, step;
ALuint c;
ALfloat gain, delta, step;
ALsizei c;
delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
for(c = 0;c < OutChans;c++)
{
ALuint pos = 0;
gain = Gains[c].Current;
step = Gains[c].Step;
if(step != 0.0f && Counter > 0)
ALsizei pos = 0;
gain = CurrentGains[c];
step = (TargetGains[c] - gain) * delta;
if(fabsf(step) > FLT_EPSILON)
{
ALuint minsize = minu(BufferSize, Counter);
ALsizei minsize = mini(BufferSize, Counter);
for(;pos < minsize;pos++)
{
OutBuffer[c][OutPos+pos] += data[pos]*gain;
gain += step;
}
if(pos == Counter)
gain = Gains[c].Target;
Gains[c].Current = gain;
gain = TargetGains[c];
CurrentGains[c] = gain;
}
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
@@ -179,3 +185,24 @@ void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[B
OutBuffer[c][OutPos+pos] += data[pos]*gain;
}
}
/* Basically the inverse of the above. Rather than one input going to multiple
* outputs (each with its own gain), it's multiple inputs (each with its own
* gain) going to one output. This applies one row (vs one column) of a matrix
* transform. And as the matrices are more or less static once set up, no
* stepping is necessary.
*/
void MixRow_C(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans, ALsizei InPos, ALsizei BufferSize)
{
ALsizei c, i;
for(c = 0;c < InChans;c++)
{
ALfloat gain = Gains[c];
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
continue;
for(i = 0;i < BufferSize;i++)
OutBuffer[i] += data[c][InPos+i] * gain;
}
}
+92 -43
View File
@@ -8,73 +8,122 @@
struct MixGains;
struct HrtfParams;
struct MixHrtfParams;
struct HrtfState;
/* C resamplers */
const ALfloat *Resample_copy32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
const ALfloat *Resample_point32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
const ALfloat *Resample_lerp32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
const ALfloat *Resample_fir4_32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
const ALfloat *Resample_fir8_32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
const ALfloat *Resample_bsinc32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
const ALfloat *Resample_copy32_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
const ALfloat *Resample_point32_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
const ALfloat *Resample_lerp32_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
const ALfloat *Resample_fir4_32_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
const ALfloat *Resample_bsinc32_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei 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);
void MixHrtf_C(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
const ALfloat *data, ALsizei Offset, ALsizei OutPos,
const ALsizei IrSize, struct MixHrtfParams *hrtfparams,
struct HrtfState *hrtfstate, ALsizei BufferSize);
void MixHrtfBlend_C(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
const ALfloat *data, ALsizei Offset, ALsizei OutPos,
const ALsizei IrSize, const HrtfParams *oldparams,
MixHrtfParams *newparams, HrtfState *hrtfstate,
ALsizei BufferSize);
void MixDirectHrtf_C(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
const ALfloat *data, ALsizei Offset, const ALsizei IrSize,
const ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2],
ALsizei BufferSize);
void Mix_C(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
ALsizei BufferSize);
void MixRow_C(ALfloat *OutBuffer, const ALfloat *Gains,
const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans,
ALsizei InPos, ALsizei 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);
void MixHrtf_SSE(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
const ALfloat *data, ALsizei Offset, ALsizei OutPos,
const ALsizei IrSize, struct MixHrtfParams *hrtfparams,
struct HrtfState *hrtfstate, ALsizei BufferSize);
void MixHrtfBlend_SSE(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
const ALfloat *data, ALsizei Offset, ALsizei OutPos,
const ALsizei IrSize, const HrtfParams *oldparams,
MixHrtfParams *newparams, HrtfState *hrtfstate,
ALsizei BufferSize);
void MixDirectHrtf_SSE(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
const ALfloat *data, ALsizei Offset, const ALsizei IrSize,
const ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2],
ALsizei BufferSize);
void Mix_SSE(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
ALsizei BufferSize);
void MixRow_SSE(ALfloat *OutBuffer, const ALfloat *Gains,
const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans,
ALsizei InPos, ALsizei BufferSize);
/* SSE resamplers */
inline void InitiatePositionArrays(ALuint frac, ALuint increment, ALuint *frac_arr, ALuint *pos_arr, ALuint size)
inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALint *restrict pos_arr, ALsizei size)
{
ALuint i;
ALsizei i;
pos_arr[0] = 0;
frac_arr[0] = frac;
for(i = 1;i < size;i++)
{
ALuint frac_tmp = frac_arr[i-1] + increment;
ALint 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_bsinc32_SSE(const BsincState *state, const ALfloat *src, ALuint frac,
ALuint increment, ALfloat *restrict dst, ALuint dstlen);
const ALfloat *Resample_lerp32_SSE2(const InterpState *state, const ALfloat *restrict src,
ALsizei frac, ALint increment, ALfloat *restrict dst,
ALsizei numsamples);
const ALfloat *Resample_lerp32_SSE41(const InterpState *state, const ALfloat *restrict src,
ALsizei frac, ALint increment, ALfloat *restrict dst,
ALsizei numsamples);
const ALfloat *Resample_lerp32_SSE2(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment,
ALfloat *restrict dst, ALuint numsamples);
const ALfloat *Resample_lerp32_SSE41(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment,
ALfloat *restrict dst, ALuint numsamples);
const ALfloat *Resample_fir4_32_SSE3(const InterpState *state, const ALfloat *restrict src,
ALsizei frac, ALint increment, ALfloat *restrict dst,
ALsizei numsamples);
const ALfloat *Resample_fir4_32_SSE41(const InterpState *state, const ALfloat *restrict src,
ALsizei frac, ALint increment, ALfloat *restrict dst,
ALsizei numsamples);
const ALfloat *Resample_fir4_32_SSE3(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment,
ALfloat *restrict dst, ALuint numsamples);
const ALfloat *Resample_fir4_32_SSE41(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment,
ALfloat *restrict dst, ALuint numsamples);
const ALfloat *Resample_fir8_32_SSE3(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment,
ALfloat *restrict dst, ALuint numsamples);
const ALfloat *Resample_fir8_32_SSE41(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment,
ALfloat *restrict dst, ALuint numsamples);
const ALfloat *Resample_bsinc32_SSE(const InterpState *state, const ALfloat *restrict src,
ALsizei frac, ALint increment, ALfloat *restrict dst,
ALsizei dstlen);
/* 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);
void MixHrtf_Neon(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
const ALfloat *data, ALsizei Offset, ALsizei OutPos,
const ALsizei IrSize, struct MixHrtfParams *hrtfparams,
struct HrtfState *hrtfstate, ALsizei BufferSize);
void MixHrtfBlend_Neon(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
const ALfloat *data, ALsizei Offset, ALsizei OutPos,
const ALsizei IrSize, const HrtfParams *oldparams,
MixHrtfParams *newparams, HrtfState *hrtfstate,
ALsizei BufferSize);
void MixDirectHrtf_Neon(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
const ALfloat *data, ALsizei Offset, const ALsizei IrSize,
const ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2],
ALsizei BufferSize);
void Mix_Neon(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
ALsizei BufferSize);
void MixRow_Neon(ALfloat *OutBuffer, const ALfloat *Gains,
const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans,
ALsizei InPos, ALsizei BufferSize);
/* Neon resamplers */
const ALfloat *Resample_lerp32_Neon(const InterpState *state, const ALfloat *restrict src,
ALsizei frac, ALint increment, ALfloat *restrict dst,
ALsizei numsamples);
const ALfloat *Resample_fir4_32_Neon(const InterpState *state, const ALfloat *restrict src,
ALsizei frac, ALint increment, ALfloat *restrict dst,
ALsizei numsamples);
const ALfloat *Resample_bsinc32_Neon(const InterpState *state, const ALfloat *restrict src,
ALsizei frac, ALint increment, ALfloat *restrict dst,
ALsizei dstlen);
#endif /* MIXER_DEFS_H */
+92 -57
View File
@@ -6,74 +6,109 @@
#include "hrtf.h"
#include "mixer_defs.h"
#include "align.h"
#include "alu.h"
static inline void SetupCoeffs(ALfloat (*restrict OutCoeffs)[2],
const HrtfParams *hrtfparams,
ALuint IrSize, ALuint Counter);
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],
static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
const ALsizei irSize,
const 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)
void MixHrtf(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
const ALfloat *data, ALsizei Offset, ALsizei OutPos,
const ALsizei IrSize, MixHrtfParams *hrtfparams, HrtfState *hrtfstate,
ALsizei BufferSize)
{
alignas(16) ALfloat Coeffs[HRIR_LENGTH][2];
ALuint Delay[2];
const ALfloat (*Coeffs)[2] = ASSUME_ALIGNED(hrtfparams->Coeffs, 16);
const ALsizei Delay[2] = { hrtfparams->Delay[0], hrtfparams->Delay[1] };
ALfloat gainstep = hrtfparams->GainStep;
ALfloat gain = hrtfparams->Gain;
ALfloat left, right;
ALuint pos;
ALsizei i;
SetupCoeffs(Coeffs, hrtfparams, IrSize, Counter);
Delay[0] = hrtfparams->Delay[0] - (hrtfparams->DelayStep[0]*Counter);
Delay[1] = hrtfparams->Delay[1] - (hrtfparams->DelayStep[1]*Counter);
pos = 0;
for(;pos < BufferSize && pos < Counter;pos++)
LeftOut += OutPos;
RightOut += OutPos;
for(i = 0;i < BufferSize;i++)
{
hrtfstate->History[Offset&HRTF_HISTORY_MASK] = data[pos];
left = lerp(hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS))&HRTF_HISTORY_MASK],
hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS)-1)&HRTF_HISTORY_MASK],
(Delay[0]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE));
right = lerp(hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS))&HRTF_HISTORY_MASK],
hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS)-1)&HRTF_HISTORY_MASK],
(Delay[1]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE));
hrtfstate->History[Offset&HRTF_HISTORY_MASK] = *(data++);
left = hrtfstate->History[(Offset-Delay[0])&HRTF_HISTORY_MASK]*gain;
right = hrtfstate->History[(Offset-Delay[1])&HRTF_HISTORY_MASK]*gain;
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[0][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0];
OutBuffer[1][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1];
OutPos++;
}
Delay[0] >>= HRTFDELAY_BITS;
Delay[1] >>= HRTFDELAY_BITS;
for(;pos < BufferSize;pos++)
{
hrtfstate->History[Offset&HRTF_HISTORY_MASK] = data[pos];
left = hrtfstate->History[(Offset-Delay[0])&HRTF_HISTORY_MASK];
right = hrtfstate->History[(Offset-Delay[1])&HRTF_HISTORY_MASK];
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f;
Offset++;
hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][0] = 0.0f;
hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][1] = 0.0f;
ApplyCoeffs(Offset, hrtfstate->Values, IrSize, Coeffs, left, right);
OutBuffer[0][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0];
OutBuffer[1][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1];
OutPos++;
*(LeftOut++) += hrtfstate->Values[Offset&HRIR_MASK][0];
*(RightOut++) += hrtfstate->Values[Offset&HRIR_MASK][1];
gain += gainstep;
Offset++;
}
hrtfparams->Gain = gain;
}
void MixHrtfBlend(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
const ALfloat *data, ALsizei Offset, ALsizei OutPos,
const ALsizei IrSize, const HrtfParams *oldparams,
MixHrtfParams *newparams, HrtfState *hrtfstate,
ALsizei BufferSize)
{
const ALfloat (*OldCoeffs)[2] = ASSUME_ALIGNED(oldparams->Coeffs, 16);
const ALsizei OldDelay[2] = { oldparams->Delay[0], oldparams->Delay[1] };
ALfloat oldGain = oldparams->Gain;
ALfloat oldGainStep = -oldGain / (ALfloat)BufferSize;
const ALfloat (*NewCoeffs)[2] = ASSUME_ALIGNED(newparams->Coeffs, 16);
const ALsizei NewDelay[2] = { newparams->Delay[0], newparams->Delay[1] };
ALfloat newGain = newparams->Gain;
ALfloat newGainStep = newparams->GainStep;
ALfloat left, right;
ALsizei i;
LeftOut += OutPos;
RightOut += OutPos;
for(i = 0;i < BufferSize;i++)
{
hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][0] = 0.0f;
hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][1] = 0.0f;
hrtfstate->History[Offset&HRTF_HISTORY_MASK] = *(data++);
left = hrtfstate->History[(Offset-OldDelay[0])&HRTF_HISTORY_MASK]*oldGain;
right = hrtfstate->History[(Offset-OldDelay[1])&HRTF_HISTORY_MASK]*oldGain;
ApplyCoeffs(Offset, hrtfstate->Values, IrSize, OldCoeffs, left, right);
left = hrtfstate->History[(Offset-NewDelay[0])&HRTF_HISTORY_MASK]*newGain;
right = hrtfstate->History[(Offset-NewDelay[1])&HRTF_HISTORY_MASK]*newGain;
ApplyCoeffs(Offset, hrtfstate->Values, IrSize, NewCoeffs, left, right);
*(LeftOut++) += hrtfstate->Values[Offset&HRIR_MASK][0];
*(RightOut++) += hrtfstate->Values[Offset&HRIR_MASK][1];
oldGain += oldGainStep;
newGain += newGainStep;
Offset++;
}
newparams->Gain = newGain;
}
void MixDirectHrtf(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
const ALfloat *data, ALsizei Offset, const ALsizei IrSize,
const ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2],
ALsizei BufferSize)
{
ALfloat insample;
ALsizei i;
for(i = 0;i < BufferSize;i++)
{
Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f;
Offset++;
insample = *(data++);
ApplyCoeffs(Offset, Values, IrSize, Coeffs, insample, insample);
*(LeftOut++) += Values[Offset&HRIR_MASK][0];
*(RightOut++) += Values[Offset&HRIR_MASK][1];
}
}
+248 -56
View File
@@ -7,65 +7,195 @@
#include "alMain.h"
#include "alu.h"
#include "hrtf.h"
#include "mixer_defs.h"
static inline void SetupCoeffs(ALfloat (*restrict OutCoeffs)[2],
const HrtfParams *hrtfparams,
ALuint IrSize, ALuint Counter)
const ALfloat *Resample_lerp32_Neon(const InterpState* UNUSED(state),
const ALfloat *restrict src, ALsizei frac, ALint increment,
ALfloat *restrict dst, ALsizei numsamples)
{
ALuint c;
float32x4_t counter4;
const int32x4_t increment4 = vdupq_n_s32(increment*4);
const float32x4_t fracOne4 = vdupq_n_f32(1.0f/FRACTIONONE);
const int32x4_t fracMask4 = vdupq_n_s32(FRACTIONMASK);
alignas(16) ALint pos_[4];
alignas(16) ALsizei frac_[4];
int32x4_t pos4;
int32x4_t frac4;
ALsizei i;
InitiatePositionArrays(frac, increment, frac_, pos_, 4);
frac4 = vld1q_s32(frac_);
pos4 = vld1q_s32(pos_);
for(i = 0;numsamples-i > 3;i += 4)
{
float32x2_t counter2 = vdup_n_f32(-(float)Counter);
counter4 = vcombine_f32(counter2, counter2);
const float32x4_t val1 = (float32x4_t){src[pos_[0]], src[pos_[1]], src[pos_[2]], src[pos_[3]]};
const float32x4_t val2 = (float32x4_t){src[pos_[0]+1], src[pos_[1]+1], src[pos_[2]+1], src[pos_[3]+1]};
/* val1 + (val2-val1)*mu */
const float32x4_t r0 = vsubq_f32(val2, val1);
const float32x4_t mu = vmulq_f32(vcvtq_f32_s32(frac4), fracOne4);
const float32x4_t out = vmlaq_f32(val1, mu, r0);
vst1q_f32(&dst[i], out);
frac4 = vaddq_s32(frac4, increment4);
pos4 = vaddq_s32(pos4, vshrq_n_s32(frac4, FRACTIONBITS));
frac4 = vandq_s32(frac4, fracMask4);
vst1q_s32(pos_, pos4);
}
for(c = 0;c < IrSize;c += 2)
if(i < numsamples)
{
float32x4_t step4 = vld1q_f32((float32_t*)hrtfparams->CoeffStep[c]);
float32x4_t coeffs = vld1q_f32((float32_t*)hrtfparams->Coeffs[c]);
coeffs = vmlaq_f32(coeffs, step4, counter4);
vst1q_f32((float32_t*)OutCoeffs[c], coeffs);
/* NOTE: These four elements represent the position *after* the last
* four samples, so the lowest element is the next position to
* resample.
*/
ALint pos = pos_[0];
frac = vgetq_lane_s32(frac4, 0);
do {
dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
frac += increment;
pos += frac>>FRACTIONBITS;
frac &= FRACTIONMASK;
} while(++i < numsamples);
}
return dst;
}
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 ALfloat *Resample_fir4_32_Neon(const InterpState* UNUSED(state),
const ALfloat *restrict src, ALsizei frac, ALint increment,
ALfloat *restrict dst, ALsizei numsamples)
{
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]);
const int32x4_t increment4 = vdupq_n_s32(increment*4);
const int32x4_t fracMask4 = vdupq_n_s32(FRACTIONMASK);
alignas(16) ALint pos_[4];
alignas(16) ALsizei frac_[4];
int32x4_t pos4;
int32x4_t frac4;
ALsizei i;
vals = vmlaq_f32(vals, coefs, leftright4);
coefs = vaddq_f32(coefs, deltas);
InitiatePositionArrays(frac, increment, frac_, pos_, 4);
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);
frac4 = vld1q_s32(frac_);
pos4 = vld1q_s32(pos_);
--src;
for(i = 0;numsamples-i > 3;i += 4)
{
const float32x4_t val0 = vld1q_f32(&src[pos_[0]]);
const float32x4_t val1 = vld1q_f32(&src[pos_[1]]);
const float32x4_t val2 = vld1q_f32(&src[pos_[2]]);
const float32x4_t val3 = vld1q_f32(&src[pos_[3]]);
float32x4_t k0 = vld1q_f32(sinc4Tab[frac_[0]]);
float32x4_t k1 = vld1q_f32(sinc4Tab[frac_[1]]);
float32x4_t k2 = vld1q_f32(sinc4Tab[frac_[2]]);
float32x4_t k3 = vld1q_f32(sinc4Tab[frac_[3]]);
float32x4_t out;
k0 = vmulq_f32(k0, val0);
k1 = vmulq_f32(k1, val1);
k2 = vmulq_f32(k2, val2);
k3 = vmulq_f32(k3, val3);
k0 = vcombine_f32(vpadd_f32(vget_low_f32(k0), vget_high_f32(k0)),
vpadd_f32(vget_low_f32(k1), vget_high_f32(k1)));
k2 = vcombine_f32(vpadd_f32(vget_low_f32(k2), vget_high_f32(k2)),
vpadd_f32(vget_low_f32(k3), vget_high_f32(k3)));
out = vcombine_f32(vpadd_f32(vget_low_f32(k0), vget_high_f32(k0)),
vpadd_f32(vget_low_f32(k2), vget_high_f32(k2)));
vst1q_f32(&dst[i], out);
frac4 = vaddq_s32(frac4, increment4);
pos4 = vaddq_s32(pos4, vshrq_n_s32(frac4, FRACTIONBITS));
frac4 = vandq_s32(frac4, fracMask4);
vst1q_s32(pos_, pos4);
vst1q_s32(frac_, frac4);
}
if(i < numsamples)
{
/* NOTE: These four elements represent the position *after* the last
* four samples, so the lowest element is the next position to
* resample.
*/
ALint pos = pos_[0];
frac = frac_[0];
do {
dst[i] = resample_fir4(src[pos], src[pos+1], src[pos+2], src[pos+3], frac);
frac += increment;
pos += frac>>FRACTIONBITS;
frac &= FRACTIONMASK;
} while(++i < numsamples);
}
return dst;
}
static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint IrSize,
ALfloat (*restrict Coeffs)[2],
const ALfloat *Resample_bsinc32_Neon(const InterpState *state,
const ALfloat *restrict src, ALsizei frac, ALint increment,
ALfloat *restrict dst, ALsizei dstlen)
{
const float32x4_t sf4 = vdupq_n_f32(state->bsinc.sf);
const ALsizei m = state->bsinc.m;
const ALfloat *fil, *scd, *phd, *spd;
ALsizei pi, i, j;
float32x4_t r4;
ALfloat pf;
src += state->bsinc.l;
for(i = 0;i < dstlen;i++)
{
// Calculate the phase index and factor.
#define FRAC_PHASE_BITDIFF (FRACTIONBITS-BSINC_PHASE_BITS)
pi = frac >> FRAC_PHASE_BITDIFF;
pf = (frac & ((1<<FRAC_PHASE_BITDIFF)-1)) * (1.0f/(1<<FRAC_PHASE_BITDIFF));
#undef FRAC_PHASE_BITDIFF
fil = ASSUME_ALIGNED(state->bsinc.coeffs[pi].filter, 16);
scd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].scDelta, 16);
phd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].phDelta, 16);
spd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].spDelta, 16);
// Apply the scale and phase interpolated filter.
r4 = vdupq_n_f32(0.0f);
{
const float32x4_t pf4 = vdupq_n_f32(pf);
for(j = 0;j < m;j+=4)
{
/* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */
const float32x4_t f4 = vmlaq_f32(vmlaq_f32(vld1q_f32(&fil[j]),
sf4, vld1q_f32(&scd[j])),
pf4, vmlaq_f32(vld1q_f32(&phd[j]),
sf4, vld1q_f32(&spd[j])
)
);
/* r += f*src */
r4 = vmlaq_f32(r4, f4, vld1q_f32(&src[j]));
}
}
r4 = vaddq_f32(r4, vcombine_f32(vrev64_f32(vget_high_f32(r4)),
vrev64_f32(vget_low_f32(r4))));
dst[i] = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0);
frac += increment;
src += frac>>FRACTIONBITS;
frac &= FRACTIONMASK;
}
return dst;
}
static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
const ALsizei IrSize,
const ALfloat (*restrict Coeffs)[2],
ALfloat left, ALfloat right)
{
ALuint c;
ALsizei c;
float32x4_t leftright4;
{
float32x2_t leftright2 = vdup_n_f32(0.0);
@@ -73,10 +203,12 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
leftright2 = vset_lane_f32(right, leftright2, 1);
leftright4 = vcombine_f32(leftright2, leftright2);
}
Values = ASSUME_ALIGNED(Values, 16);
Coeffs = ASSUME_ALIGNED(Coeffs, 16);
for(c = 0;c < IrSize;c += 2)
{
const ALuint o0 = (Offset+c)&HRIR_MASK;
const ALuint o1 = (o0+1)&HRIR_MASK;
const ALsizei o0 = (Offset+c)&HRIR_MASK;
const ALsizei 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]);
@@ -89,36 +221,68 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
}
#define MixHrtf MixHrtf_Neon
#define MixHrtfBlend MixHrtfBlend_Neon
#define MixDirectHrtf MixDirectHrtf_Neon
#include "mixer_inc.c"
#undef MixHrtf
void Mix_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize)
void Mix_Neon(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
ALsizei BufferSize)
{
ALfloat gain, step;
ALfloat gain, delta, step;
float32x4_t gain4;
ALuint c;
ALsizei c;
data = ASSUME_ALIGNED(data, 16);
OutBuffer = ASSUME_ALIGNED(OutBuffer, 16);
delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
for(c = 0;c < OutChans;c++)
{
ALuint pos = 0;
gain = Gains[c].Current;
step = Gains[c].Step;
if(step != 0.0f && Counter > 0)
ALsizei pos = 0;
gain = CurrentGains[c];
step = (TargetGains[c] - gain) * delta;
if(fabsf(step) > FLT_EPSILON)
{
ALuint minsize = minu(BufferSize, Counter);
ALsizei minsize = mini(BufferSize, Counter);
/* Mix with applying gain steps in aligned multiples of 4. */
if(minsize-pos > 3)
{
float32x4_t step4;
gain4 = vsetq_lane_f32(gain, gain4, 0);
gain4 = vsetq_lane_f32(gain + step, gain4, 1);
gain4 = vsetq_lane_f32(gain + step + step, gain4, 2);
gain4 = vsetq_lane_f32(gain + step + step + step, gain4, 3);
step4 = vdupq_n_f32(step + step + step + step);
do {
const float32x4_t val4 = vld1q_f32(&data[pos]);
float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]);
dry4 = vmlaq_f32(dry4, val4, gain4);
gain4 = vaddq_f32(gain4, step4);
vst1q_f32(&OutBuffer[c][OutPos+pos], dry4);
pos += 4;
} while(minsize-pos > 3);
/* NOTE: gain4 now represents the next four gains after the
* last four mixed samples, so the lowest element represents
* the next gain to apply.
*/
gain = vgetq_lane_f32(gain4, 0);
}
/* Mix with applying left over gain steps that aren't aligned multiples of 4. */
for(;pos < minsize;pos++)
{
OutBuffer[c][OutPos+pos] += data[pos]*gain;
gain += step;
}
if(pos == Counter)
gain = Gains[c].Target;
Gains[c].Current = gain;
gain = TargetGains[c];
CurrentGains[c] = gain;
/* Mix until pos is aligned with 4 or the mix is done. */
minsize = minu(BufferSize, (pos+3)&~3);
minsize = mini(BufferSize, (pos+3)&~3);
for(;pos < minsize;pos++)
OutBuffer[c][OutPos+pos] += data[pos]*gain;
}
@@ -137,3 +301,31 @@ void Mix_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer
OutBuffer[c][OutPos+pos] += data[pos]*gain;
}
}
void MixRow_Neon(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans, ALsizei InPos, ALsizei BufferSize)
{
float32x4_t gain4;
ALsizei c;
data = ASSUME_ALIGNED(data, 16);
OutBuffer = ASSUME_ALIGNED(OutBuffer, 16);
for(c = 0;c < InChans;c++)
{
ALsizei pos = 0;
ALfloat gain = Gains[c];
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
continue;
gain4 = vdupq_n_f32(gain);
for(;BufferSize-pos > 3;pos += 4)
{
const float32x4_t val4 = vld1q_f32(&data[c][InPos+pos]);
float32x4_t dry4 = vld1q_f32(&OutBuffer[pos]);
dry4 = vmlaq_f32(dry4, val4, gain4);
vst1q_f32(&OutBuffer[pos], dry4);
}
for(;pos < BufferSize;pos++)
OutBuffer[pos] += data[c][InPos+pos]*gain;
}
}
+75 -124
View File
@@ -12,18 +12,18 @@
#include "mixer_defs.h"
const ALfloat *Resample_bsinc32_SSE(const BsincState *state, const ALfloat *src, ALuint frac,
ALuint increment, ALfloat *restrict dst, ALuint dstlen)
const ALfloat *Resample_bsinc32_SSE(const InterpState *state, const ALfloat *restrict src,
ALsizei frac, ALint increment, ALfloat *restrict dst,
ALsizei dstlen)
{
const __m128 sf4 = _mm_set1_ps(state->sf);
const ALuint m = state->m;
const ALint l = state->l;
const __m128 sf4 = _mm_set1_ps(state->bsinc.sf);
const ALsizei m = state->bsinc.m;
const ALfloat *fil, *scd, *phd, *spd;
ALuint pi, j_f, i;
ALsizei pi, i, j;
ALfloat pf;
ALint j_s;
__m128 r4;
src += state->bsinc.l;
for(i = 0;i < dstlen;i++)
{
// Calculate the phase index and factor.
@@ -32,32 +32,30 @@ const ALfloat *Resample_bsinc32_SSE(const BsincState *state, const ALfloat *src,
pf = (frac & ((1<<FRAC_PHASE_BITDIFF)-1)) * (1.0f/(1<<FRAC_PHASE_BITDIFF));
#undef FRAC_PHASE_BITDIFF
fil = state->coeffs[pi].filter;
scd = state->coeffs[pi].scDelta;
phd = state->coeffs[pi].phDelta;
spd = state->coeffs[pi].spDelta;
fil = ASSUME_ALIGNED(state->bsinc.coeffs[pi].filter, 16);
scd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].scDelta, 16);
phd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].phDelta, 16);
spd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].spDelta, 16);
// Apply the scale and phase interpolated filter.
r4 = _mm_setzero_ps();
{
const __m128 pf4 = _mm_set1_ps(pf);
for(j_f = 0,j_s = l;j_f < m;j_f+=4,j_s+=4)
#define LD4(x) _mm_load_ps(x)
#define ULD4(x) _mm_loadu_ps(x)
#define MLA4(x, y, z) _mm_add_ps(x, _mm_mul_ps(y, z))
for(j = 0;j < m;j+=4)
{
const __m128 f4 = _mm_add_ps(
_mm_add_ps(
_mm_load_ps(&fil[j_f]),
_mm_mul_ps(sf4, _mm_load_ps(&scd[j_f]))
),
_mm_mul_ps(
pf4,
_mm_add_ps(
_mm_load_ps(&phd[j_f]),
_mm_mul_ps(sf4, _mm_load_ps(&spd[j_f]))
)
)
/* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */
const __m128 f4 = MLA4(MLA4(LD4(&fil[j]), sf4, LD4(&scd[j])),
pf4, MLA4(LD4(&phd[j]), sf4, LD4(&spd[j]))
);
r4 = _mm_add_ps(r4, _mm_mul_ps(f4, _mm_loadu_ps(&src[j_s])));
/* r += f*src */
r4 = MLA4(r4, f4, ULD4(&src[j]));
}
#undef MLA4
#undef ULD4
#undef LD4
}
r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3)));
r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4));
@@ -71,99 +69,22 @@ const ALfloat *Resample_bsinc32_SSE(const BsincState *state, const ALfloat *src,
}
static inline void SetupCoeffs(ALfloat (*restrict OutCoeffs)[2],
const HrtfParams *hrtfparams,
ALuint IrSize, ALuint Counter)
{
const __m128 counter4 = _mm_set1_ps((float)Counter);
__m128 coeffs, step4;
ALuint i;
for(i = 0;i < IrSize;i += 2)
{
step4 = _mm_load_ps(&hrtfparams->CoeffStep[i][0]);
coeffs = _mm_load_ps(&hrtfparams->Coeffs[i][0]);
coeffs = _mm_sub_ps(coeffs, _mm_mul_ps(step4, counter4));
_mm_store_ps(&OutCoeffs[i][0], coeffs);
}
}
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],
static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
const ALsizei IrSize,
const 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;
ALsizei i;
Values = ASSUME_ALIGNED(Values, 16);
Coeffs = ASSUME_ALIGNED(Coeffs, 16);
if((Offset&1))
{
const ALuint o0 = Offset&HRIR_MASK;
const ALuint o1 = (Offset+IrSize-1)&HRIR_MASK;
const ALsizei o0 = Offset&HRIR_MASK;
const ALsizei o1 = (Offset+IrSize-1)&HRIR_MASK;
__m128 imp0, imp1;
coeffs = _mm_load_ps(&Coeffs[0][0]);
@@ -173,7 +94,7 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
_mm_storel_pi((__m64*)&Values[o0][0], vals);
for(i = 1;i < IrSize-1;i += 2)
{
const ALuint o2 = (Offset+i)&HRIR_MASK;
const ALsizei o2 = (Offset+i)&HRIR_MASK;
coeffs = _mm_load_ps(&Coeffs[i+1][0]);
vals = _mm_load_ps(&Values[o2][0]);
@@ -192,7 +113,7 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
{
for(i = 0;i < IrSize;i += 2)
{
const ALuint o = (Offset + i)&HRIR_MASK;
const ALsizei o = (Offset + i)&HRIR_MASK;
coeffs = _mm_load_ps(&Coeffs[i][0]);
vals = _mm_load_ps(&Values[o][0]);
@@ -203,25 +124,30 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
}
#define MixHrtf MixHrtf_SSE
#define MixHrtfBlend MixHrtfBlend_SSE
#define MixDirectHrtf MixDirectHrtf_SSE
#include "mixer_inc.c"
#undef MixHrtf
void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize)
void Mix_SSE(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
ALsizei BufferSize)
{
ALfloat gain, step;
ALfloat gain, delta, step;
__m128 gain4;
ALuint c;
ALsizei c;
delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
for(c = 0;c < OutChans;c++)
{
ALuint pos = 0;
gain = Gains[c].Current;
step = Gains[c].Step;
if(step != 0.0f && Counter > 0)
ALsizei pos = 0;
gain = CurrentGains[c];
step = (TargetGains[c] - gain) * delta;
if(fabsf(step) > FLT_EPSILON)
{
ALuint minsize = minu(BufferSize, Counter);
ALsizei minsize = mini(BufferSize, Counter);
/* Mix with applying gain steps in aligned multiples of 4. */
if(minsize-pos > 3)
{
@@ -254,11 +180,11 @@ void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)
gain += step;
}
if(pos == Counter)
gain = Gains[c].Target;
Gains[c].Current = gain;
gain = TargetGains[c];
CurrentGains[c] = gain;
/* Mix until pos is aligned with 4 or the mix is done. */
minsize = minu(BufferSize, (pos+3)&~3);
minsize = mini(BufferSize, (pos+3)&~3);
for(;pos < minsize;pos++)
OutBuffer[c][OutPos+pos] += data[pos]*gain;
}
@@ -277,3 +203,28 @@ void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)
OutBuffer[c][OutPos+pos] += data[pos]*gain;
}
}
void MixRow_SSE(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans, ALsizei InPos, ALsizei BufferSize)
{
__m128 gain4;
ALsizei c;
for(c = 0;c < InChans;c++)
{
ALsizei pos = 0;
ALfloat gain = Gains[c];
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
continue;
gain4 = _mm_set1_ps(gain);
for(;BufferSize-pos > 3;pos += 4)
{
const __m128 val4 = _mm_load_ps(&data[c][InPos+pos]);
__m128 dry4 = _mm_load_ps(&OutBuffer[pos]);
dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
_mm_store_ps(&OutBuffer[pos], dry4);
}
for(;pos < BufferSize;pos++)
OutBuffer[pos] += data[c][InPos+pos]*gain;
}
}
+7 -6
View File
@@ -27,17 +27,18 @@
#include "mixer_defs.h"
const ALfloat *Resample_lerp32_SSE2(const BsincState* UNUSED(state), const ALfloat *src, ALuint frac, ALuint increment,
ALfloat *restrict dst, ALuint numsamples)
const ALfloat *Resample_lerp32_SSE2(const InterpState* UNUSED(state),
const ALfloat *restrict src, ALsizei frac, ALint increment,
ALfloat *restrict dst, ALsizei 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_;
union { alignas(16) ALint i[4]; float f[4]; } pos_;
union { alignas(16) ALsizei i[4]; float f[4]; } frac_;
__m128i frac4, pos4;
ALuint pos;
ALuint i;
ALint pos;
ALsizei i;
InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
+11 -76
View File
@@ -31,16 +31,17 @@
#include "mixer_defs.h"
const ALfloat *Resample_fir4_32_SSE3(const BsincState* UNUSED(state), const ALfloat *src, ALuint frac, ALuint increment,
ALfloat *restrict dst, ALuint numsamples)
const ALfloat *Resample_fir4_32_SSE3(const InterpState* UNUSED(state),
const ALfloat *restrict src, ALsizei frac, ALint increment,
ALfloat *restrict dst, ALsizei numsamples)
{
const __m128i increment4 = _mm_set1_epi32(increment*4);
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_;
union { alignas(16) ALint i[4]; float f[4]; } pos_;
union { alignas(16) ALsizei i[4]; float f[4]; } frac_;
__m128i frac4, pos4;
ALuint pos;
ALuint i;
ALint pos;
ALsizei i;
InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
@@ -54,10 +55,10 @@ const ALfloat *Resample_fir4_32_SSE3(const BsincState* UNUSED(state), const ALfl
const __m128 val1 = _mm_loadu_ps(&src[pos_.i[1]]);
const __m128 val2 = _mm_loadu_ps(&src[pos_.i[2]]);
const __m128 val3 = _mm_loadu_ps(&src[pos_.i[3]]);
__m128 k0 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[0]]);
__m128 k1 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[1]]);
__m128 k2 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[2]]);
__m128 k3 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[3]]);
__m128 k0 = _mm_load_ps(sinc4Tab[frac_.i[0]]);
__m128 k1 = _mm_load_ps(sinc4Tab[frac_.i[1]]);
__m128 k2 = _mm_load_ps(sinc4Tab[frac_.i[2]]);
__m128 k3 = _mm_load_ps(sinc4Tab[frac_.i[3]]);
__m128 out;
k0 = _mm_mul_ps(k0, val0);
@@ -94,69 +95,3 @@ const ALfloat *Resample_fir4_32_SSE3(const BsincState* UNUSED(state), const ALfl
}
return dst;
}
const ALfloat *Resample_fir8_32_SSE3(const BsincState* UNUSED(state), const ALfloat *src, ALuint frac, ALuint increment,
ALfloat *restrict dst, ALuint numsamples)
{
const __m128i increment4 = _mm_set1_epi32(increment*4);
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, j;
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));
src -= 3;
for(i = 0;numsamples-i > 3;i += 4)
{
__m128 out[2];
for(j = 0;j < 8;j+=4)
{
const __m128 val0 = _mm_loadu_ps(&src[pos_.i[0]+j]);
const __m128 val1 = _mm_loadu_ps(&src[pos_.i[1]+j]);
const __m128 val2 = _mm_loadu_ps(&src[pos_.i[2]+j]);
const __m128 val3 = _mm_loadu_ps(&src[pos_.i[3]+j]);
__m128 k0 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[0]][j]);
__m128 k1 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[1]][j]);
__m128 k2 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[2]][j]);
__m128 k3 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[3]][j]);
k0 = _mm_mul_ps(k0, val0);
k1 = _mm_mul_ps(k1, val1);
k2 = _mm_mul_ps(k2, val2);
k3 = _mm_mul_ps(k3, val3);
k0 = _mm_hadd_ps(k0, k1);
k2 = _mm_hadd_ps(k2, k3);
out[j>>2] = _mm_hadd_ps(k0, k2);
}
out[0] = _mm_add_ps(out[0], out[1]);
_mm_store_ps(&dst[i], out[0]);
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));
_mm_store_ps(frac_.f, _mm_castsi128_ps(frac4));
}
pos = pos_.i[0];
frac = frac_.i[0];
for(;i < numsamples;i++)
{
dst[i] = resample_fir8(src[pos ], src[pos+1], src[pos+2], src[pos+3],
src[pos+4], src[pos+5], src[pos+6], src[pos+7], frac);
frac += increment;
pos += frac>>FRACTIONBITS;
frac &= FRACTIONMASK;
}
return dst;
}
+18 -88
View File
@@ -28,17 +28,18 @@
#include "mixer_defs.h"
const ALfloat *Resample_lerp32_SSE41(const BsincState* UNUSED(state), const ALfloat *src, ALuint frac, ALuint increment,
ALfloat *restrict dst, ALuint numsamples)
const ALfloat *Resample_lerp32_SSE41(const InterpState* UNUSED(state),
const ALfloat *restrict src, ALsizei frac, ALint increment,
ALfloat *restrict dst, ALsizei 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_;
union { alignas(16) ALint i[4]; float f[4]; } pos_;
union { alignas(16) ALsizei i[4]; float f[4]; } frac_;
__m128i frac4, pos4;
ALuint pos;
ALuint i;
ALint pos;
ALsizei i;
InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
@@ -84,16 +85,17 @@ const ALfloat *Resample_lerp32_SSE41(const BsincState* UNUSED(state), const ALfl
return dst;
}
const ALfloat *Resample_fir4_32_SSE41(const BsincState* UNUSED(state), const ALfloat *src, ALuint frac, ALuint increment,
ALfloat *restrict dst, ALuint numsamples)
const ALfloat *Resample_fir4_32_SSE41(const InterpState* UNUSED(state),
const ALfloat *restrict src, ALsizei frac, ALint increment,
ALfloat *restrict dst, ALsizei numsamples)
{
const __m128i increment4 = _mm_set1_epi32(increment*4);
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_;
union { alignas(16) ALint i[4]; float f[4]; } pos_;
union { alignas(16) ALsizei i[4]; float f[4]; } frac_;
__m128i frac4, pos4;
ALuint pos;
ALuint i;
ALint pos;
ALsizei i;
InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
@@ -107,10 +109,10 @@ const ALfloat *Resample_fir4_32_SSE41(const BsincState* UNUSED(state), const ALf
const __m128 val1 = _mm_loadu_ps(&src[pos_.i[1]]);
const __m128 val2 = _mm_loadu_ps(&src[pos_.i[2]]);
const __m128 val3 = _mm_loadu_ps(&src[pos_.i[3]]);
__m128 k0 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[0]]);
__m128 k1 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[1]]);
__m128 k2 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[2]]);
__m128 k3 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[3]]);
__m128 k0 = _mm_load_ps(sinc4Tab[frac_.i[0]]);
__m128 k1 = _mm_load_ps(sinc4Tab[frac_.i[1]]);
__m128 k2 = _mm_load_ps(sinc4Tab[frac_.i[2]]);
__m128 k3 = _mm_load_ps(sinc4Tab[frac_.i[3]]);
__m128 out;
k0 = _mm_mul_ps(k0, val0);
@@ -150,75 +152,3 @@ const ALfloat *Resample_fir4_32_SSE41(const BsincState* UNUSED(state), const ALf
}
return dst;
}
const ALfloat *Resample_fir8_32_SSE41(const BsincState* UNUSED(state), const ALfloat *src, ALuint frac, ALuint increment,
ALfloat *restrict dst, ALuint numsamples)
{
const __m128i increment4 = _mm_set1_epi32(increment*4);
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, j;
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));
src -= 3;
for(i = 0;numsamples-i > 3;i += 4)
{
__m128 out[2];
for(j = 0;j < 8;j+=4)
{
const __m128 val0 = _mm_loadu_ps(&src[pos_.i[0]+j]);
const __m128 val1 = _mm_loadu_ps(&src[pos_.i[1]+j]);
const __m128 val2 = _mm_loadu_ps(&src[pos_.i[2]+j]);
const __m128 val3 = _mm_loadu_ps(&src[pos_.i[3]+j]);
__m128 k0 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[0]][j]);
__m128 k1 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[1]][j]);
__m128 k2 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[2]][j]);
__m128 k3 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[3]][j]);
k0 = _mm_mul_ps(k0, val0);
k1 = _mm_mul_ps(k1, val1);
k2 = _mm_mul_ps(k2, val2);
k3 = _mm_mul_ps(k3, val3);
k0 = _mm_hadd_ps(k0, k1);
k2 = _mm_hadd_ps(k2, k3);
out[j>>2] = _mm_hadd_ps(k0, k2);
}
out[0] = _mm_add_ps(out[0], out[1]);
_mm_store_ps(&dst[i], out[0]);
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);
frac_.i[0] = _mm_extract_epi32(frac4, 0);
frac_.i[1] = _mm_extract_epi32(frac4, 1);
frac_.i[2] = _mm_extract_epi32(frac4, 2);
frac_.i[3] = _mm_extract_epi32(frac4, 3);
}
pos = pos_.i[0];
frac = frac_.i[0];
for(;i < numsamples;i++)
{
dst[i] = resample_fir8(src[pos ], src[pos+1], src[pos+2], src[pos+3],
src[pos+4], src[pos+5], src[pos+6], src[pos+7], frac);
frac += increment;
pos += frac>>FRACTIONBITS;
frac &= FRACTIONMASK;
}
return dst;
}
+418
View File
@@ -0,0 +1,418 @@
#include "config.h"
#include "nfcfilter.h"
#include "alu.h"
/* Near-field control filters are the basis for handling the near-field effect.
* The near-field effect is a bass-boost present in the directional components
* of a recorded signal, created as a result of the wavefront curvature (itself
* a function of sound distance). Proper reproduction dictates this be
* compensated for using a bass-cut given the playback speaker distance, to
* avoid excessive bass in the playback.
*
* For real-time rendered audio, emulating the near-field effect based on the
* sound source's distance, and subsequently compensating for it at output
* based on the speaker distances, can create a more realistic perception of
* sound distance beyond a simple 1/r attenuation.
*
* These filters do just that. Each one applies a low-shelf filter, created as
* the combination of a bass-boost for a given sound source distance (near-
* field emulation) along with a bass-cut for a given control/speaker distance
* (near-field compensation).
*
* Note that it is necessary to apply a cut along with the boost, since the
* boost alone is unstable in higher-order ambisonics as it causes an infinite
* DC gain (even first-order ambisonics requires there to be no DC offset for
* the boost to work). Consequently, ambisonics requires a control parameter to
* be used to avoid an unstable boost-only filter. NFC-HOA defines this control
* as a reference delay, calculated with:
*
* reference_delay = control_distance / speed_of_sound
*
* This means w0 (for input) or w1 (for output) should be set to:
*
* wN = 1 / (reference_delay * sample_rate)
*
* when dealing with NFC-HOA content. For FOA input content, which does not
* specify a reference_delay variable, w0 should be set to 0 to apply only
* near-field compensation for output. It's important that w1 be a finite,
* positive, non-0 value or else the bass-boost will become unstable again.
* Also, w0 should not be too large compared to w1, to avoid excessively loud
* low frequencies.
*/
static const float B[4][3] = {
{ 0.0f },
{ 1.0f },
{ 3.0f, 3.0f },
{ 3.6778f, 6.4595f, 2.3222f },
/*{ 4.2076f, 11.4877f, 5.7924f, 9.1401f }*/
};
void NfcFilterCreate1(NfcFilter *nfc, const float w0, const float w1)
{
float b_00, g_0;
float r;
memset(nfc, 0, sizeof(*nfc));
nfc->g = 1.0f;
nfc->coeffs[0] = 1.0f;
/* Calculate bass-boost coefficients. */
r = 0.5f * w0;
b_00 = B[1][0] * r;
g_0 = 1.0f + b_00;
nfc->coeffs[0] *= g_0;
nfc->coeffs[1] = (2.0f * b_00) / g_0;
/* Calculate bass-cut coefficients. */
r = 0.5f * w1;
b_00 = B[1][0] * r;
g_0 = 1.0f + b_00;
nfc->g /= g_0;
nfc->coeffs[0] /= g_0;
nfc->coeffs[1+1] = (2.0f * b_00) / g_0;
}
void NfcFilterAdjust1(NfcFilter *nfc, const float w0)
{
float b_00, g_0;
float r;
r = 0.5f * w0;
b_00 = B[1][0] * r;
g_0 = 1.0f + b_00;
nfc->coeffs[0] = nfc->g * g_0;
nfc->coeffs[1] = (2.0f * b_00) / g_0;
}
void NfcFilterUpdate1(NfcFilter *nfc, ALfloat *restrict dst, const float *restrict src, const int count)
{
const float b0 = nfc->coeffs[0];
const float a0 = nfc->coeffs[1];
const float a1 = nfc->coeffs[2];
float z1 = nfc->history[0];
int i;
for(i = 0;i < count;i++)
{
float out = src[i] * b0;
float y;
y = out - (a1*z1);
out = y + (a0*z1);
z1 += y;
dst[i] = out;
}
nfc->history[0] = z1;
}
void NfcFilterCreate2(NfcFilter *nfc, const float w0, const float w1)
{
float b_10, b_11, g_1;
float r;
memset(nfc, 0, sizeof(*nfc));
nfc->g = 1.0f;
nfc->coeffs[0] = 1.0f;
/* Calculate bass-boost coefficients. */
r = 0.5f * w0;
b_10 = B[2][0] * r;
b_11 = B[2][1] * r * r;
g_1 = 1.0f + b_10 + b_11;
nfc->coeffs[0] *= g_1;
nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
nfc->coeffs[2] = (4.0f * b_11) / g_1;
/* Calculate bass-cut coefficients. */
r = 0.5f * w1;
b_10 = B[2][0] * r;
b_11 = B[2][1] * r * r;
g_1 = 1.0f + b_10 + b_11;
nfc->g /= g_1;
nfc->coeffs[0] /= g_1;
nfc->coeffs[2+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
nfc->coeffs[2+2] = (4.0f * b_11) / g_1;
}
void NfcFilterAdjust2(NfcFilter *nfc, const float w0)
{
float b_10, b_11, g_1;
float r;
r = 0.5f * w0;
b_10 = B[2][0] * r;
b_11 = B[2][1] * r * r;
g_1 = 1.0f + b_10 + b_11;
nfc->coeffs[0] = nfc->g * g_1;
nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
nfc->coeffs[2] = (4.0f * b_11) / g_1;
}
void NfcFilterUpdate2(NfcFilter *nfc, ALfloat *restrict dst, const float *restrict src, const int count)
{
const float b0 = nfc->coeffs[0];
const float a00 = nfc->coeffs[1];
const float a01 = nfc->coeffs[2];
const float a10 = nfc->coeffs[3];
const float a11 = nfc->coeffs[4];
float z1 = nfc->history[0];
float z2 = nfc->history[1];
int i;
for(i = 0;i < count;i++)
{
float out = src[i] * b0;
float y;
y = out - (a10*z1) - (a11*z2);
out = y + (a00*z1) + (a01*z2);
z2 += z1;
z1 += y;
dst[i] = out;
}
nfc->history[0] = z1;
nfc->history[1] = z2;
}
void NfcFilterCreate3(NfcFilter *nfc, const float w0, const float w1)
{
float b_10, b_11, g_1;
float b_00, g_0;
float r;
memset(nfc, 0, sizeof(*nfc));
nfc->g = 1.0f;
nfc->coeffs[0] = 1.0f;
/* Calculate bass-boost coefficients. */
r = 0.5f * w0;
b_10 = B[3][0] * r;
b_11 = B[3][1] * r * r;
g_1 = 1.0f + b_10 + b_11;
nfc->coeffs[0] *= g_1;
nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
nfc->coeffs[2] = (4.0f * b_11) / g_1;
b_00 = B[3][2] * r;
g_0 = 1.0f + b_00;
nfc->coeffs[0] *= g_0;
nfc->coeffs[2+1] = (2.0f * b_00) / g_0;
/* Calculate bass-cut coefficients. */
r = 0.5f * w1;
b_10 = B[3][0] * r;
b_11 = B[3][1] * r * r;
g_1 = 1.0f + b_10 + b_11;
nfc->g /= g_1;
nfc->coeffs[0] /= g_1;
nfc->coeffs[3+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
nfc->coeffs[3+2] = (4.0f * b_11) / g_1;
b_00 = B[3][2] * r;
g_0 = 1.0f + b_00;
nfc->g /= g_0;
nfc->coeffs[0] /= g_0;
nfc->coeffs[3+2+1] = (2.0f * b_00) / g_0;
}
void NfcFilterAdjust3(NfcFilter *nfc, const float w0)
{
float b_10, b_11, g_1;
float b_00, g_0;
float r;
r = 0.5f * w0;
b_10 = B[3][0] * r;
b_11 = B[3][1] * r * r;
g_1 = 1.0f + b_10 + b_11;
nfc->coeffs[0] = nfc->g * g_1;
nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
nfc->coeffs[2] = (4.0f * b_11) / g_1;
b_00 = B[3][2] * r;
g_0 = 1.0f + b_00;
nfc->coeffs[0] *= g_0;
nfc->coeffs[2+1] = (2.0f * b_00) / g_0;
}
void NfcFilterUpdate3(NfcFilter *nfc, ALfloat *restrict dst, const float *restrict src, const int count)
{
const float b0 = nfc->coeffs[0];
const float a00 = nfc->coeffs[1];
const float a01 = nfc->coeffs[2];
const float a02 = nfc->coeffs[3];
const float a10 = nfc->coeffs[4];
const float a11 = nfc->coeffs[5];
const float a12 = nfc->coeffs[6];
float z1 = nfc->history[0];
float z2 = nfc->history[1];
float z3 = nfc->history[2];
int i;
for(i = 0;i < count;i++)
{
float out = src[i] * b0;
float y;
y = out - (a10*z1) - (a11*z2);
out = y + (a00*z1) + (a01*z2);
z2 += z1;
z1 += y;
y = out - (a12*z3);
out = y + (a02*z3);
z3 += y;
dst[i] = out;
}
nfc->history[0] = z1;
nfc->history[1] = z2;
nfc->history[2] = z3;
}
#if 0 /* Original methods the above are derived from. */
static void NfcFilterCreate(NfcFilter *nfc, const ALsizei order, const float src_dist, const float ctl_dist, const float rate)
{
static const float B[4][5] = {
{ },
{ 1.0f },
{ 3.0f, 3.0f },
{ 3.6778f, 6.4595f, 2.3222f },
{ 4.2076f, 11.4877f, 5.7924f, 9.1401f }
};
float w0 = SPEEDOFSOUNDMETRESPERSEC / (src_dist * rate);
float w1 = SPEEDOFSOUNDMETRESPERSEC / (ctl_dist * rate);
ALsizei i;
float r;
nfc->g = 1.0f;
nfc->coeffs[0] = 1.0f;
/* NOTE: Slight adjustment from the literature to raise the center
* frequency a bit (0.5 -> 1.0).
*/
r = 1.0f * w0;
for(i = 0; i < (order-1);i += 2)
{
float b_10 = B[order][i ] * r;
float b_11 = B[order][i+1] * r * r;
float g_1 = 1.0f + b_10 + b_11;
nfc->b[i] = b_10;
nfc->b[i + 1] = b_11;
nfc->coeffs[0] *= g_1;
nfc->coeffs[i+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
nfc->coeffs[i+2] = (4.0f * b_11) / g_1;
}
if(i < order)
{
float b_00 = B[order][i] * r;
float g_0 = 1.0f + b_00;
nfc->b[i] = b_00;
nfc->coeffs[0] *= g_0;
nfc->coeffs[i+1] = (2.0f * b_00) / g_0;
}
r = 1.0f * w1;
for(i = 0;i < (order-1);i += 2)
{
float b_10 = B[order][i ] * r;
float b_11 = B[order][i+1] * r * r;
float g_1 = 1.0f + b_10 + b_11;
nfc->g /= g_1;
nfc->coeffs[0] /= g_1;
nfc->coeffs[order+i+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
nfc->coeffs[order+i+2] = (4.0f * b_11) / g_1;
}
if(i < order)
{
float b_00 = B[order][i] * r;
float g_0 = 1.0f + b_00;
nfc->g /= g_0;
nfc->coeffs[0] /= g_0;
nfc->coeffs[order+i+1] = (2.0f * b_00) / g_0;
}
for(i = 0; i < MAX_AMBI_ORDER; i++)
nfc->history[i] = 0.0f;
}
static void NfcFilterAdjust(NfcFilter *nfc, const float distance)
{
int i;
nfc->coeffs[0] = nfc->g;
for(i = 0;i < (nfc->order-1);i += 2)
{
float b_10 = nfc->b[i] / distance;
float b_11 = nfc->b[i+1] / (distance * distance);
float g_1 = 1.0f + b_10 + b_11;
nfc->coeffs[0] *= g_1;
nfc->coeffs[i+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
nfc->coeffs[i+2] = (4.0f * b_11) / g_1;
}
if(i < nfc->order)
{
float b_00 = nfc->b[i] / distance;
float g_0 = 1.0f + b_00;
nfc->coeffs[0] *= g_0;
nfc->coeffs[i+1] = (2.0f * b_00) / g_0;
}
}
static float NfcFilterUpdate(const float in, NfcFilter *nfc)
{
int i;
float out = in * nfc->coeffs[0];
for(i = 0;i < (nfc->order-1);i += 2)
{
float y = out - (nfc->coeffs[nfc->order+i+1] * nfc->history[i]) -
(nfc->coeffs[nfc->order+i+2] * nfc->history[i+1]) + 1.0e-30f;
out = y + (nfc->coeffs[i+1]*nfc->history[i]) + (nfc->coeffs[i+2]*nfc->history[i+1]);
nfc->history[i+1] += nfc->history[i];
nfc->history[i] += y;
}
if(i < nfc->order)
{
float y = out - (nfc->coeffs[nfc->order+i+1] * nfc->history[i]) + 1.0e-30f;
out = y + (nfc->coeffs[i+1] * nfc->history[i]);
nfc->history[i] += y;
}
return out;
}
#endif
+37
View File
@@ -0,0 +1,37 @@
#ifndef NFCFILTER_H
#define NFCFILTER_H
#include "alMain.h"
typedef struct NfcFilter {
float g;
float coeffs[MAX_AMBI_ORDER*2 + 1];
float history[MAX_AMBI_ORDER];
} NfcFilter;
/* NOTE:
* w0 = speed_of_sound / (source_distance * sample_rate);
* w1 = speed_of_sound / (control_distance * sample_rate);
*
* Generally speaking, the control distance should be approximately the average
* speaker distance, or based on the reference delay if outputing NFC-HOA. It
* must not be negative, 0, or infinite. The source distance should not be too
* small relative to the control distance.
*/
/* Near-field control filter for first-order ambisonic channels (1-3). */
void NfcFilterCreate1(NfcFilter *nfc, const float w0, const float w1);
void NfcFilterAdjust1(NfcFilter *nfc, const float w0);
void NfcFilterUpdate1(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count);
/* Near-field control filter for second-order ambisonic channels (4-8). */
void NfcFilterCreate2(NfcFilter *nfc, const float w0, const float w1);
void NfcFilterAdjust2(NfcFilter *nfc, const float w0);
void NfcFilterUpdate2(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count);
/* Near-field control filter for third-order ambisonic channels (9-15). */
void NfcFilterCreate3(NfcFilter *nfc, const float w0, const float w1);
void NfcFilterAdjust3(NfcFilter *nfc, const float w0);
void NfcFilterUpdate3(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count);
#endif /* NFCFILTER_H */
+1042 -328
View File
File diff suppressed because it is too large Load Diff
+134
View File
@@ -0,0 +1,134 @@
#include "config.h"
#include "alu.h"
#include "uhjfilter.h"
/* This is the maximum number of samples processed for each inner loop
* iteration. */
#define MAX_UPDATE_SAMPLES 128
static const ALfloat Filter1Coeff[4] = {
0.6923878f, 0.9360654322959f, 0.9882295226860f, 0.9987488452737f
};
static const ALfloat Filter2Coeff[4] = {
0.4021921162426f, 0.8561710882420f, 0.9722909545651f, 0.9952884791278f
};
static void allpass_process(AllPassState *state, ALfloat *restrict dst, const ALfloat *restrict src, const ALfloat aa, ALsizei todo)
{
ALsizei i;
if(todo > 1)
{
dst[0] = aa*(src[0] + state->y[1]) - state->x[1];
dst[1] = aa*(src[1] + state->y[0]) - state->x[0];
for(i = 2;i < todo;i++)
dst[i] = aa*(src[i] + dst[i-2]) - src[i-2];
state->x[1] = src[i-2];
state->x[0] = src[i-1];
state->y[1] = dst[i-2];
state->y[0] = dst[i-1];
}
else if(todo == 1)
{
dst[0] = aa*(src[0] + state->y[1]) - state->x[1];
state->x[1] = state->x[0];
state->x[0] = src[0];
state->y[1] = state->y[0];
state->y[0] = dst[0];
}
}
/* NOTE: There seems to be a bit of an inconsistency in how this encoding is
* supposed to work. Some references, such as
*
* http://members.tripod.com/martin_leese/Ambisonic/UHJ_file_format.html
*
* specify a pre-scaling of sqrt(2) on the W channel input, while other
* references, such as
*
* https://en.wikipedia.org/wiki/Ambisonic_UHJ_format#Encoding.5B1.5D
* and
* https://wiki.xiph.org/Ambisonics#UHJ_format
*
* do not. The sqrt(2) scaling is in line with B-Format decoder coefficients
* which include such a scaling for the W channel input, however the original
* source for this equation is a 1985 paper by Michael Gerzon, which does not
* apparently include the scaling. Applying the extra scaling creates a louder
* result with a narrower stereo image compared to not scaling, and I don't
* know which is the intended result.
*/
void EncodeUhj2(Uhj2Encoder *enc, ALfloat *restrict LeftOut, ALfloat *restrict RightOut, ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo)
{
ALfloat D[MAX_UPDATE_SAMPLES], S[MAX_UPDATE_SAMPLES];
ALfloat temp[2][MAX_UPDATE_SAMPLES];
ALsizei base, i;
for(base = 0;base < SamplesToDo;)
{
ALsizei todo = mini(SamplesToDo - base, MAX_UPDATE_SAMPLES);
/* D = 0.6554516*Y */
for(i = 0;i < todo;i++)
temp[0][i] = 0.6554516f*InSamples[2][base+i];
allpass_process(&enc->Filter1_Y[0], temp[1], temp[0],
Filter1Coeff[0]*Filter1Coeff[0], todo);
allpass_process(&enc->Filter1_Y[1], temp[0], temp[1],
Filter1Coeff[1]*Filter1Coeff[1], todo);
allpass_process(&enc->Filter1_Y[2], temp[1], temp[0],
Filter1Coeff[2]*Filter1Coeff[2], todo);
/* NOTE: Filter1 requires a 1 sample delay for the final output, so
* take the last processed sample from the previous run as the first
* output sample.
*/
D[0] = enc->Filter1_Y[3].y[0];
allpass_process(&enc->Filter1_Y[3], temp[0], temp[1],
Filter1Coeff[3]*Filter1Coeff[3], todo);
for(i = 1;i < todo;i++)
D[i] = temp[0][i-1];
/* D += j(-0.3420201*W + 0.5098604*X) */
for(i = 0;i < todo;i++)
temp[0][i] = -0.3420201f*InSamples[0][base+i] +
0.5098604f*InSamples[1][base+i];
allpass_process(&enc->Filter2_WX[0], temp[1], temp[0],
Filter2Coeff[0]*Filter2Coeff[0], todo);
allpass_process(&enc->Filter2_WX[1], temp[0], temp[1],
Filter2Coeff[1]*Filter2Coeff[1], todo);
allpass_process(&enc->Filter2_WX[2], temp[1], temp[0],
Filter2Coeff[2]*Filter2Coeff[2], todo);
allpass_process(&enc->Filter2_WX[3], temp[0], temp[1],
Filter2Coeff[3]*Filter2Coeff[3], todo);
for(i = 0;i < todo;i++)
D[i] += temp[0][i];
/* S = 0.9396926*W + 0.1855740*X */
for(i = 0;i < todo;i++)
temp[0][i] = 0.9396926f*InSamples[0][base+i] +
0.1855740f*InSamples[1][base+i];
allpass_process(&enc->Filter1_WX[0], temp[1], temp[0],
Filter1Coeff[0]*Filter1Coeff[0], todo);
allpass_process(&enc->Filter1_WX[1], temp[0], temp[1],
Filter1Coeff[1]*Filter1Coeff[1], todo);
allpass_process(&enc->Filter1_WX[2], temp[1], temp[0],
Filter1Coeff[2]*Filter1Coeff[2], todo);
S[0] = enc->Filter1_WX[3].y[0];
allpass_process(&enc->Filter1_WX[3], temp[0], temp[1],
Filter1Coeff[3]*Filter1Coeff[3], todo);
for(i = 1;i < todo;i++)
S[i] = temp[0][i-1];
/* Left = (S + D)/2.0 */
for(i = 0;i < todo;i++)
*(LeftOut++) += (S[i] + D[i]) * 0.5f;
/* Right = (S - D)/2.0 */
for(i = 0;i < todo;i++)
*(RightOut++) += (S[i] - D[i]) * 0.5f;
base += todo;
}
}
+49
View File
@@ -0,0 +1,49 @@
#ifndef UHJFILTER_H
#define UHJFILTER_H
#include "AL/al.h"
#include "alMain.h"
typedef struct AllPassState {
ALfloat x[2]; /* Last two input samples */
ALfloat y[2]; /* Last two output samples */
} AllPassState;
/* Encoding 2-channel UHJ from B-Format is done as:
*
* S = 0.9396926*W + 0.1855740*X
* D = j(-0.3420201*W + 0.5098604*X) + 0.6554516*Y
*
* Left = (S + D)/2.0
* Right = (S - D)/2.0
*
* where j is a wide-band +90 degree phase shift.
*
* The phase shift is done using a Hilbert transform, described here:
* https://web.archive.org/web/20060708031958/http://www.biochem.oulu.fi/~oniemita/dsp/hilbert/
* It works using 2 sets of 4 chained filters. The first filter chain produces
* a phase shift of varying magnitude over a wide range of frequencies, while
* the second filter chain produces a phase shift 90 degrees ahead of the
* first over the same range.
*
* Combining these two stages requires the use of three filter chains. S-
* channel output uses a Filter1 chain on the W and X channel mix, while the D-
* channel output uses a Filter1 chain on the Y channel plus a Filter2 chain on
* the W and X channel mix. This results in the W and X input mix on the D-
* channel output having the required +90 degree phase shift relative to the
* other inputs.
*/
typedef struct Uhj2Encoder {
AllPassState Filter1_WX[4];
AllPassState Filter1_Y[4];
AllPassState Filter2_WX[4];
} Uhj2Encoder;
/* Encodes a 2-channel UHJ (stereo-compatible) signal from a B-Format input
* signal. The input must use FuMa channel ordering and scaling.
*/
void EncodeUhj2(Uhj2Encoder *enc, ALfloat *restrict LeftOut, ALfloat *restrict RightOut, ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo);
#endif /* UHJFILTER_H */
+41 -53
View File
@@ -5,11 +5,8 @@
#include <AL/al.h>
/* "Base" vector type, designed to alias with the actual vector types. */
typedef struct vector__s {
size_t Capacity;
size_t Size;
} *vector_;
#include "almalloc.h"
#define TYPEDEF_VECTOR(T, N) typedef struct { \
size_t Capacity; \
@@ -27,38 +24,47 @@ typedef const _##N* const_##N;
#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)
#define VECTOR_DEINIT(_x) do { al_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, size_t 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, size_t obj_count);
#define VECTOR_RESIZE(_x, _c) (vector_resize((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_c)))
#define VECTOR_RESIZE(_x, _s, _c) do { \
size_t _size = (_s); \
size_t _cap = (_c); \
if(_size > _cap) \
_cap = _size; \
\
if(!(_x) && _cap == 0) \
break; \
\
if(((_x) ? (_x)->Capacity : 0) < _cap) \
{ \
ptrdiff_t data_offset = (char*)((_x)->Data) - (char*)(_x); \
size_t old_size = ((_x) ? (_x)->Size : 0); \
void *temp; \
\
temp = al_calloc(16, data_offset + sizeof((_x)->Data[0])*_cap); \
assert(temp != NULL); \
if((_x)) \
memcpy(((char*)temp)+data_offset, (_x)->Data, \
sizeof((_x)->Data[0])*old_size); \
\
al_free((_x)); \
(_x) = temp; \
(_x)->Capacity = _cap; \
} \
(_x)->Size = _size; \
} while(0) \
#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)
#define VECTOR_BEGIN(_x) ((_x) ? (_x)->Data + 0 : NULL)
#define VECTOR_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_PUSH_BACK(_x, _obj) do { \
size_t _pbsize = VECTOR_SIZE(_x)+1; \
VECTOR_RESIZE(_x, _pbsize, _pbsize); \
(_x)->Data[(_x)->Size-1] = (_obj); \
} while(0)
#define VECTOR_POP_BACK(_x) ((void)((_x)->Size--))
#define VECTOR_BACK(_x) ((_x)->Data[(_x)->Size-1])
@@ -67,22 +73,15 @@ ALboolean vector_insert(char *ptr, size_t base_size, size_t obj_size, void *ins_
#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)); \
_t *_iter = VECTOR_BEGIN((_x)); \
_t *_end = VECTOR_END((_x)); \
for(;_iter != _end;++_iter) \
_f(_iter); \
} while(0)
#define VECTOR_FOR_EACH_PARAMS(_t, _x, _f, ...) do { \
_t *_iter = VECTOR_ITER_BEGIN((_x)); \
_t *_end = VECTOR_ITER_END((_x)); \
for(;_iter != _end;++_iter) \
_f(__VA_ARGS__, _iter); \
} while(0)
#define VECTOR_FIND_IF(_i, _t, _x, _f) do { \
_t *_iter = VECTOR_ITER_BEGIN((_x)); \
_t *_end = VECTOR_ITER_END((_x)); \
_t *_iter = VECTOR_BEGIN((_x)); \
_t *_end = VECTOR_END((_x)); \
for(;_iter != _end;++_iter) \
{ \
if(_f(_iter)) \
@@ -91,15 +90,4 @@ ALboolean vector_insert(char *ptr, size_t base_size, size_t obj_size, void *ins_
(_i) = _iter; \
} while(0)
#define VECTOR_FIND_IF_PARMS(_i, _t, _x, _f, ...) do { \
_t *_iter = VECTOR_ITER_BEGIN((_x)); \
_t *_end = VECTOR_ITER_END((_x)); \
for(;_iter != _end;++_iter) \
{ \
if(_f(__VA_ARGS__, _iter)) \
break; \
} \
(_i) = _iter; \
} while(0)
#endif /* AL_VECTOR_H */
+452 -240
View File
File diff suppressed because it is too large Load Diff
+117
View File
@@ -1,3 +1,120 @@
openal-soft-1.18.2:
Fixed resetting the FPU rounding mode after certain function calls on
Windows.
Fixed use of SSE intrinsics when building with Clang on Windows.
Fixed a crash with the JACK backend when using JACK1.
Fixed use of pthread_setnane_np on NetBSD.
Fixed building on FreeBSD with an older freebsd-lib.
OSS now links with libossaudio if found at build time (for NetBSD).
openal-soft-1.18.1:
Fixed an issue where resuming a source might not restart playing it.
Fixed PulseAudio playback when the configured stream length is much less
than the requested length.
Fixed MMDevAPI capture with sample rates not matching the backing device.
Fixed int32 output for the Wave Writer.
Fixed enumeration of OSS devices that are missing device files.
Added correct retrieval of the executable's path on FreeBSD.
Added a config option to specify the dithering depth.
Added a 5.1 decoder preset that excludes front-center output.
openal-soft-1.18.0:
Implemented the AL_EXT_STEREO_ANGLES and AL_EXT_SOURCE_RADIUS extensions.
Implemented the AL_SOFT_gain_clamp_ex, AL_SOFT_source_resampler,
AL_SOFT_source_spatialize, and ALC_SOFT_output_limiter extensions.
Implemented 3D processing for some effects. Currently implemented for
Reverb, Compressor, Equalizer, and Ring Modulator.
Implemented 2-channel UHJ output encoding. This needs to be enabled with a
config option to be used.
Implemented dual-band processing for high-quality ambisonic decoding.
Implemented distance-compensation for surround sound output.
Implemented near-field emulation and compensation with ambisonic rendering.
Currently only applies when using the high-quality ambisonic decoder or
ambisonic output, with appropriate config options.
Implemented an output limiter to reduce the amount of distortion from
clipping.
Implemented dithering for 8-bit and 16-bit output.
Implemented a config option to select a preferred HRTF.
Implemented a run-time check for NEON extensions using /proc/cpuinfo.
Implemented experimental capture support for the OpenSL backend.
Fixed building on compilers with NEON support but don't default to having
NEON enabled.
Fixed support for JACK on Windows.
Fixed starting a source while alcSuspendContext is in effect.
Fixed detection of headsets as headphones, with MMDevAPI.
Added support for AmbDec config files, for custom ambisonic decoder
configurations. Version 3 files only.
Added backend-specific options to alsoft-config.
Added first-, second-, and third-order ambisonic output formats. Currently
only works with backends that don't rely on channel labels, like JACK,
ALSA, and OSS.
Added a build option to embed the default HRTFs into the lib.
Added AmbDec presets to enable high-quality ambisonic decoding.
Added an AmbDec preset for 3D7.1 speaker setups.
Added documentation regarding Ambisonics, 3D7.1, AmbDec config files, and
the provided ambdec presets.
Added the ability for MMDevAPI to open devices given a Device ID or GUID
string.
Added an option to the example apps to open a specific device.
Increased the maximum auxiliary send limit to 16 (up from 4). Requires
requesting them with the ALC_MAX_AUXILIARY_SENDS context creation
attribute.
Increased the default auxiliary effect slot count to 64 (up from 4).
Reduced the default period count to 3 (down from 4).
Slightly improved automatic naming for enumerated HRTFs.
Improved B-Format decoding with HRTF output.
Improved internal property handling for better batching behavior.
Improved performance of certain filter uses.
Removed support for the AL_SOFT_buffer_samples and AL_SOFT_buffer_sub_data
extensions. Due to conflicts with AL_EXT_SOURCE_RADIUS.
openal-soft-1.17.2:
Implemented device enumeration for OSSv4.
+87 -14
View File
@@ -4,6 +4,7 @@
#include "alMain.h"
#include "alEffect.h"
#include "atomic.h"
#include "align.h"
#ifdef __cplusplus
@@ -14,15 +15,22 @@ struct ALeffectStateVtable;
struct ALeffectslot;
typedef struct ALeffectState {
RefCount Ref;
const struct ALeffectStateVtable *vtbl;
ALfloat (*OutBuffer)[BUFFERSIZE];
ALsizei OutChannels;
} ALeffectState;
void ALeffectState_Construct(ALeffectState *state);
void ALeffectState_Destruct(ALeffectState *state);
struct ALeffectStateVtable {
void (*const Destruct)(ALeffectState *state);
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], ALuint numChannels);
void (*const update)(ALeffectState *state, const ALCdevice *device, const struct ALeffectslot *slot, const union ALeffectProps *props);
void (*const process)(ALeffectState *state, ALsizei samplesToDo, const ALfloat (*restrict samplesIn)[BUFFERSIZE], ALfloat (*restrict samplesOut)[BUFFERSIZE], ALsizei numChannels);
void (*const Delete)(void *ptr);
};
@@ -30,8 +38,8 @@ struct ALeffectStateVtable {
#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_THUNK4(T, ALeffectState, void, process, ALuint, const ALfloat*restrict, ALfloatBUFFERSIZE*restrict, ALuint) \
DECLARE_THUNK3(T, ALeffectState, void, update, const ALCdevice*, const ALeffectslot*, const ALeffectProps*) \
DECLARE_THUNK4(T, ALeffectState, void, process, ALsizei, const ALfloatBUFFERSIZE*restrict, ALfloatBUFFERSIZE*restrict, ALsizei) \
static void T##_ALeffectState_Delete(void *ptr) \
{ return T##_Delete(STATIC_UPCAST(T, ALeffectState, (ALeffectState*)ptr)); } \
\
@@ -64,36 +72,101 @@ static const struct ALeffectStateFactoryVtable T##_ALeffectStateFactory_vtable =
}
#define MAX_EFFECT_CHANNELS (4)
struct ALeffectslotArray {
ALsizei count;
struct ALeffectslot *slot[];
};
struct ALeffectslotProps {
ALfloat Gain;
ALboolean AuxSendAuto;
ALenum Type;
ALeffectProps Props;
ALeffectState *State;
ATOMIC(struct ALeffectslotProps*) next;
};
typedef struct ALeffectslot {
ALenum EffectType;
ALeffectProps EffectProps;
ALfloat Gain;
ALboolean AuxSendAuto;
volatile ALfloat Gain;
volatile ALboolean AuxSendAuto;
struct {
ALenum Type;
ALeffectProps Props;
ATOMIC(ALenum) NeedsUpdate;
ALeffectState *EffectState;
ALeffectState *State;
} Effect;
alignas(16) ALfloat WetBuffer[1][BUFFERSIZE];
ATOMIC_FLAG PropsClean;
RefCount ref;
ATOMIC(struct ALeffectslotProps*) Update;
ATOMIC(struct ALeffectslotProps*) FreeList;
struct {
ALfloat Gain;
ALboolean AuxSendAuto;
ALenum EffectType;
ALeffectState *EffectState;
ALfloat RoomRolloff; /* Added to the source's room rolloff, not multiplied. */
ALfloat DecayTime;
ALfloat DecayHFRatio;
ALboolean DecayHFLimit;
ALfloat AirAbsorptionGainHF;
} Params;
/* Self ID */
ALuint id;
ALsizei NumChannels;
BFChannelConfig ChanMap[MAX_EFFECT_CHANNELS];
/* Wet buffer configuration is ACN channel order with N3D scaling:
* * Channel 0 is the unattenuated mono signal.
* * Channel 1 is OpenAL -X
* * Channel 2 is OpenAL Y
* * Channel 3 is OpenAL -Z
* Consequently, effects that only want to work with mono input can use
* channel 0 by itself. Effects that want multichannel can process the
* ambisonics signal and make a B-Format pan (ComputeFirstOrderGains) for
* first-order device output (FOAOut).
*/
alignas(16) ALfloat WetBuffer[MAX_EFFECT_CHANNELS][BUFFERSIZE];
} ALeffectslot;
inline void LockEffectSlotsRead(ALCcontext *context)
{ LockUIntMapRead(&context->EffectSlotMap); }
inline void UnlockEffectSlotsRead(ALCcontext *context)
{ UnlockUIntMapRead(&context->EffectSlotMap); }
inline void LockEffectSlotsWrite(ALCcontext *context)
{ LockUIntMapWrite(&context->EffectSlotMap); }
inline void UnlockEffectSlotsWrite(ALCcontext *context)
{ UnlockUIntMapWrite(&context->EffectSlotMap); }
inline struct ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id)
{ return (struct ALeffectslot*)LookupUIntMapKey(&context->EffectSlotMap, id); }
{ return (struct ALeffectslot*)LookupUIntMapKeyNoLock(&context->EffectSlotMap, id); }
inline struct ALeffectslot *RemoveEffectSlot(ALCcontext *context, ALuint id)
{ return (struct ALeffectslot*)RemoveUIntMapKey(&context->EffectSlotMap, id); }
{ return (struct ALeffectslot*)RemoveUIntMapKeyNoLock(&context->EffectSlotMap, id); }
ALenum InitEffectSlot(ALeffectslot *slot);
void DeinitEffectSlot(ALeffectslot *slot);
void UpdateEffectSlotProps(ALeffectslot *slot);
void UpdateAllEffectSlotProps(ALCcontext *context);
ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context);
ALeffectStateFactory *ALnullStateFactory_getFactory(void);
ALeffectStateFactory *ALreverbStateFactory_getFactory(void);
ALeffectStateFactory *ALautowahStateFactory_getFactory(void);
ALeffectStateFactory *ALchorusStateFactory_getFactory(void);
ALeffectStateFactory *ALcompressorStateFactory_getFactory(void);
ALeffectStateFactory *ALdistortionStateFactory_getFactory(void);
+31 -23
View File
@@ -17,28 +17,26 @@ enum UserFmtType {
UserFmtUInt = AL_UNSIGNED_INT_SOFT,
UserFmtFloat = AL_FLOAT_SOFT,
UserFmtDouble = AL_DOUBLE_SOFT,
UserFmtByte3 = AL_BYTE3_SOFT,
UserFmtUByte3 = AL_UNSIGNED_BYTE3_SOFT,
UserFmtMulaw,
UserFmtAlaw,
UserFmtMulaw = AL_MULAW_SOFT,
UserFmtAlaw = 0x10000000,
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) */
UserFmtBFormat2D = 0x10000000, /* WXY */
UserFmtBFormat3D, /* WXYZ */
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) */
UserFmtBFormat2D = AL_BFORMAT2D_SOFT, /* WXY */
UserFmtBFormat3D = AL_BFORMAT3D_SOFT, /* WXYZ */
};
ALuint BytesFromUserFmt(enum UserFmtType type) DECL_CONST;
ALuint ChannelsFromUserFmt(enum UserFmtChannels chans) DECL_CONST;
inline ALuint FrameSizeFromUserFmt(enum UserFmtChannels chans, enum UserFmtType type)
ALsizei BytesFromUserFmt(enum UserFmtType type);
ALsizei ChannelsFromUserFmt(enum UserFmtChannels chans);
inline ALsizei FrameSizeFromUserFmt(enum UserFmtChannels chans, enum UserFmtType type)
{
return ChannelsFromUserFmt(chans) * BytesFromUserFmt(type);
}
@@ -63,9 +61,9 @@ enum FmtChannels {
};
#define MAX_INPUT_CHANNELS (8)
ALuint BytesFromFmt(enum FmtType type) DECL_CONST;
ALuint ChannelsFromFmt(enum FmtChannels chans) DECL_CONST;
inline ALuint FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type)
ALsizei BytesFromFmt(enum FmtType type);
ALsizei ChannelsFromFmt(enum FmtChannels chans);
inline ALsizei FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type)
{
return ChannelsFromFmt(chans) * BytesFromFmt(type);
}
@@ -80,14 +78,15 @@ typedef struct ALbuffer {
enum FmtChannels FmtChannels;
enum FmtType FmtType;
ALuint BytesAlloc;
enum UserFmtChannels OriginalChannels;
enum UserFmtType OriginalType;
ALsizei OriginalSize;
ALsizei OriginalAlign;
ALsizei LoopStart;
ALsizei LoopEnd;
ALsizei LoopStart;
ALsizei LoopEnd;
ATOMIC(ALsizei) UnpackAlign;
ATOMIC(ALsizei) PackAlign;
@@ -106,10 +105,19 @@ 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 void LockBuffersRead(ALCdevice *device)
{ LockUIntMapRead(&device->BufferMap); }
inline void UnlockBuffersRead(ALCdevice *device)
{ UnlockUIntMapRead(&device->BufferMap); }
inline void LockBuffersWrite(ALCdevice *device)
{ LockUIntMapWrite(&device->BufferMap); }
inline void UnlockBuffersWrite(ALCdevice *device)
{ UnlockUIntMapWrite(&device->BufferMap); }
inline struct ALbuffer *LookupBuffer(ALCdevice *device, ALuint id)
{ return (struct ALbuffer*)LookupUIntMapKey(&device->BufferMap, id); }
{ return (struct ALbuffer*)LookupUIntMapKeyNoLock(&device->BufferMap, id); }
inline struct ALbuffer *RemoveBuffer(ALCdevice *device, ALuint id)
{ return (struct ALbuffer*)RemoveUIntMapKey(&device->BufferMap, id); }
{ return (struct ALbuffer*)RemoveUIntMapKeyNoLock(&device->BufferMap, id); }
ALvoid ReleaseALBuffers(ALCdevice *device);
+21 -21
View File
@@ -10,17 +10,16 @@ extern "C" {
struct ALeffect;
enum {
EAXREVERB = 0,
REVERB,
AUTOWAH,
CHORUS,
COMPRESSOR,
DISTORTION,
ECHO,
EQUALIZER,
FLANGER,
MODULATOR,
DEDICATED,
AL__EAXREVERB = 0,
AL__REVERB,
AL__CHORUS,
AL__COMPRESSOR,
AL__DISTORTION,
AL__ECHO,
AL__EQUALIZER,
AL__FLANGER,
AL__MODULATOR,
AL__DEDICATED,
MAX_EFFECTS
};
@@ -51,7 +50,6 @@ const struct ALeffectVtable T##_vtable = { \
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;
@@ -93,13 +91,6 @@ typedef union ALeffectProps {
ALfloat LFReference;
} Reverb;
struct {
ALfloat AttackTime;
ALfloat ReleaseTime;
ALfloat PeakGain;
ALfloat Resonance;
} Autowah;
struct {
ALint Waveform;
ALint Phase;
@@ -176,10 +167,19 @@ typedef struct ALeffect {
ALuint id;
} ALeffect;
inline void LockEffectsRead(ALCdevice *device)
{ LockUIntMapRead(&device->EffectMap); }
inline void UnlockEffectsRead(ALCdevice *device)
{ UnlockUIntMapRead(&device->EffectMap); }
inline void LockEffectsWrite(ALCdevice *device)
{ LockUIntMapWrite(&device->EffectMap); }
inline void UnlockEffectsWrite(ALCdevice *device)
{ UnlockUIntMapWrite(&device->EffectMap); }
inline struct ALeffect *LookupEffect(ALCdevice *device, ALuint id)
{ return (struct ALeffect*)LookupUIntMapKey(&device->EffectMap, id); }
{ return (struct ALeffect*)LookupUIntMapKeyNoLock(&device->EffectMap, id); }
inline struct ALeffect *RemoveEffect(ALCdevice *device, ALuint id)
{ return (struct ALeffect*)RemoveUIntMapKey(&device->EffectMap, id); }
{ return (struct ALeffect*)RemoveUIntMapKeyNoLock(&device->EffectMap, id); }
inline ALboolean IsReverbEffect(ALenum type)
{ return type == AL_EFFECT_REVERB || type == AL_EFFECT_EAXREVERB; }
+22 -23
View File
@@ -42,13 +42,11 @@ typedef enum ALfilterType {
typedef struct ALfilterState {
ALfloat x[2]; /* History of two last input samples */
ALfloat y[2]; /* History of two last output samples */
ALfloat b0, b1, b2; /* Transfer function coefficients "b" */
ALfloat a1, a2; /* Transfer function coefficients "a" (a0 is pre-applied) */
ALfloat b1, b2; /* Transfer function coefficients "b" (b0 is input_gain) */
ALfloat input_gain;
void (*process)(struct ALfilterState *self, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples);
} ALfilterState;
#define ALfilterState_process(a, ...) ((a)->process((a), __VA_ARGS__))
/* Currently only a C-based filter process method is implemented. */
#define ALfilterState_process ALfilterState_processC
/* Calculates the rcpQ (i.e. 1/Q) coefficient for shelving filters, using the
* reference gain and shelf slope parameter.
@@ -79,26 +77,18 @@ inline void ALfilterState_clear(ALfilterState *filter)
void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat gain, ALfloat freq_mult, ALfloat rcpQ);
inline ALfloat ALfilterState_processSingle(ALfilterState *filter, ALfloat sample)
inline void ALfilterState_copyParams(ALfilterState *restrict dst, const ALfilterState *restrict src)
{
ALfloat outsmp;
outsmp = filter->input_gain * sample +
filter->b1 * filter->x[0] +
filter->b2 * filter->x[1] -
filter->a1 * filter->y[0] -
filter->a2 * filter->y[1];
filter->x[1] = filter->x[0];
filter->x[0] = sample;
filter->y[1] = filter->y[0];
filter->y[0] = outsmp;
return outsmp;
dst->b0 = src->b0;
dst->b1 = src->b1;
dst->b2 = src->b2;
dst->a1 = src->a1;
dst->a2 = src->a2;
}
void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples);
void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALsizei numsamples);
inline void ALfilterState_processPassthru(ALfilterState *filter, const ALfloat *src, ALuint numsamples)
inline void ALfilterState_processPassthru(ALfilterState *filter, const ALfloat *restrict src, ALsizei numsamples)
{
if(numsamples >= 2)
{
@@ -151,10 +141,19 @@ typedef struct ALfilter {
#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)))
inline void LockFiltersRead(ALCdevice *device)
{ LockUIntMapRead(&device->FilterMap); }
inline void UnlockFiltersRead(ALCdevice *device)
{ UnlockUIntMapRead(&device->FilterMap); }
inline void LockFiltersWrite(ALCdevice *device)
{ LockUIntMapWrite(&device->FilterMap); }
inline void UnlockFiltersWrite(ALCdevice *device)
{ UnlockUIntMapWrite(&device->FilterMap); }
inline struct ALfilter *LookupFilter(ALCdevice *device, ALuint id)
{ return (struct ALfilter*)LookupUIntMapKey(&device->FilterMap, id); }
{ return (struct ALfilter*)LookupUIntMapKeyNoLock(&device->FilterMap, id); }
inline struct ALfilter *RemoveFilter(ALCdevice *device, ALuint id)
{ return (struct ALfilter*)RemoveUIntMapKey(&device->FilterMap, id); }
{ return (struct ALfilter*)RemoveUIntMapKeyNoLock(&device->FilterMap, id); }
ALvoid ReleaseALFilters(ALCdevice *device);
+44 -7
View File
@@ -8,20 +8,57 @@
extern "C" {
#endif
struct ALlistenerProps {
ALfloat Position[3];
ALfloat Velocity[3];
ALfloat Forward[3];
ALfloat Up[3];
ALfloat Gain;
ALfloat MetersPerUnit;
ALfloat DopplerFactor;
ALfloat DopplerVelocity;
ALfloat SpeedOfSound;
ALboolean SourceDistanceModel;
enum DistanceModel DistanceModel;
ATOMIC(struct ALlistenerProps*) next;
};
typedef struct ALlistener {
aluVector Position;
aluVector Velocity;
volatile ALfloat Forward[3];
volatile ALfloat Up[3];
volatile ALfloat Gain;
volatile ALfloat MetersPerUnit;
alignas(16) ALfloat Position[3];
ALfloat Velocity[3];
ALfloat Forward[3];
ALfloat Up[3];
ALfloat Gain;
ALfloat MetersPerUnit;
/* Pointer to the most recent property values that are awaiting an update.
*/
ATOMIC(struct ALlistenerProps*) Update;
/* A linked list of unused property containers, free to use for future
* updates.
*/
ATOMIC(struct ALlistenerProps*) FreeList;
struct {
aluMatrixd Matrix;
aluMatrixf Matrix;
aluVector Velocity;
ALfloat Gain;
ALfloat MetersPerUnit;
ALfloat DopplerFactor;
ALfloat SpeedOfSound;
ALboolean SourceDistanceModel;
enum DistanceModel DistanceModel;
} Params;
} ALlistener;
void UpdateListenerProps(ALCcontext *context);
#ifdef __cplusplus
}
#endif
+499 -155
View File
@@ -3,6 +3,7 @@
#include <string.h>
#include <stdio.h>
#include <stddef.h>
#include <stdarg.h>
#include <assert.h>
#include <math.h>
@@ -20,6 +21,124 @@
#include "AL/alc.h"
#include "AL/alext.h"
#include "static_assert.h"
#include "align.h"
#include "atomic.h"
#include "uintmap.h"
#include "vector.h"
#include "alstring.h"
#include "almalloc.h"
#include "threads.h"
#ifndef ALC_SOFT_loopback2
#define ALC_SOFT_loopback2 1
#define ALC_AMBISONIC_LAYOUT_SOFT 0x1997
#define ALC_AMBISONIC_SCALING_SOFT 0x1998
#define ALC_AMBISONIC_ORDER_SOFT 0x1999
#define ALC_BFORMAT3D_SOFT 0x1508
/* Ambisonic layouts */
#define ALC_ACN_SOFT 0x1600
#define ALC_FUMA_SOFT 0x1601
/* Ambisonic scalings (normalization) */
/*#define ALC_FUMA_SOFT*/
#define ALC_SN3D_SOFT 0x1602
#define ALC_N3D_SOFT 0x1603
typedef ALCboolean (ALC_APIENTRY*LPALCISAMBISONICFORMATSUPPORTEDSOFT)(ALCdevice *device, ALCenum layout, ALCenum scaling, ALsizei order);
#ifdef AL_ALEXT_PROTOTYPES
ALC_API ALCboolean ALC_APIENTRY alcIsAmbisonicFormatSupportedSOFT(ALCdevice *device, ALCenum layout, ALCenum scaling, ALsizei order);
#endif
#endif
#ifndef ALC_SOFT_device_clock
#define ALC_SOFT_device_clock 1
typedef int64_t ALCint64SOFT;
typedef uint64_t ALCuint64SOFT;
#define ALC_DEVICE_CLOCK_SOFT 0x1600
#define ALC_DEVICE_LATENCY_SOFT 0x1601
#define ALC_DEVICE_CLOCK_LATENCY_SOFT 0x1602
typedef void (ALC_APIENTRY*LPALCGETINTEGER64VSOFT)(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values);
#ifdef AL_ALEXT_PROTOTYPES
ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values);
#endif
#endif
#ifndef AL_SOFT_buffer_samples2
#define AL_SOFT_buffer_samples2 1
/* Channel configurations */
#define AL_MONO_SOFT 0x1500
#define AL_STEREO_SOFT 0x1501
#define AL_REAR_SOFT 0x1502
#define AL_QUAD_SOFT 0x1503
#define AL_5POINT1_SOFT 0x1504
#define AL_6POINT1_SOFT 0x1505
#define AL_7POINT1_SOFT 0x1506
#define AL_BFORMAT2D_SOFT 0x1507
#define AL_BFORMAT3D_SOFT 0x1508
/* Sample types */
#define AL_BYTE_SOFT 0x1400
#define AL_UNSIGNED_BYTE_SOFT 0x1401
#define AL_SHORT_SOFT 0x1402
#define AL_UNSIGNED_SHORT_SOFT 0x1403
#define AL_INT_SOFT 0x1404
#define AL_UNSIGNED_INT_SOFT 0x1405
#define AL_FLOAT_SOFT 0x1406
#define AL_DOUBLE_SOFT 0x1407
#define AL_BYTE3_SOFT 0x1408
#define AL_UNSIGNED_BYTE3_SOFT 0x1409
#define AL_MULAW_SOFT 0x140A
/* Storage formats */
#define AL_MONO8_SOFT 0x1100
#define AL_MONO16_SOFT 0x1101
#define AL_MONO32F_SOFT 0x10010
#define AL_STEREO8_SOFT 0x1102
#define AL_STEREO16_SOFT 0x1103
#define AL_STEREO32F_SOFT 0x10011
#define AL_QUAD8_SOFT 0x1204
#define AL_QUAD16_SOFT 0x1205
#define AL_QUAD32F_SOFT 0x1206
#define AL_REAR8_SOFT 0x1207
#define AL_REAR16_SOFT 0x1208
#define AL_REAR32F_SOFT 0x1209
#define AL_5POINT1_8_SOFT 0x120A
#define AL_5POINT1_16_SOFT 0x120B
#define AL_5POINT1_32F_SOFT 0x120C
#define AL_6POINT1_8_SOFT 0x120D
#define AL_6POINT1_16_SOFT 0x120E
#define AL_6POINT1_32F_SOFT 0x120F
#define AL_7POINT1_8_SOFT 0x1210
#define AL_7POINT1_16_SOFT 0x1211
#define AL_7POINT1_32F_SOFT 0x1212
#define AL_BFORMAT2D_8_SOFT 0x20021
#define AL_BFORMAT2D_16_SOFT 0x20022
#define AL_BFORMAT2D_32F_SOFT 0x20023
#define AL_BFORMAT3D_8_SOFT 0x20031
#define AL_BFORMAT3D_16_SOFT 0x20032
#define AL_BFORMAT3D_32F_SOFT 0x20033
/* Buffer attributes */
#define AL_INTERNAL_FORMAT_SOFT 0x2008
#define AL_BYTE_LENGTH_SOFT 0x2009
#define AL_SAMPLE_LENGTH_SOFT 0x200A
#define AL_SEC_LENGTH_SOFT 0x200B
#if 0
typedef void (AL_APIENTRY*LPALBUFFERSAMPLESSOFT)(ALuint,ALuint,ALenum,ALsizei,ALenum,ALenum,const ALvoid*);
typedef void (AL_APIENTRY*LPALGETBUFFERSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,ALvoid*);
typedef ALboolean (AL_APIENTRY*LPALISBUFFERFORMATSUPPORTEDSOFT)(ALenum);
#ifdef AL_ALEXT_PROTOTYPES
AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, ALuint samplerate, ALenum internalformat, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data);
AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, ALvoid *data);
AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format);
#endif
#endif
#endif
#if defined(_WIN64)
#define SZFMT "%I64u"
@@ -30,24 +149,59 @@
#endif
#include "static_assert.h"
#include "align.h"
#include "atomic.h"
#include "uintmap.h"
#include "vector.h"
#include "alstring.h"
#include "hrtf.h"
#ifndef ALC_SOFT_device_clock
#define ALC_SOFT_device_clock 1
typedef int64_t ALCint64SOFT;
typedef uint64_t ALCuint64SOFT;
#define ALC_DEVICE_CLOCK_SOFT 0x1600
typedef void (ALC_APIENTRY*LPALCGETINTEGER64VSOFT)(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values);
#ifdef AL_ALEXT_PROTOTYPES
ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values);
#ifdef __GNUC__
/* Because of a long-standing deficiency in C, you're not allowed to implicitly
* cast a pointer-to-type-array to a pointer-to-const-type-array. For example,
*
* int (*ptr)[10];
* const int (*cptr)[10] = ptr;
*
* is not allowed and most compilers will generate noisy warnings about
* incompatible types, even though it just makes the array elements const.
* Clang will allow it if you make the array type a typedef, like this:
*
* typedef int int10[10];
* int10 *ptr;
* const int10 *cptr = ptr;
*
* however GCC does not and still issues the incompatible type warning. The
* "proper" way to fix it is to add an explicit cast for the constified type,
* but that removes the vast majority of otherwise useful type-checking you'd
* get, and runs the risk of improper casts if types are later changed. Leaving
* it non-const can also be an issue if you use it as a function parameter, and
* happen to have a const type as input (and also reduce the capabilities of
* the compiler to better optimize the function).
*
* So to work around the problem, we use a macro. The macro first assigns the
* incoming variable to the specified non-const type to ensure it's the correct
* type, then casts the variable as the desired constified type. Very ugly, but
* I'd rather not have hundreds of lines of warnings because I want to tell the
* compiler that some array(s) can't be changed by the code, or have lots of
* error-prone casts.
*/
#define SAFE_CONST(T, var) __extension__({ \
T _tmp = (var); \
(const T)_tmp; \
})
#else
/* Non-GNU-compatible compilers have to use a straight cast with no extra
* checks, due to the lack of multi-statement expressions.
*/
#define SAFE_CONST(T, var) ((const T)(var))
#endif
#ifdef __GNUC__
/* This helps cast away the const-ness of a pointer without accidentally
* changing the pointer type. This is necessary due to Clang's inability to use
* atomic_load on a const _Atomic variable.
*/
#define CONST_CAST(T, V) __extension__({ \
const T _tmp = (V); \
(T)_tmp; \
})
#else
#define CONST_CAST(T, V) ((T)(V))
#endif
@@ -81,13 +235,17 @@ typedef ALuint64SOFT ALuint64;
#endif
#ifdef __GNUC__
#define DECL_CONST __attribute__((const))
#define DECL_FORMAT(x, y, z) __attribute__((format(x, (y), (z))))
#else
#define DECL_CONST
#define DECL_FORMAT(x, y, z)
#endif
/* Calculates the size of a struct with N elements of a flexible array member.
* GCC and Clang allow offsetof(Type, fam[N]) for this, but MSVC seems to have
* trouble, so a bit more verbose workaround is needed.
*/
#define FAM_SIZE(T, M, N) (offsetof(T, M) + sizeof(((T*)NULL)->M[0])*(N))
#if defined(__GNUC__) && defined(__i386__)
/* force_align_arg_pointer is required for proper function arguments aligning
* when SSE code is used. Some systems (Windows, QNX) do not guarantee our
@@ -119,7 +277,7 @@ static const union {
} EndianTest = { 1 };
#define IS_LITTLE_ENDIAN (EndianTest.b[0] == 1)
#define COUNTOF(x) (sizeof((x))/sizeof((x)[0]))
#define COUNTOF(x) (sizeof(x) / sizeof(0[x]))
#define DERIVE_FROM_TYPE(t) t t##_parent
@@ -208,6 +366,12 @@ static void T##_Delete(void *ptr) { al_free(ptr); }
{ \
memset(_res, 0, sizeof(T)); \
T##_Construct(_res, EXTRACT_NEW_ARGS
#define NEW_OBJ0(_res, T) do { \
_res = T##_New(sizeof(T)); \
if(_res) \
{ \
memset(_res, 0, sizeof(T)); \
T##_Construct(_res EXTRACT_NEW_ARGS
#ifdef __cplusplus
@@ -215,6 +379,8 @@ extern "C" {
#endif
struct Hrtf;
struct HrtfEntry;
struct Compressor;
#define DEFAULT_OUTPUT_RATE (44100)
@@ -236,6 +402,31 @@ inline ALuint NextPowerOf2(ALuint value)
return value+1;
}
/** Round up a value to the next multiple. */
inline size_t RoundUp(size_t value, size_t r)
{
value += r-1;
return value - (value%r);
}
/* Scales the given value using 64-bit integer math, rounding the result. */
inline ALuint64 ScaleRound(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale)
{
return (val*new_scale + old_scale/2) / old_scale;
}
/* Scales the given value using 64-bit integer math, flooring the result. */
inline ALuint64 ScaleFloor(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale)
{
return val * new_scale / old_scale;
}
/* Scales the given value using 64-bit integer math, ceiling the result. */
inline ALuint64 ScaleCeil(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale)
{
return (val*new_scale + old_scale-1) / old_scale;
}
/* Fast float-to-int conversion. Assumes the FPU is already in round-to-zero
* mode. */
inline ALint fastf2i(ALfloat f)
@@ -252,45 +443,12 @@ inline ALint fastf2i(ALfloat f)
#endif
}
/* Fast float-to-uint conversion. Assumes the FPU is already in round-to-zero
* mode. */
inline ALuint fastf2u(ALfloat f)
{ return fastf2i(f); }
enum DevProbe {
ALL_DEVICE_PROBE,
CAPTURE_DEVICE_PROBE
};
typedef struct {
ALCenum (*OpenPlayback)(ALCdevice*, const ALCchar*);
void (*ClosePlayback)(ALCdevice*);
ALCboolean (*ResetPlayback)(ALCdevice*);
ALCboolean (*StartPlayback)(ALCdevice*);
void (*StopPlayback)(ALCdevice*);
ALCenum (*OpenCapture)(ALCdevice*, const ALCchar*);
void (*CloseCapture)(ALCdevice*);
void (*StartCapture)(ALCdevice*);
void (*StopCapture)(ALCdevice*);
ALCenum (*CaptureSamples)(ALCdevice*, void*, ALCuint);
ALCuint (*AvailableSamples)(ALCdevice*);
} BackendFuncs;
ALCboolean alc_sndio_init(BackendFuncs *func_list);
void alc_sndio_deinit(void);
void alc_sndio_probe(enum DevProbe type);
ALCboolean alc_ca_init(BackendFuncs *func_list);
void alc_ca_deinit(void);
void alc_ca_probe(enum DevProbe type);
ALCboolean alc_opensl_init(BackendFuncs *func_list);
void alc_opensl_deinit(void);
void alc_opensl_probe(enum DevProbe type);
ALCboolean alc_qsa_init(BackendFuncs *func_list);
void alc_qsa_deinit(void);
void alc_qsa_probe(enum DevProbe type);
struct ALCbackend;
@@ -317,10 +475,31 @@ enum Channel {
SideLeft,
SideRight,
BFormatW,
BFormatX,
BFormatY,
BFormatZ,
UpperFrontLeft,
UpperFrontRight,
UpperBackLeft,
UpperBackRight,
LowerFrontLeft,
LowerFrontRight,
LowerBackLeft,
LowerBackRight,
Aux0,
Aux1,
Aux2,
Aux3,
Aux4,
Aux5,
Aux6,
Aux7,
Aux8,
Aux9,
Aux10,
Aux11,
Aux12,
Aux13,
Aux14,
Aux15,
InvalidChannel
};
@@ -345,23 +524,37 @@ enum DevFmtChannels {
DevFmtX51 = ALC_5POINT1_SOFT,
DevFmtX61 = ALC_6POINT1_SOFT,
DevFmtX71 = ALC_7POINT1_SOFT,
DevFmtAmbi3D = ALC_BFORMAT3D_SOFT,
/* Similar to 5.1, except using rear channels instead of sides */
DevFmtX51Rear = 0x80000000,
DevFmtBFormat3D,
DevFmtChannelsDefault = DevFmtStereo
};
#define MAX_OUTPUT_CHANNELS (8)
#define MAX_OUTPUT_CHANNELS (16)
ALuint BytesFromDevFmt(enum DevFmtType type) DECL_CONST;
ALuint ChannelsFromDevFmt(enum DevFmtChannels chans) DECL_CONST;
inline ALuint FrameSizeFromDevFmt(enum DevFmtChannels chans, enum DevFmtType type)
ALsizei BytesFromDevFmt(enum DevFmtType type);
ALsizei ChannelsFromDevFmt(enum DevFmtChannels chans, ALsizei ambiorder);
inline ALsizei FrameSizeFromDevFmt(enum DevFmtChannels chans, enum DevFmtType type, ALsizei ambiorder)
{
return ChannelsFromDevFmt(chans) * BytesFromDevFmt(type);
return ChannelsFromDevFmt(chans, ambiorder) * BytesFromDevFmt(type);
}
enum AmbiLayout {
AmbiLayout_FuMa = ALC_FUMA_SOFT, /* FuMa channel order */
AmbiLayout_ACN = ALC_ACN_SOFT, /* ACN channel order */
AmbiLayout_Default = AmbiLayout_ACN
};
enum AmbiNorm {
AmbiNorm_FuMa = ALC_FUMA_SOFT, /* FuMa normalization */
AmbiNorm_SN3D = ALC_SN3D_SOFT, /* SN3D normalization */
AmbiNorm_N3D = ALC_N3D_SOFT, /* N3D normalization */
AmbiNorm_Default = AmbiNorm_SN3D
};
extern const struct EffectList {
const char *name;
@@ -378,25 +571,57 @@ enum DeviceType {
};
enum HrtfMode {
DisabledHrtf,
BasicHrtf,
FullHrtf
enum RenderMode {
NormalRender,
StereoPair,
HrtfRender
};
/* The maximum number of Ambisonics coefficients. For a given order (o), the
* size needed will be (o+1)**2, thus zero-order has 1, first-order has 4,
* second-order has 9, and third-order has 16. */
#define MAX_AMBI_COEFFS 16
* second-order has 9, third-order has 16, and fourth-order has 25.
*/
#define MAX_AMBI_ORDER 3
#define MAX_AMBI_COEFFS ((MAX_AMBI_ORDER+1) * (MAX_AMBI_ORDER+1))
/* A bitmask of ambisonic channels with height information. If none of these
* channels are used/needed, there's no height (e.g. with most surround sound
* speaker setups). This only specifies up to 4th order, which is the highest
* order a 32-bit mask value can specify (a 64-bit mask could handle up to 7th
* order). This is ACN ordering, with bit 0 being ACN 0, etc.
*/
#define AMBI_PERIPHONIC_MASK (0xfe7ce4)
/* The maximum number of Ambisonic coefficients for 2D (non-periphonic)
* representation. This is 2 per each order above zero-order, plus 1 for zero-
* order. Or simply, o*2 + 1.
*/
#define MAX_AMBI2D_COEFFS (MAX_AMBI_ORDER*2 + 1)
typedef ALfloat ChannelConfig[MAX_AMBI_COEFFS];
typedef struct BFChannelConfig {
ALfloat Scale;
ALsizei Index;
} BFChannelConfig;
typedef union AmbiConfig {
/* Ambisonic coefficients for mixing to the dry buffer. */
ChannelConfig Coeffs[MAX_OUTPUT_CHANNELS];
/* Coefficient channel mapping for mixing to the dry buffer. */
BFChannelConfig Map[MAX_OUTPUT_CHANNELS];
} AmbiConfig;
#define HRTF_HISTORY_BITS (6)
#define HRTF_HISTORY_LENGTH (1<<HRTF_HISTORY_BITS)
#define HRTF_HISTORY_MASK (HRTF_HISTORY_LENGTH-1)
#define HRIR_BITS (7)
#define HRIR_LENGTH (1<<HRIR_BITS)
#define HRIR_MASK (HRIR_LENGTH-1)
typedef struct HrtfState {
alignas(16) ALfloat History[HRTF_HISTORY_LENGTH];
alignas(16) ALfloat Values[HRIR_LENGTH][2];
@@ -404,18 +629,43 @@ typedef struct HrtfState {
typedef struct HrtfParams {
alignas(16) ALfloat Coeffs[HRIR_LENGTH][2];
alignas(16) ALfloat CoeffStep[HRIR_LENGTH][2];
ALuint Delay[2];
ALint DelayStep[2];
ALsizei Delay[2];
ALfloat Gain;
} HrtfParams;
typedef struct DirectHrtfState {
/* HRTF filter state for dry buffer content */
ALsizei Offset;
ALsizei IrSize;
struct {
alignas(16) ALfloat Values[HRIR_LENGTH][2];
alignas(16) ALfloat Coeffs[HRIR_LENGTH][2];
} Chan[];
} DirectHrtfState;
typedef struct EnumeratedHrtf {
al_string name;
struct HrtfEntry *hrtf;
} EnumeratedHrtf;
TYPEDEF_VECTOR(EnumeratedHrtf, vector_EnumeratedHrtf)
/* Maximum delay in samples for speaker distance compensation. */
#define MAX_DELAY_LENGTH 1024
typedef struct DistanceComp {
ALfloat Gain;
ALsizei Length; /* Valid range is [0...MAX_DELAY_LENGTH). */
ALfloat *Buffer;
} DistanceComp;
/* Size for temporary storage of buffer data, in ALfloats. Larger values need
* more memory, while smaller values may need more iterations. The value needs
* to be a sensible size, however, as it constrains the max stepping value used
* for mixing, as well as the maximum number of samples per mixing iteration.
*/
#define BUFFERSIZE (2048u)
#define BUFFERSIZE 2048
struct ALCdevice_struct
{
@@ -424,25 +674,31 @@ struct ALCdevice_struct
ALCboolean Connected;
enum DeviceType Type;
ALuint Frequency;
ALuint UpdateSize;
ALuint NumUpdates;
ALuint Frequency;
ALuint UpdateSize;
ALuint NumUpdates;
enum DevFmtChannels FmtChans;
enum DevFmtType FmtType;
ALboolean IsHeadphones;
ALboolean IsHeadphones;
ALsizei AmbiOrder;
/* For DevFmtAmbi* output only, specifies the channel order and
* normalization.
*/
enum AmbiLayout AmbiLayout;
enum AmbiNorm AmbiScale;
al_string DeviceName;
ATOMIC(ALCenum) LastError;
// Maximum number of sources that can be created
ALuint MaxNoOfSources;
ALuint SourcesMax;
// Maximum number of slots that can be created
ALuint AuxiliaryEffectSlotMax;
ALuint AuxiliaryEffectSlotMax;
ALCuint NumMonoSources;
ALCuint NumStereoSources;
ALuint NumAuxSends;
ALCuint NumMonoSources;
ALCuint NumStereoSources;
ALsizei NumAuxSends;
// Map of Buffers for this device
UIntMap BufferMap;
@@ -453,27 +709,31 @@ struct ALCdevice_struct
// Map of Filters for this device
UIntMap FilterMap;
/* HRTF filter tables */
vector_HrtfEntry Hrtf_List;
al_string Hrtf_Name;
const struct Hrtf *Hrtf;
ALCenum Hrtf_Status;
enum HrtfMode Hrtf_Mode;
HrtfState Hrtf_State[MAX_OUTPUT_CHANNELS];
HrtfParams Hrtf_Params[MAX_OUTPUT_CHANNELS];
ALuint Hrtf_Offset;
/* HRTF state and info */
DirectHrtfState *Hrtf;
al_string HrtfName;
struct Hrtf *HrtfHandle;
vector_EnumeratedHrtf HrtfList;
ALCenum HrtfStatus;
// Stereo-to-binaural filter
/* UHJ encoder state */
struct Uhj2Encoder *Uhj_Encoder;
/* High quality Ambisonic decoder */
struct BFormatDec *AmbiDecoder;
/* Stereo-to-binaural filter */
struct bs2b *Bs2b;
/* First-order ambisonic upsampler for higher-order output */
struct AmbiUpsampler *AmbiUp;
/* Rendering mode. */
enum RenderMode Render_Mode;
// Device flags
ALuint Flags;
enum Channel ChannelName[MAX_OUTPUT_CHANNELS];
ChannelConfig AmbiCoeffs[MAX_OUTPUT_CHANNELS];
ALfloat AmbiScale; /* Scale for first-order XYZ inputs using AmbCoeffs. */
ALuint NumChannels;
ALuint64 ClockBase;
ALuint SamplesDone;
@@ -481,9 +741,55 @@ struct ALCdevice_struct
alignas(16) ALfloat SourceData[BUFFERSIZE];
alignas(16) ALfloat ResampledData[BUFFERSIZE];
alignas(16) ALfloat FilteredData[BUFFERSIZE];
alignas(16) ALfloat NFCtrlData[BUFFERSIZE];
/* Dry path buffer mix. */
alignas(16) ALfloat (*DryBuffer)[BUFFERSIZE];
/* The "dry" path corresponds to the main output. */
struct {
AmbiConfig Ambi;
/* Number of coefficients in each Ambi.Coeffs to mix together (4 for
* first-order, 9 for second-order, etc). If the count is 0, Ambi.Map
* is used instead to map each output to a coefficient index.
*/
ALsizei CoeffCount;
ALfloat (*Buffer)[BUFFERSIZE];
ALsizei NumChannels;
ALsizei NumChannelsPerOrder[MAX_AMBI_ORDER+1];
} Dry;
/* First-order ambisonics output, to be upsampled to the dry buffer if different. */
struct {
AmbiConfig Ambi;
/* Will only be 4 or 0. */
ALsizei CoeffCount;
ALfloat (*Buffer)[BUFFERSIZE];
ALsizei NumChannels;
} FOAOut;
/* "Real" output, which will be written to the device buffer. May alias the
* dry buffer.
*/
struct {
enum Channel ChannelName[MAX_OUTPUT_CHANNELS];
ALfloat (*Buffer)[BUFFERSIZE];
ALsizei NumChannels;
} RealOut;
struct Compressor *Limiter;
/* The average speaker distance as determined by the ambdec configuration
* (or alternatively, by the NFC-HOA reference delay). Only used for NFC.
*/
ALfloat AvgSpeakerDist;
/* Delay buffers used to compensate for speaker distances. */
DistanceComp ChannelDelay[MAX_OUTPUT_CHANNELS];
/* Dithering control. */
ALfloat DitherDepth;
ALuint DitherSeed;
/* Running count of the mixer invocations, in 31.1 fixed point. This
* actually increments *twice* when mixing, first at the start and then at
@@ -492,34 +798,27 @@ struct ALCdevice_struct
*/
RefCount MixCount;
/* Default effect slot */
struct ALeffectslot *DefaultSlot;
// Contexts created on this device
ATOMIC(ALCcontext*) ContextList;
almtx_t BackendLock;
struct ALCbackend *Backend;
void *ExtraData; // For the backend's use
ALCdevice *volatile next;
/* Memory space used by the default slot (Playback devices only) */
alignas(16) ALCbyte _slot_mem[];
};
// Frequency was requested by the app or config file
#define DEVICE_FREQUENCY_REQUEST (1<<1)
#define DEVICE_FREQUENCY_REQUEST (1u<<1)
// Channel configuration was requested by the config file
#define DEVICE_CHANNELS_REQUEST (1<<2)
#define DEVICE_CHANNELS_REQUEST (1u<<2)
// Sample type was requested by the config file
#define DEVICE_SAMPLE_TYPE_REQUEST (1<<3)
#define DEVICE_SAMPLE_TYPE_REQUEST (1u<<3)
// Specifies if the DSP is paused at user request
#define DEVICE_PAUSED (1<<30)
#define DEVICE_PAUSED (1u<<30)
// Specifies if the device is currently running
#define DEVICE_RUNNING (1<<31)
#define DEVICE_RUNNING (1u<<31)
/* Nanosecond resolution for the device clock time. */
@@ -533,8 +832,7 @@ struct ALCdevice_struct
#define RECORD_THREAD_NAME "alsoft-record"
struct ALCcontext_struct
{
struct ALCcontext_struct {
RefCount ref;
struct ALlistener *Listener;
@@ -544,28 +842,39 @@ struct ALCcontext_struct
ATOMIC(ALenum) LastError;
ATOMIC(ALenum) UpdateSources;
enum DistanceModel DistanceModel;
ALboolean SourceDistanceModel;
volatile enum DistanceModel DistanceModel;
volatile ALboolean SourceDistanceModel;
ALfloat DopplerFactor;
ALfloat DopplerVelocity;
ALfloat SpeedOfSound;
ATOMIC(ALenum) DeferUpdates;
volatile ALfloat DopplerFactor;
volatile ALfloat DopplerVelocity;
volatile ALfloat SpeedOfSound;
volatile ALenum DeferUpdates;
RWLock PropLock;
struct ALvoice *Voices;
/* Counter for the pre-mixing updates, in 31.1 fixed point (lowest bit
* indicates if updates are currently happening).
*/
RefCount UpdateCount;
ATOMIC(ALenum) HoldUpdates;
ALfloat GainBoost;
struct ALvoice **Voices;
ALsizei VoiceCount;
ALsizei MaxVoices;
VECTOR(struct ALeffectslot*) ActiveAuxSlots;
ATOMIC(struct ALeffectslotArray*) ActiveAuxSlots;
/* Default effect slot */
struct ALeffectslot *DefaultSlot;
ALCdevice *Device;
const ALCchar *ExtensionList;
ALCcontext *volatile next;
/* Memory space used by the listener */
/* Memory space used by the listener (and possibly default effect slot) */
alignas(16) ALCbyte _listener_mem[];
};
@@ -574,6 +883,8 @@ ALCcontext *GetContextRef(void);
void ALCcontext_IncRef(ALCcontext *context);
void ALCcontext_DecRef(ALCcontext *context);
void AllocateVoices(ALCcontext *context, ALsizei num_voices, ALsizei old_sends);
void AppendAllDevicesList(const ALCchar *name);
void AppendCaptureDeviceList(const ALCchar *name);
@@ -583,21 +894,13 @@ void ALCdevice_Unlock(ALCdevice *device);
void ALCcontext_DeferUpdates(ALCcontext *context);
void ALCcontext_ProcessUpdates(ALCcontext *context);
inline void LockContext(ALCcontext *context)
{ ALCdevice_Lock(context->Device); }
inline void UnlockContext(ALCcontext *context)
{ ALCdevice_Unlock(context->Device); }
void *al_malloc(size_t alignment, size_t size);
void *al_calloc(size_t alignment, size_t size);
void al_free(void *ptr);
typedef struct {
#ifdef HAVE_FENV_H
DERIVE_FROM_TYPE(fenv_t);
#ifdef _WIN32
int round_mode;
#endif
#else
int state;
#endif
@@ -607,15 +910,19 @@ typedef struct {
} FPUCtl;
void SetMixerFPUMode(FPUCtl *ctl);
void RestoreFPUMode(const FPUCtl *ctl);
#ifdef __GNUC__
/* Use an alternate macro set with GCC to avoid accidental continue or break
* statements within the mixer mode.
*/
#define START_MIXER_MODE() __extension__({ FPUCtl _oldMode; SetMixerFPUMode(&_oldMode);
#define END_MIXER_MODE() RestoreFPUMode(&_oldMode); })
#else
#define START_MIXER_MODE() do { FPUCtl _oldMode; SetMixerFPUMode(&_oldMode);
#define END_MIXER_MODE() RestoreFPUMode(&_oldMode); } while(0)
#endif
#define LEAVE_MIXER_MODE() RestoreFPUMode(&_oldMode)
typedef struct RingBuffer RingBuffer;
RingBuffer *CreateRingBuffer(ALsizei frame_size, ALsizei length);
void DestroyRingBuffer(RingBuffer *ring);
ALsizei RingBufferSize(RingBuffer *ring);
void WriteRingBuffer(RingBuffer *ring, const ALubyte *data, ALsizei len);
void ReadRingBuffer(RingBuffer *ring, ALubyte *data, ALsizei len);
typedef struct ll_ringbuffer ll_ringbuffer_t;
typedef struct ll_ringbuffer_data {
char *buf;
@@ -651,26 +958,26 @@ void SetRTPriority(void);
void SetDefaultChannelOrder(ALCdevice *device);
void SetDefaultWFXChannelOrder(ALCdevice *device);
const ALCchar *DevFmtTypeString(enum DevFmtType type) DECL_CONST;
const ALCchar *DevFmtChannelsString(enum DevFmtChannels chans) DECL_CONST;
const ALCchar *DevFmtTypeString(enum DevFmtType type);
const ALCchar *DevFmtChannelsString(enum DevFmtChannels chans);
/**
* GetChannelIdxByName
*
* Returns the device's channel index given a channel name (e.g. FrontCenter),
* or -1 if it doesn't exist.
* Returns the index for the given channel name (e.g. FrontCenter), or -1 if it
* doesn't exist.
*/
inline ALint GetChannelIdxByName(const ALCdevice *device, enum Channel chan)
inline ALint GetChannelIndex(const enum Channel names[MAX_OUTPUT_CHANNELS], enum Channel chan)
{
ALint i = 0;
ALint i;
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
{
if(device->ChannelName[i] == chan)
if(names[i] == chan)
return i;
}
return -1;
}
#define GetChannelIdxByName(x, c) GetChannelIndex((x).ChannelName, (c))
extern FILE *LogFile;
@@ -681,6 +988,13 @@ void al_print(const char *type, const char *func, const char *fmt, ...) DECL_FOR
#define AL_PRINT(T, ...) al_print((T), __FUNCTION__, __VA_ARGS__)
#endif
#ifdef __ANDROID__
#include <android/log.h>
#define LOG_ANDROID(T, MSG, ...) __android_log_print(T, "openal", "AL lib: %s: "MSG, __FUNCTION__ , ## __VA_ARGS__)
#else
#define LOG_ANDROID(T, MSG, ...) ((void)0)
#endif
enum LogLevel {
NoLog,
LogError,
@@ -698,16 +1012,19 @@ extern enum LogLevel LogLevel;
#define TRACE(...) do { \
if(LogLevel >= LogTrace) \
AL_PRINT("(II)", __VA_ARGS__); \
LOG_ANDROID(ANDROID_LOG_DEBUG, __VA_ARGS__); \
} while(0)
#define WARN(...) do { \
if(LogLevel >= LogWarning) \
AL_PRINT("(WW)", __VA_ARGS__); \
LOG_ANDROID(ANDROID_LOG_WARN, __VA_ARGS__); \
} while(0)
#define ERR(...) do { \
if(LogLevel >= LogError) \
AL_PRINT("(EE)", __VA_ARGS__); \
LOG_ANDROID(ANDROID_LOG_ERROR, __VA_ARGS__); \
} while(0)
@@ -725,15 +1042,42 @@ enum {
void FillCPUCaps(ALuint capfilter);
FILE *OpenDataFile(const char *fname, const char *subdir);
vector_al_string SearchDataFiles(const char *match, const char *subdir);
/* Small hack to use a pointer-to-array type as a normal argument type.
* Shouldn't be used directly. */
/* Small hack to use a pointer-to-array types as a normal argument type.
* Shouldn't be used directly.
*/
typedef ALfloat ALfloatBUFFERSIZE[BUFFERSIZE];
typedef ALfloat ALfloat2[2];
/* The compressor requires the following information for proper
* initialization:
*
* PreGainDb - Gain applied before detection (in dB).
* PostGainDb - Gain applied after compression (in dB).
* SummedLink - Whether to use summed (true) or maxed (false) linking.
* RmsSensing - Whether to use RMS (true) or Peak (false) sensing.
* AttackTimeMin - Minimum attack time (in seconds).
* AttackTimeMax - Maximum attack time. Automates when min != max.
* ReleaseTimeMin - Minimum release time (in seconds).
* ReleaseTimeMax - Maximum release time. Automates when min != max.
* Ratio - Compression ratio (x:1). Set to 0 for true limiter.
* ThresholdDb - Triggering threshold (in dB).
* KneeDb - Knee width (below threshold; in dB).
* SampleRate - Sample rate to process.
*/
struct Compressor *CompressorInit(const ALfloat PreGainDb, const ALfloat PostGainDb,
const ALboolean SummedLink, const ALboolean RmsSensing, const ALfloat AttackTimeMin,
const ALfloat AttackTimeMax, const ALfloat ReleaseTimeMin, const ALfloat ReleaseTimeMax,
const ALfloat Ratio, const ALfloat ThresholdDb, const ALfloat KneeDb,
const ALuint SampleRate);
ALuint GetCompressorSampleRate(const struct Compressor *Comp);
void ApplyCompression(struct Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
ALfloat (*restrict OutBuffer)[BUFFERSIZE]);
#ifdef __cplusplus
}
#endif
+70 -90
View File
@@ -1,11 +1,14 @@
#ifndef _AL_SOURCE_H_
#define _AL_SOURCE_H_
#define MAX_SENDS 4
#include "bool.h"
#include "alMain.h"
#include "alu.h"
#include "hrtf.h"
#include "atomic.h"
#define MAX_SENDS 16
#define DEFAULT_SENDS 2
#ifdef __cplusplus
extern "C" {
@@ -17,95 +20,48 @@ struct ALsource;
typedef struct ALbufferlistitem {
struct ALbuffer *buffer;
struct ALbufferlistitem *volatile next;
struct ALbufferlistitem *volatile prev;
ATOMIC(struct ALbufferlistitem*) next;
} ALbufferlistitem;
typedef struct ALvoice {
struct ALsource *volatile Source;
/** Method to update mixing parameters. */
ALvoid (*Update)(struct ALvoice *self, const struct ALsource *source, const ALCcontext *context);
/** Current target parameters used for mixing. */
ALint Step;
ALboolean IsHrtf;
ALuint Offset; /* Number of output samples mixed since starting. */
alignas(16) ALfloat PrevSamples[MAX_INPUT_CHANNELS][MAX_PRE_SAMPLES];
BsincState SincState;
DirectParams Direct;
SendParams Send[MAX_SENDS];
} ALvoice;
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;
aluVector Position;
aluVector Velocity;
aluVector Direction;
volatile ALfloat Orientation[2][3];
volatile ALboolean HeadRelative;
volatile ALboolean Looping;
volatile enum DistanceModel DistanceModel;
volatile ALboolean DirectChannels;
ALfloat Pitch;
ALfloat Gain;
ALfloat OuterGain;
ALfloat MinGain;
ALfloat MaxGain;
ALfloat InnerAngle;
ALfloat OuterAngle;
ALfloat RefDistance;
ALfloat MaxDistance;
ALfloat RolloffFactor;
ALfloat Position[3];
ALfloat Velocity[3];
ALfloat Direction[3];
ALfloat Orientation[2][3];
ALboolean HeadRelative;
ALboolean Looping;
enum DistanceModel DistanceModel;
enum Resampler Resampler;
ALboolean DirectChannels;
enum SpatializeMode Spatialize;
volatile ALboolean DryGainHFAuto;
volatile ALboolean WetGainAuto;
volatile ALboolean WetGainHFAuto;
volatile ALfloat OuterGainHF;
ALboolean DryGainHFAuto;
ALboolean WetGainAuto;
ALboolean WetGainHFAuto;
ALfloat OuterGainHF;
volatile ALfloat AirAbsorptionFactor;
volatile ALfloat RoomRolloffFactor;
volatile ALfloat DopplerFactor;
ALfloat AirAbsorptionFactor;
ALfloat RoomRolloffFactor;
ALfloat DopplerFactor;
volatile ALfloat Radius;
/**
* Last user-specified offset, and the offset type (bytes, samples, or
* seconds).
/* NOTE: Stereo pan angles are specified in radians, counter-clockwise
* rather than clockwise.
*/
ALdouble Offset;
ALenum OffsetType;
ALfloat StereoPan[2];
/** 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;
ALfloat Radius;
/** Direct filter and auxiliary send info. */
struct {
@@ -122,22 +78,46 @@ typedef struct ALsource {
ALfloat HFReference;
ALfloat GainLF;
ALfloat LFReference;
} Send[MAX_SENDS];
} *Send;
/** Source needs to update its mixing parameters. */
ATOMIC(ALenum) NeedsUpdate;
/**
* Last user-specified offset, and the offset type (bytes, samples, or
* seconds).
*/
ALdouble Offset;
ALenum OffsetType;
/** Source type (static, streaming, or undetermined) */
ALint SourceType;
/** Source state (initial, playing, paused, or stopped) */
ATOMIC(ALenum) state;
/** Source Buffer Queue head. */
RWLock queue_lock;
ALbufferlistitem *queue;
ATOMIC_FLAG PropsClean;
/** 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); }
inline void LockSourcesRead(ALCcontext *context)
{ LockUIntMapRead(&context->SourceMap); }
inline void UnlockSourcesRead(ALCcontext *context)
{ UnlockUIntMapRead(&context->SourceMap); }
inline void LockSourcesWrite(ALCcontext *context)
{ LockUIntMapWrite(&context->SourceMap); }
inline void UnlockSourcesWrite(ALCcontext *context)
{ UnlockUIntMapWrite(&context->SourceMap); }
ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state);
ALboolean ApplyOffset(ALsource *Source);
inline struct ALsource *LookupSource(ALCcontext *context, ALuint id)
{ return (struct ALsource*)LookupUIntMapKeyNoLock(&context->SourceMap, id); }
inline struct ALsource *RemoveSource(ALCcontext *context, ALuint id)
{ return (struct ALsource*)RemoveUIntMapKeyNoLock(&context->SourceMap, id); }
void UpdateAllSourceProps(ALCcontext *context);
ALvoid ReleaseALSources(ALCcontext *Context);
+295 -109
View File
@@ -13,9 +13,11 @@
#include "alMain.h"
#include "alBuffer.h"
#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "hrtf.h"
#include "align.h"
#include "nfcfilter.h"
#include "math_defs.h"
@@ -33,9 +35,30 @@ extern "C" {
#endif
struct ALsource;
struct ALbufferlistitem;
struct ALvoice;
struct ALeffectslot;
#define DITHER_RNG_SEED 22222
enum SpatializeMode {
SpatializeOff = AL_FALSE,
SpatializeOn = AL_TRUE,
SpatializeAuto = AL_AUTO_SOFT
};
enum Resampler {
PointResampler,
LinearResampler,
FIR4Resampler,
BSincResampler,
ResamplerMax = BSincResampler
};
extern enum Resampler ResamplerDefault;
/* The number of distinct scale and phase intervals within the filter table. */
#define BSINC_SCALE_BITS 4
#define BSINC_SCALE_COUNT (1<<BSINC_SCALE_BITS)
@@ -58,6 +81,17 @@ typedef struct BsincState {
} coeffs[BSINC_PHASE_COUNT];
} BsincState;
typedef union InterpState {
BsincState bsinc;
} InterpState;
ALboolean BsincPrepare(const ALuint increment, BsincState *state);
typedef const ALfloat* (*ResamplerFunc)(const InterpState *state,
const ALfloat *restrict src, ALsizei frac, ALint increment,
ALfloat *restrict dst, ALsizei dstlen
);
typedef union aluVector {
alignas(16) ALfloat v[4];
@@ -75,6 +109,7 @@ inline void aluVectorSet(aluVector *vector, ALfloat x, ALfloat y, ALfloat z, ALf
typedef union aluMatrixf {
alignas(16) ALfloat m[4][4];
} aluMatrixf;
extern const aluMatrixf IdentityMatrixf;
inline void aluMatrixfSetRow(aluMatrixf *matrix, ALuint row,
ALfloat m0, ALfloat m1, ALfloat m2, ALfloat m3)
@@ -97,31 +132,6 @@ inline void aluMatrixfSet(aluMatrixf *matrix, ALfloat m00, ALfloat m01, ALfloat
}
typedef union aluMatrixd {
alignas(16) ALdouble m[4][4];
} aluMatrixd;
inline void aluMatrixdSetRow(aluMatrixd *matrix, ALuint row,
ALdouble m0, ALdouble m1, ALdouble m2, ALdouble m3)
{
matrix->m[row][0] = m0;
matrix->m[row][1] = m1;
matrix->m[row][2] = m2;
matrix->m[row][3] = m3;
}
inline void aluMatrixdSet(aluMatrixd *matrix, ALdouble m00, ALdouble m01, ALdouble m02, ALdouble m03,
ALdouble m10, ALdouble m11, ALdouble m12, ALdouble m13,
ALdouble m20, ALdouble m21, ALdouble m22, ALdouble m23,
ALdouble m30, ALdouble m31, ALdouble m32, ALdouble m33)
{
aluMatrixdSetRow(matrix, 0, m00, m01, m02, m03);
aluMatrixdSetRow(matrix, 1, m10, m11, m12, m13);
aluMatrixdSetRow(matrix, 2, m20, m21, m22, m23);
aluMatrixdSetRow(matrix, 3, m30, m31, m32, m33);
}
enum ActiveFilters {
AF_None = 0,
AF_LowPass = 1,
@@ -130,74 +140,200 @@ enum ActiveFilters {
};
typedef struct MixGains {
ALfloat Current;
ALfloat Step;
ALfloat Target;
} MixGains;
typedef struct MixHrtfParams {
const ALfloat (*Coeffs)[2];
ALsizei Delay[2];
ALfloat Gain;
ALfloat GainStep;
} MixHrtfParams;
typedef struct DirectParams {
ALfloat (*OutBuffer)[BUFFERSIZE];
ALuint OutChannels;
ALfilterState LowPass;
ALfilterState HighPass;
/* If not 'moving', gain/coefficients are set directly without fading. */
ALboolean Moving;
/* Stepping counter for gain/coefficient fading. */
ALuint Counter;
/* Last direction (relative to listener) and gain of a moving source. */
aluVector LastDir;
ALfloat LastGain;
NfcFilter NFCtrlFilter[MAX_AMBI_ORDER];
struct {
enum ActiveFilters ActiveType;
ALfilterState LowPass;
ALfilterState HighPass;
} Filters[MAX_INPUT_CHANNELS];
struct {
HrtfParams Params;
HrtfParams Old;
HrtfParams Target;
HrtfState State;
} Hrtf[MAX_INPUT_CHANNELS];
MixGains Gains[MAX_INPUT_CHANNELS][MAX_OUTPUT_CHANNELS];
} Hrtf;
struct {
ALfloat Current[MAX_OUTPUT_CHANNELS];
ALfloat Target[MAX_OUTPUT_CHANNELS];
} Gains;
} DirectParams;
typedef struct SendParams {
ALfloat (*OutBuffer)[BUFFERSIZE];
ALboolean Moving;
ALuint Counter;
ALfilterState LowPass;
ALfilterState HighPass;
struct {
enum ActiveFilters ActiveType;
ALfilterState LowPass;
ALfilterState HighPass;
} Filters[MAX_INPUT_CHANNELS];
/* Gain control, which applies to each input channel to a single (mono)
* output buffer. */
MixGains Gains[MAX_INPUT_CHANNELS];
ALfloat Current[MAX_OUTPUT_CHANNELS];
ALfloat Target[MAX_OUTPUT_CHANNELS];
} Gains;
} SendParams;
typedef const ALfloat* (*ResamplerFunc)(const BsincState *state,
const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen
);
struct ALvoiceProps {
ATOMIC(struct ALvoiceProps*) next;
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);
ALfloat Pitch;
ALfloat Gain;
ALfloat OuterGain;
ALfloat MinGain;
ALfloat MaxGain;
ALfloat InnerAngle;
ALfloat OuterAngle;
ALfloat RefDistance;
ALfloat MaxDistance;
ALfloat RolloffFactor;
ALfloat Position[3];
ALfloat Velocity[3];
ALfloat Direction[3];
ALfloat Orientation[2][3];
ALboolean HeadRelative;
enum DistanceModel DistanceModel;
enum Resampler Resampler;
ALboolean DirectChannels;
enum SpatializeMode SpatializeMode;
ALboolean DryGainHFAuto;
ALboolean WetGainAuto;
ALboolean WetGainHFAuto;
ALfloat OuterGainHF;
ALfloat AirAbsorptionFactor;
ALfloat RoomRolloffFactor;
ALfloat DopplerFactor;
ALfloat StereoPan[2];
ALfloat Radius;
/** Direct filter and auxiliary send info. */
struct {
ALfloat Gain;
ALfloat GainHF;
ALfloat HFReference;
ALfloat GainLF;
ALfloat LFReference;
} Direct;
struct {
struct ALeffectslot *Slot;
ALfloat Gain;
ALfloat GainHF;
ALfloat HFReference;
ALfloat GainLF;
ALfloat LFReference;
} Send[];
};
/* If not 'fading', gain targets are used directly without fading. */
#define VOICE_IS_FADING (1<<0)
#define VOICE_HAS_HRTF (1<<1)
#define VOICE_HAS_NFC (1<<2)
typedef struct ALvoice {
struct ALvoiceProps *Props;
ATOMIC(struct ALvoiceProps*) Update;
ATOMIC(struct ALvoiceProps*) FreeList;
ATOMIC(struct ALsource*) Source;
ATOMIC(bool) Playing;
/**
* Source offset in samples, relative to the currently playing buffer, NOT
* the whole queue, and the fractional (fixed-point) offset to the next
* sample.
*/
ATOMIC(ALuint) position;
ATOMIC(ALsizei) position_fraction;
/* Current buffer queue item being played. */
ATOMIC(struct ALbufferlistitem*) current_buffer;
/* Buffer queue item to loop to at end of queue (will be NULL for non-
* looping voices).
*/
ATOMIC(struct ALbufferlistitem*) loop_buffer;
/**
* Number of channels and bytes-per-sample for the attached source's
* buffer(s).
*/
ALsizei NumChannels;
ALsizei SampleSize;
/** Current target parameters used for mixing. */
ALint Step;
ResamplerFunc Resampler;
ALuint Flags;
ALuint Offset; /* Number of output samples mixed since starting. */
alignas(16) ALfloat PrevSamples[MAX_INPUT_CHANNELS][MAX_PRE_SAMPLES];
InterpState ResampleState;
struct {
enum ActiveFilters FilterType;
DirectParams Params[MAX_INPUT_CHANNELS];
ALfloat (*Buffer)[BUFFERSIZE];
ALsizei Channels;
ALsizei ChannelsPerOrder[MAX_AMBI_ORDER+1];
} Direct;
struct {
enum ActiveFilters FilterType;
SendParams Params[MAX_INPUT_CHANNELS];
ALfloat (*Buffer)[BUFFERSIZE];
ALsizei Channels;
} Send[];
} ALvoice;
void DeinitVoice(ALvoice *voice);
typedef void (*MixerFunc)(const ALfloat *data, ALsizei OutChans,
ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALfloat *CurrentGains,
const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
ALsizei BufferSize);
typedef void (*RowMixerFunc)(ALfloat *OutBuffer, const ALfloat *gains,
const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans,
ALsizei InPos, ALsizei BufferSize);
typedef void (*HrtfMixerFunc)(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
const ALfloat *data, ALsizei Offset, ALsizei OutPos,
const ALsizei IrSize, MixHrtfParams *hrtfparams,
HrtfState *hrtfstate, ALsizei BufferSize);
typedef void (*HrtfMixerBlendFunc)(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
const ALfloat *data, ALsizei Offset, ALsizei OutPos,
const ALsizei IrSize, const HrtfParams *oldparams,
MixHrtfParams *newparams, HrtfState *hrtfstate,
ALsizei BufferSize);
typedef void (*HrtfDirectMixerFunc)(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
const ALfloat *data, ALsizei Offset, const ALsizei IrSize,
const ALfloat (*restrict Coeffs)[2],
ALfloat (*restrict Values)[2], ALsizei BufferSize);
#define GAIN_MIX_MAX (16.0f) /* +24dB */
#define GAIN_SILENCE_THRESHOLD (0.00001f) /* -100dB */
#define SPEEDOFSOUNDMETRESPERSEC (343.3f)
#define AIRABSORBGAINHF (0.99426f) /* -0.05dB */
/* Target gain for the reverb decay feedback reaching the decay time. */
#define REVERB_DECAY_GAIN (0.001f) /* -60 dB */
#define FRACTIONBITS (12)
#define FRACTIONONE (1<<FRACTIONBITS)
#define FRACTIONMASK (FRACTIONONE-1)
@@ -246,79 +382,129 @@ inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max)
{ return minu64(max, maxu64(min, val)); }
union ResamplerCoeffs {
ALfloat FIR4[FRACTIONONE][4];
ALfloat FIR8[FRACTIONONE][8];
};
extern alignas(16) union ResamplerCoeffs ResampleCoeffs;
extern alignas(16) const ALfloat bsincTab[18840];
extern alignas(16) const ALfloat sinc4Tab[FRACTIONONE][4];
inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu)
{
return val1 + (val2-val1)*mu;
}
inline ALfloat resample_fir4(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALuint frac)
inline ALfloat resample_fir4(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALsizei frac)
{
const ALfloat *k = ResampleCoeffs.FIR4[frac];
return k[0]*val0 + k[1]*val1 + k[2]*val2 + k[3]*val3;
}
inline ALfloat resample_fir8(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALfloat val4, ALfloat val5, ALfloat val6, ALfloat val7, ALuint frac)
{
const ALfloat *k = ResampleCoeffs.FIR8[frac];
return k[0]*val0 + k[1]*val1 + k[2]*val2 + k[3]*val3 +
k[4]*val4 + k[5]*val5 + k[6]*val6 + k[7]*val7;
return sinc4Tab[frac][0]*val0 + sinc4Tab[frac][1]*val1 +
sinc4Tab[frac][2]*val2 + sinc4Tab[frac][3]*val3;
}
enum HrtfRequestMode {
Hrtf_Default = 0,
Hrtf_Enable = 1,
Hrtf_Disable = 2,
};
void aluInitMixer(void);
ALvoid aluInitPanning(ALCdevice *Device);
MixerFunc SelectMixer(void);
RowMixerFunc SelectRowMixer(void);
ResamplerFunc SelectResampler(enum Resampler resampler);
/* aluInitRenderer
*
* Set up the appropriate panning method and mixing method given the device
* properties.
*/
void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf_appreq, enum HrtfRequestMode hrtf_userreq);
void aluInitEffectPanning(struct ALeffectslot *slot);
/**
* ComputeDirectionalGains
* CalcDirectionCoeffs
*
* Sets channel gains based on a direction. The direction must be a 3-component
* vector no longer than 1 unit.
* Calculates ambisonic coefficients based on a direction vector. The vector
* must be normalized (unit length), and the spread is the angular width of the
* sound (0...tau).
*/
void ComputeDirectionalGains(const ALCdevice *device, const ALfloat dir[3], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
/**
* ComputeAngleGains
* CalcAngleCoeffs
*
* Sets channel gains based on angle and elevation. The angle and elevation
* parameters are in radians, going right and up respectively.
* Calculates ambisonic coefficients based on azimuth and elevation. The
* azimuth and elevation parameters are in radians, going right and up
* respectively.
*/
void ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat elevation, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
inline void CalcAngleCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS])
{
ALfloat dir[3] = {
sinf(azimuth) * cosf(elevation),
sinf(elevation),
-cosf(azimuth) * cosf(elevation)
};
CalcDirectionCoeffs(dir, spread, coeffs);
}
/**
* CalcAnglePairwiseCoeffs
*
* Calculates ambisonic coefficients based on azimuth and elevation. The
* azimuth and elevation parameters are in radians, going right and up
* respectively. This pairwise variant warps the result such that +30 azimuth
* is full right, and -30 azimuth is full left.
*/
void CalcAnglePairwiseCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
/**
* ComputeAmbientGains
*
* Sets channel gains for ambient, omni-directional sounds.
* Computes channel gains for ambient, omni-directional sounds.
*/
void ComputeAmbientGains(const ALCdevice *device, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
#define ComputeAmbientGains(b, g, o) do { \
if((b).CoeffCount > 0) \
ComputeAmbientGainsMC((b).Ambi.Coeffs, (b).NumChannels, g, o); \
else \
ComputeAmbientGainsBF((b).Ambi.Map, (b).NumChannels, g, o); \
} while (0)
void ComputeAmbientGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
void ComputeAmbientGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
/**
* ComputeBFormatGains
* ComputePanningGains
*
* Sets channel gains for a given (first-order) B-Format channel. The matrix is
* a 1x4 'slice' of the rotation matrix for a given channel used to orient the
* coefficients.
* Computes panning gains using the given channel decoder coefficients and the
* pre-calculated direction or angle coefficients.
*/
void ComputeBFormatGains(const ALCdevice *device, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
#define ComputePanningGains(b, c, g, o) do { \
if((b).CoeffCount > 0) \
ComputePanningGainsMC((b).Ambi.Coeffs, (b).NumChannels, (b).CoeffCount, c, g, o);\
else \
ComputePanningGainsBF((b).Ambi.Map, (b).NumChannels, c, g, o); \
} while (0)
void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALsizei numcoeffs, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
/**
* ComputeFirstOrderGains
*
* Sets channel gains for a first-order ambisonics input channel. The matrix is
* a 1x4 'slice' of a transform matrix for the input channel, used to scale and
* orient the sound samples.
*/
#define ComputeFirstOrderGains(b, m, g, o) do { \
if((b).CoeffCount > 0) \
ComputeFirstOrderGainsMC((b).Ambi.Coeffs, (b).NumChannels, m, g, o); \
else \
ComputeFirstOrderGainsBF((b).Ambi.Map, (b).NumChannels, m, g, o); \
} while (0)
void ComputeFirstOrderGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
void ComputeFirstOrderGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
ALvoid UpdateContextSources(ALCcontext *context);
ALboolean MixSource(struct ALvoice *voice, struct ALsource *Source, ALCdevice *Device, ALsizei SamplesToDo);
ALvoid CalcSourceParams(struct ALvoice *voice, const struct ALsource *source, const ALCcontext *ALContext);
ALvoid CalcNonAttnSourceParams(struct ALvoice *voice, const struct ALsource *source, const ALCcontext *ALContext);
ALvoid MixSource(struct ALvoice *voice, struct ALsource *source, ALCdevice *Device, ALuint SamplesToDo);
ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size);
void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples);
/* Caller must lock the device. */
ALvoid aluHandleDisconnect(ALCdevice *device);
void aluHandleDisconnect(ALCdevice *device);
extern ALfloat ConeScale;
extern ALfloat ZScale;
+5 -36
View File
@@ -63,10 +63,10 @@ struct bs2b {
* [0] - first channel, [1] - second channel
*/
struct t_last_sample {
float asis[2];
float lo[2];
float hi[2];
} last_sample;
float asis;
float lo;
float hi;
} last_sample[2];
};
/* Clear buffers and set new coefficients with new crossfeed level and sample
@@ -85,38 +85,7 @@ 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 */
void bs2b_cross_feed(struct bs2b *bs2b, float *restrict Left, float *restrict Right, int SamplesToDo);
#ifdef __cplusplus
} /* extern "C" */
+283 -106
View File
@@ -29,16 +29,19 @@
#include "alAuxEffectSlot.h"
#include "alThunk.h"
#include "alError.h"
#include "alListener.h"
#include "alSource.h"
#include "almalloc.h"
extern inline void LockEffectSlotsRead(ALCcontext *context);
extern inline void UnlockEffectSlotsRead(ALCcontext *context);
extern inline void LockEffectSlotsWrite(ALCcontext *context);
extern inline void UnlockEffectSlotsWrite(ALCcontext *context);
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);
static UIntMap EffectStateFactoryMap;
static inline ALeffectStateFactory *getFactoryByType(ALenum type)
{
@@ -48,24 +51,32 @@ static inline ALeffectStateFactory *getFactoryByType(ALenum type)
return NULL;
}
static void ALeffectState_IncRef(ALeffectState *state);
static void ALeffectState_DecRef(ALeffectState *state);
#define DO_UPDATEPROPS() do { \
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) \
UpdateEffectSlotProps(slot); \
else \
ATOMIC_FLAG_CLEAR(&slot->PropsClean, almemory_order_release); \
} while(0)
AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots)
{
ALCcontext *context;
VECTOR(ALeffectslot*) slotvec;
ALeffectslot **tmpslots = NULL;
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);
tmpslots = al_malloc(DEF_ALIGN, sizeof(ALeffectslot*)*n);
LockEffectSlotsWrite(context);
for(cur = 0;cur < n;cur++)
{
ALeffectslot *slot = al_calloc(16, sizeof(ALeffectslot));
@@ -73,37 +84,57 @@ AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslo
if(!slot || (err=InitEffectSlot(slot)) != AL_NO_ERROR)
{
al_free(slot);
UnlockEffectSlotsWrite(context);
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);
err = InsertUIntMapEntryNoLock(&context->EffectSlotMap, slot->id, slot);
if(err != AL_NO_ERROR)
{
FreeThunkEntry(slot->id);
DELETE_OBJ(slot->EffectState);
ALeffectState_DecRef(slot->Effect.State);
if(slot->Params.EffectState)
ALeffectState_DecRef(slot->Params.EffectState);
al_free(slot);
UnlockEffectSlotsWrite(context);
alDeleteAuxiliaryEffectSlots(cur, effectslots);
SET_ERROR_AND_GOTO(context, err, done);
}
VECTOR_PUSH_BACK(slotvec, slot);
aluInitEffectPanning(slot);
tmpslots[cur] = slot;
effectslots[cur] = slot->id;
}
err = AddEffectSlotArray(context, VECTOR_ITER_BEGIN(slotvec), n);
if(err != AL_NO_ERROR)
if(n > 0)
{
alDeleteAuxiliaryEffectSlots(cur, effectslots);
SET_ERROR_AND_GOTO(context, err, done);
struct ALeffectslotArray *curarray = ATOMIC_LOAD(&context->ActiveAuxSlots, almemory_order_acquire);
struct ALeffectslotArray *newarray = NULL;
ALsizei newcount = curarray->count + n;
ALCdevice *device;
newarray = al_calloc(DEF_ALIGN, FAM_SIZE(struct ALeffectslotArray, slot, newcount));
newarray->count = newcount;
memcpy(newarray->slot, tmpslots, sizeof(ALeffectslot*)*n);
if(curarray)
memcpy(newarray->slot+n, curarray->slot, sizeof(ALeffectslot*)*curarray->count);
newarray = ATOMIC_EXCHANGE_PTR(&context->ActiveAuxSlots, newarray,
almemory_order_acq_rel);
device = context->Device;
while((ATOMIC_LOAD(&device->MixCount, almemory_order_acquire)&1))
althrd_yield();
al_free(newarray);
}
UnlockEffectSlotsWrite(context);
done:
VECTOR_DEINIT(slotvec);
al_free(tmpslots);
ALCcontext_DecRef(context);
}
@@ -116,6 +147,7 @@ AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *
context = GetContextRef();
if(!context) return;
LockEffectSlotsWrite(context);
if(!(n >= 0))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
for(i = 0;i < n;i++)
@@ -127,20 +159,51 @@ AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *
}
// All effectslots are valid
if(n > 0)
{
struct ALeffectslotArray *curarray = ATOMIC_LOAD(&context->ActiveAuxSlots, almemory_order_acquire);
struct ALeffectslotArray *newarray = NULL;
ALsizei newcount = curarray->count - n;
ALCdevice *device;
ALsizei j, k;
assert(newcount >= 0);
newarray = al_calloc(DEF_ALIGN, FAM_SIZE(struct ALeffectslotArray, slot, newcount));
newarray->count = newcount;
for(i = j = 0;i < newarray->count;)
{
slot = curarray->slot[j++];
for(k = 0;k < n;k++)
{
if(slot->id == effectslots[k])
break;
}
if(k == n)
newarray->slot[i++] = slot;
}
newarray = ATOMIC_EXCHANGE_PTR(&context->ActiveAuxSlots, newarray,
almemory_order_acq_rel);
device = context->Device;
while((ATOMIC_LOAD(&device->MixCount, almemory_order_acquire)&1))
althrd_yield();
al_free(newarray);
}
for(i = 0;i < n;i++)
{
if((slot=RemoveEffectSlot(context, effectslots[i])) == NULL)
continue;
FreeThunkEntry(slot->id);
RemoveEffectSlotArray(context, slot);
DELETE_OBJ(slot->EffectState);
DeinitEffectSlot(slot);
memset(slot, 0, sizeof(*slot));
al_free(slot);
}
done:
UnlockEffectSlotsWrite(context);
ALCcontext_DecRef(context);
}
@@ -152,7 +215,9 @@ AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot)
context = GetContextRef();
if(!context) return AL_FALSE;
LockEffectSlotsRead(context);
ret = (LookupEffectSlot(context, effectslot) ? AL_TRUE : AL_FALSE);
UnlockEffectSlotsRead(context);
ALCcontext_DecRef(context);
@@ -170,35 +235,43 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param
context = GetContextRef();
if(!context) return;
device = context->Device;
WriteLock(&context->PropLock);
LockEffectSlotsRead(context);
if((slot=LookupEffectSlot(context, effectslot)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
{
case AL_EFFECTSLOT_EFFECT:
device = context->Device;
LockEffectsRead(device);
effect = (value ? LookupEffect(device, value) : NULL);
if(!(value == 0 || effect != NULL))
{
UnlockEffectsRead(device);
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
}
err = InitializeEffect(device, slot, effect);
UnlockEffectsRead(device);
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);
}
DO_UPDATEPROPS();
done:
UnlockEffectSlotsRead(context);
WriteUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
@@ -217,6 +290,7 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum para
context = GetContextRef();
if(!context) return;
LockEffectSlotsRead(context);
if(LookupEffectSlot(context, effectslot) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
@@ -226,6 +300,7 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum para
}
done:
UnlockEffectSlotsRead(context);
ALCcontext_DecRef(context);
}
@@ -237,6 +312,8 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param
context = GetContextRef();
if(!context) return;
WriteLock(&context->PropLock);
LockEffectSlotsRead(context);
if((slot=LookupEffectSlot(context, effectslot)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
@@ -244,16 +321,17 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum 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);
}
DO_UPDATEPROPS();
done:
UnlockEffectSlotsRead(context);
WriteUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
@@ -271,6 +349,7 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum para
context = GetContextRef();
if(!context) return;
LockEffectSlotsRead(context);
if(LookupEffectSlot(context, effectslot) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
@@ -280,6 +359,7 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum para
}
done:
UnlockEffectSlotsRead(context);
ALCcontext_DecRef(context);
}
@@ -291,6 +371,7 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum pa
context = GetContextRef();
if(!context) return;
LockEffectSlotsRead(context);
if((slot=LookupEffectSlot(context, effectslot)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
@@ -304,6 +385,7 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum pa
}
done:
UnlockEffectSlotsRead(context);
ALCcontext_DecRef(context);
}
@@ -322,6 +404,7 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum p
context = GetContextRef();
if(!context) return;
LockEffectSlotsRead(context);
if(LookupEffectSlot(context, effectslot) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
@@ -331,6 +414,7 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum p
}
done:
UnlockEffectSlotsRead(context);
ALCcontext_DecRef(context);
}
@@ -342,6 +426,7 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum pa
context = GetContextRef();
if(!context) return;
LockEffectSlotsRead(context);
if((slot=LookupEffectSlot(context, effectslot)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
@@ -355,6 +440,7 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum pa
}
done:
UnlockEffectSlotsRead(context);
ALCcontext_DecRef(context);
}
@@ -372,6 +458,7 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum p
context = GetContextRef();
if(!context) return;
LockEffectSlotsRead(context);
if(LookupEffectSlot(context, effectslot) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
@@ -381,47 +468,18 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum p
}
done:
UnlockEffectSlotsRead(context);
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);
InitUIntMap(&EffectStateFactoryMap, INT_MAX);
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);
@@ -442,12 +500,12 @@ void DeinitEffectFactoryMap(void)
ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *effect)
{
ALenum newtype = (effect ? effect->type : AL_EFFECT_NULL);
ALeffectStateFactory *factory;
struct ALeffectslotProps *props;
ALeffectState *State;
if(newtype != EffectSlot->EffectType)
if(newtype != EffectSlot->Effect.Type)
{
ALeffectState *State;
FPUCtl oldMode;
ALeffectStateFactory *factory;
factory = getFactoryByType(newtype);
if(!factory)
@@ -456,92 +514,211 @@ ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *e
return AL_INVALID_ENUM;
}
State = V0(factory,create)();
if(!State)
return AL_OUT_OF_MEMORY;
if(!State) return AL_OUT_OF_MEMORY;
SetMixerFPUMode(&oldMode);
ALCdevice_Lock(Device);
START_MIXER_MODE();
almtx_lock(&Device->BackendLock);
State->OutBuffer = Device->Dry.Buffer;
State->OutChannels = Device->Dry.NumChannels;
if(V(State,deviceUpdate)(Device) == AL_FALSE)
{
ALCdevice_Unlock(Device);
RestoreFPUMode(&oldMode);
DELETE_OBJ(State);
almtx_unlock(&Device->BackendLock);
LEAVE_MIXER_MODE();
ALeffectState_DecRef(State);
return AL_OUT_OF_MEMORY;
}
almtx_unlock(&Device->BackendLock);
END_MIXER_MODE();
State = ExchangePtr((XchgPtr*)&EffectSlot->EffectState, State);
if(!effect)
{
memset(&EffectSlot->EffectProps, 0, sizeof(EffectSlot->EffectProps));
EffectSlot->EffectType = AL_EFFECT_NULL;
EffectSlot->Effect.Type = AL_EFFECT_NULL;
memset(&EffectSlot->Effect.Props, 0, sizeof(EffectSlot->Effect.Props));
}
else
{
memcpy(&EffectSlot->EffectProps, &effect->Props, sizeof(effect->Props));
EffectSlot->EffectType = effect->type;
EffectSlot->Effect.Type = effect->type;
EffectSlot->Effect.Props = effect->Props;
}
/* 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;
ALeffectState_DecRef(EffectSlot->Effect.State);
EffectSlot->Effect.State = State;
}
else
else if(effect)
EffectSlot->Effect.Props = effect->Props;
/* Remove state references from old effect slot property updates. */
props = ATOMIC_LOAD_SEQ(&EffectSlot->FreeList);
while(props)
{
if(effect)
{
ALCdevice_Lock(Device);
memcpy(&EffectSlot->EffectProps, &effect->Props, sizeof(effect->Props));
ALCdevice_Unlock(Device);
ATOMIC_STORE(&EffectSlot->NeedsUpdate, AL_TRUE);
}
if(props->State)
ALeffectState_DecRef(props->State);
props->State = NULL;
props = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
}
return AL_NO_ERROR;
}
static void ALeffectState_IncRef(ALeffectState *state)
{
uint ref;
ref = IncrementRef(&state->Ref);
TRACEREF("%p increasing refcount to %u\n", state, ref);
}
static void ALeffectState_DecRef(ALeffectState *state)
{
uint ref;
ref = DecrementRef(&state->Ref);
TRACEREF("%p decreasing refcount to %u\n", state, ref);
if(ref == 0) DELETE_OBJ(state);
}
void ALeffectState_Construct(ALeffectState *state)
{
InitRef(&state->Ref, 1);
state->OutBuffer = NULL;
state->OutChannels = 0;
}
void ALeffectState_Destruct(ALeffectState *UNUSED(state))
{
}
ALenum InitEffectSlot(ALeffectslot *slot)
{
ALeffectStateFactory *factory;
ALuint i, c;
slot->EffectType = AL_EFFECT_NULL;
slot->Effect.Type = AL_EFFECT_NULL;
factory = getFactoryByType(AL_EFFECT_NULL);
if(!(slot->EffectState=V0(factory,create)()))
if(!(slot->Effect.State=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;
}
ATOMIC_FLAG_TEST_AND_SET(&slot->PropsClean, almemory_order_relaxed);
InitRef(&slot->ref, 0);
ATOMIC_INIT(&slot->Update, NULL);
ATOMIC_INIT(&slot->FreeList, NULL);
slot->Params.Gain = 1.0f;
slot->Params.AuxSendAuto = AL_TRUE;
ALeffectState_IncRef(slot->Effect.State);
slot->Params.EffectState = slot->Effect.State;
slot->Params.RoomRolloff = 0.0f;
slot->Params.DecayTime = 0.0f;
slot->Params.DecayHFRatio = 0.0f;
slot->Params.DecayHFLimit = AL_FALSE;
slot->Params.AirAbsorptionGainHF = 1.0f;
return AL_NO_ERROR;
}
void DeinitEffectSlot(ALeffectslot *slot)
{
struct ALeffectslotProps *props;
size_t count = 0;
props = ATOMIC_LOAD_SEQ(&slot->Update);
if(props)
{
if(props->State) ALeffectState_DecRef(props->State);
TRACE("Freed unapplied AuxiliaryEffectSlot update %p\n", props);
al_free(props);
}
props = ATOMIC_LOAD(&slot->FreeList, almemory_order_relaxed);
while(props)
{
struct ALeffectslotProps *next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
if(props->State) ALeffectState_DecRef(props->State);
al_free(props);
props = next;
++count;
}
TRACE("Freed "SZFMT" AuxiliaryEffectSlot property object%s\n", count, (count==1)?"":"s");
ALeffectState_DecRef(slot->Effect.State);
if(slot->Params.EffectState)
ALeffectState_DecRef(slot->Params.EffectState);
}
void UpdateEffectSlotProps(ALeffectslot *slot)
{
struct ALeffectslotProps *props;
ALeffectState *oldstate;
/* Get an unused property container, or allocate a new one as needed. */
props = ATOMIC_LOAD(&slot->FreeList, almemory_order_relaxed);
if(!props)
props = al_calloc(16, sizeof(*props));
else
{
struct ALeffectslotProps *next;
do {
next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
} while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(&slot->FreeList, &props, next,
almemory_order_seq_cst, almemory_order_acquire) == 0);
}
/* Copy in current property values. */
props->Gain = slot->Gain;
props->AuxSendAuto = slot->AuxSendAuto;
props->Type = slot->Effect.Type;
props->Props = slot->Effect.Props;
/* Swap out any stale effect state object there may be in the container, to
* delete it.
*/
ALeffectState_IncRef(slot->Effect.State);
oldstate = props->State;
props->State = slot->Effect.State;
/* Set the new container for updating internal parameters. */
props = ATOMIC_EXCHANGE_PTR(&slot->Update, props, almemory_order_acq_rel);
if(props)
{
/* If there was an unused update container, put it back in the
* freelist.
*/
ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &slot->FreeList, props);
}
if(oldstate)
ALeffectState_DecRef(oldstate);
}
void UpdateAllEffectSlotProps(ALCcontext *context)
{
struct ALeffectslotArray *auxslots;
ALsizei i;
LockEffectSlotsRead(context);
auxslots = ATOMIC_LOAD(&context->ActiveAuxSlots, almemory_order_acquire);
for(i = 0;i < auxslots->count;i++)
{
ALeffectslot *slot = auxslots->slot[i];
if(!ATOMIC_FLAG_TEST_AND_SET(&slot->PropsClean, almemory_order_acq_rel))
UpdateEffectSlotProps(slot);
}
UnlockEffectSlotsRead(context);
}
ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context)
{
ALsizei pos;
for(pos = 0;pos < Context->EffectSlotMap.size;pos++)
{
ALeffectslot *temp = Context->EffectSlotMap.array[pos].value;
Context->EffectSlotMap.array[pos].value = NULL;
ALeffectslot *temp = Context->EffectSlotMap.values[pos];
Context->EffectSlotMap.values[pos] = NULL;
DELETE_OBJ(temp->EffectState);
DeinitEffectSlot(temp);
FreeThunkEntry(temp->id);
memset(temp, 0, sizeof(ALeffectslot));
+108 -58
View File
@@ -36,15 +36,19 @@
#include "sample_cvt.h"
extern inline void LockBuffersRead(ALCdevice *device);
extern inline void UnlockBuffersRead(ALCdevice *device);
extern inline void LockBuffersWrite(ALCdevice *device);
extern inline void UnlockBuffersWrite(ALCdevice *device);
extern inline struct ALbuffer *LookupBuffer(ALCdevice *device, ALuint id);
extern inline struct ALbuffer *RemoveBuffer(ALCdevice *device, ALuint id);
extern inline ALuint FrameSizeFromUserFmt(enum UserFmtChannels chans, enum UserFmtType type);
extern inline ALuint FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type);
extern inline ALsizei FrameSizeFromUserFmt(enum UserFmtChannels chans, enum UserFmtType type);
extern inline ALsizei FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type);
static ALboolean IsValidType(ALenum type) DECL_CONST;
static ALboolean IsValidChannels(ALenum channels) DECL_CONST;
static ALboolean DecomposeUserFormat(ALenum format, enum UserFmtChannels *chans, enum UserFmtType *type) DECL_CONST;
static ALboolean DecomposeFormat(ALenum format, enum FmtChannels *chans, enum FmtType *type) DECL_CONST;
static ALboolean IsValidType(ALenum type);
static ALboolean IsValidChannels(ALenum channels);
static ALboolean DecomposeUserFormat(ALenum format, enum UserFmtChannels *chans, enum UserFmtType *type);
static ALboolean DecomposeFormat(ALenum format, enum FmtChannels *chans, enum FmtType *type);
static ALboolean SanitizeAlignment(enum UserFmtType type, ALsizei *align);
@@ -85,10 +89,12 @@ AL_API ALvoid AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers)
context = GetContextRef();
if(!context) return;
device = context->Device;
LockBuffersWrite(device);
if(!(n >= 0))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
device = context->Device;
for(i = 0;i < n;i++)
{
if(!buffers[i])
@@ -108,6 +114,7 @@ AL_API ALvoid AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers)
}
done:
UnlockBuffersWrite(device);
ALCcontext_DecRef(context);
}
@@ -119,8 +126,10 @@ AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer)
context = GetContextRef();
if(!context) return AL_FALSE;
LockBuffersRead(context->Device);
ret = ((!buffer || LookupBuffer(context->Device, buffer)) ?
AL_TRUE : AL_FALSE);
UnlockBuffersRead(context->Device);
ALCcontext_DecRef(context);
@@ -130,13 +139,13 @@ AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer)
AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq)
{
enum UserFmtChannels srcchannels;
enum UserFmtType srctype;
enum UserFmtChannels srcchannels = UserFmtMono;
enum UserFmtType srctype = UserFmtByte;
ALCdevice *device;
ALCcontext *context;
ALbuffer *albuf;
ALenum newformat = AL_NONE;
ALuint framesize;
ALsizei framesize;
ALsizei align;
ALenum err;
@@ -144,6 +153,7 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi
if(!context) return;
device = context->Device;
LockBuffersRead(device);
if((albuf=LookupBuffer(device, buffer)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
if(!(size >= 0 && freq > 0))
@@ -151,7 +161,7 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi
if(DecomposeUserFormat(format, &srcchannels, &srctype) == AL_FALSE)
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
align = ATOMIC_LOAD(&albuf->UnpackAlign);
align = ATOMIC_LOAD_SEQ(&albuf->UnpackAlign);
if(SanitizeAlignment(srctype, &align) == AL_FALSE)
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
switch(srctype)
@@ -173,8 +183,6 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi
case UserFmtInt:
case UserFmtUInt:
case UserFmtByte3:
case UserFmtUByte3:
case UserFmtDouble:
framesize = FrameSizeFromUserFmt(srcchannels, srctype) * align;
if((size%framesize) != 0)
@@ -272,25 +280,27 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi
}
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei offset, ALsizei length)
{
enum UserFmtChannels srcchannels;
enum UserFmtType srctype;
enum UserFmtChannels srcchannels = UserFmtMono;
enum UserFmtType srctype = UserFmtByte;
ALCdevice *device;
ALCcontext *context;
ALbuffer *albuf;
ALuint byte_align;
ALuint channels;
ALuint bytes;
ALsizei byte_align;
ALsizei channels;
ALsizei bytes;
ALsizei align;
context = GetContextRef();
if(!context) return;
device = context->Device;
LockBuffersRead(device);
if((albuf=LookupBuffer(device, buffer)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
if(!(length >= 0 && offset >= 0))
@@ -299,7 +309,7 @@ AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer, ALenum format, cons
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
WriteLock(&albuf->lock);
align = ATOMIC_LOAD(&albuf->UnpackAlign);
align = ATOMIC_LOAD_SEQ(&albuf->UnpackAlign);
if(SanitizeAlignment(srctype, &align) == AL_FALSE)
{
WriteUnlock(&albuf->lock);
@@ -351,6 +361,7 @@ AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer, ALenum format, cons
WriteUnlock(&albuf->lock);
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -369,6 +380,7 @@ AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer,
if(!context) return;
device = context->Device;
LockBuffersRead(device);
if((albuf=LookupBuffer(device, buffer)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
if(!(samples >= 0 && samplerate != 0))
@@ -376,7 +388,7 @@ AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer,
if(IsValidType(type) == AL_FALSE || IsValidChannels(channels) == AL_FALSE)
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
align = ATOMIC_LOAD(&albuf->UnpackAlign);
align = ATOMIC_LOAD_SEQ(&albuf->UnpackAlign);
if(SanitizeAlignment(type, &align) == AL_FALSE)
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
if((samples%align) != 0)
@@ -388,6 +400,7 @@ AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer,
SET_ERROR_AND_GOTO(context, err, done);
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -404,6 +417,7 @@ AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer,
if(!context) return;
device = context->Device;
LockBuffersRead(device);
if((albuf=LookupBuffer(device, buffer)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
if(!(samples >= 0 && offset >= 0))
@@ -412,7 +426,7 @@ AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer,
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
WriteLock(&albuf->lock);
align = ATOMIC_LOAD(&albuf->UnpackAlign);
align = ATOMIC_LOAD_SEQ(&albuf->UnpackAlign);
if(SanitizeAlignment(type, &align) == AL_FALSE)
{
WriteUnlock(&albuf->lock);
@@ -441,6 +455,7 @@ AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer,
WriteUnlock(&albuf->lock);
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -457,6 +472,7 @@ AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer,
if(!context) return;
device = context->Device;
LockBuffersRead(device);
if((albuf=LookupBuffer(device, buffer)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
if(!(samples >= 0 && offset >= 0))
@@ -465,7 +481,7 @@ AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer,
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
ReadLock(&albuf->lock);
align = ATOMIC_LOAD(&albuf->PackAlign);
align = ATOMIC_LOAD_SEQ(&albuf->PackAlign);
if(SanitizeAlignment(type, &align) == AL_FALSE)
{
ReadUnlock(&albuf->lock);
@@ -494,6 +510,7 @@ AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer,
ReadUnlock(&albuf->lock);
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -524,6 +541,7 @@ AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat UNUSED(va
if(!context) return;
device = context->Device;
LockBuffersRead(device);
if(LookupBuffer(device, buffer) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
@@ -534,6 +552,7 @@ AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat UNUSED(va
}
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -547,6 +566,7 @@ AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, ALfloat UNUSED(v
if(!context) return;
device = context->Device;
LockBuffersRead(device);
if(LookupBuffer(device, buffer) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
@@ -557,6 +577,7 @@ AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, ALfloat UNUSED(v
}
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -570,6 +591,7 @@ AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *v
if(!context) return;
device = context->Device;
LockBuffersRead(device);
if(LookupBuffer(device, buffer) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
@@ -582,6 +604,7 @@ AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *v
}
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -596,6 +619,7 @@ AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value)
if(!context) return;
device = context->Device;
LockBuffersRead(device);
if((albuf=LookupBuffer(device, buffer)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
@@ -604,13 +628,13 @@ AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value)
case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
if(!(value >= 0))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
ATOMIC_STORE(&albuf->UnpackAlign, value);
ATOMIC_STORE_SEQ(&albuf->UnpackAlign, value);
break;
case AL_PACK_BLOCK_ALIGNMENT_SOFT:
if(!(value >= 0))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
ATOMIC_STORE(&albuf->PackAlign, value);
ATOMIC_STORE_SEQ(&albuf->PackAlign, value);
break;
default:
@@ -618,6 +642,7 @@ AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value)
}
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -666,6 +691,7 @@ AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *val
if(!context) return;
device = context->Device;
LockBuffersRead(device);
if((albuf=LookupBuffer(device, buffer)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
@@ -697,6 +723,7 @@ AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *val
}
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -711,6 +738,7 @@ AL_API ALvoid AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *val
if(!context) return;
device = context->Device;
LockBuffersRead(device);
if((albuf=LookupBuffer(device, buffer)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
@@ -732,6 +760,7 @@ AL_API ALvoid AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *val
}
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -745,6 +774,7 @@ AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *valu
if(!context) return;
device = context->Device;
LockBuffersRead(device);
if(LookupBuffer(device, buffer) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
@@ -757,6 +787,7 @@ AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *valu
}
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -777,6 +808,7 @@ AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *valu
if(!context) return;
device = context->Device;
LockBuffersRead(device);
if(LookupBuffer(device, buffer) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
@@ -789,6 +821,7 @@ AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *valu
}
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -803,6 +836,7 @@ AL_API ALvoid AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value
if(!context) return;
device = context->Device;
LockBuffersRead(device);
if((albuf=LookupBuffer(device, buffer)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
@@ -842,11 +876,11 @@ AL_API ALvoid AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value
break;
case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
*value = ATOMIC_LOAD(&albuf->UnpackAlign);
*value = ATOMIC_LOAD_SEQ(&albuf->UnpackAlign);
break;
case AL_PACK_BLOCK_ALIGNMENT_SOFT:
*value = ATOMIC_LOAD(&albuf->PackAlign);
*value = ATOMIC_LOAD_SEQ(&albuf->PackAlign);
break;
default:
@@ -854,6 +888,7 @@ AL_API ALvoid AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value
}
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -867,6 +902,7 @@ AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1
if(!context) return;
device = context->Device;
LockBuffersRead(device);
if(LookupBuffer(device, buffer) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
@@ -879,6 +915,7 @@ AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1
}
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -908,6 +945,7 @@ AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values
if(!context) return;
device = context->Device;
LockBuffersRead(device);
if((albuf=LookupBuffer(device, buffer)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
@@ -927,6 +965,7 @@ AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values
}
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -940,14 +979,14 @@ done:
*/
ALenum LoadData(ALbuffer *ALBuf, ALuint freq, ALenum NewFormat, ALsizei frames, enum UserFmtChannels SrcChannels, enum UserFmtType SrcType, const ALvoid *data, ALsizei align, ALboolean storesrc)
{
enum FmtChannels DstChannels = FmtMono;
enum FmtType DstType = FmtByte;
ALuint NewChannels, NewBytes;
enum FmtChannels DstChannels;
enum FmtType DstType;
ALuint64 newsize;
ALvoid *temp;
if(DecomposeFormat(NewFormat, &DstChannels, &DstType) == AL_FALSE ||
(long)SrcChannels != (long)DstChannels)
if(DecomposeFormat(NewFormat, &DstChannels, &DstType) == AL_FALSE)
return AL_INVALID_ENUM;
if((long)SrcChannels != (long)DstChannels)
return AL_INVALID_ENUM;
NewChannels = ChannelsFromFmt(DstChannels);
@@ -966,13 +1005,25 @@ ALenum LoadData(ALbuffer *ALBuf, ALuint freq, ALenum NewFormat, ALsizei frames,
return AL_INVALID_OPERATION;
}
temp = realloc(ALBuf->data, (size_t)newsize);
if(!temp && newsize)
/* Round up to the next 16-byte multiple. This could reallocate only when
* increasing or the new size is less than half the current, but then the
* buffer's AL_SIZE would not be very reliable for accounting buffer memory
* usage, and reporting the real size could cause problems for apps that
* use AL_SIZE to try to get the buffer's play length.
*/
newsize = (newsize+15) & ~0xf;
if(newsize != ALBuf->BytesAlloc)
{
WriteUnlock(&ALBuf->lock);
return AL_OUT_OF_MEMORY;
void *temp = al_calloc(16, (size_t)newsize);
if(!temp && newsize)
{
WriteUnlock(&ALBuf->lock);
return AL_OUT_OF_MEMORY;
}
al_free(ALBuf->data);
ALBuf->data = temp;
ALBuf->BytesAlloc = (ALuint)newsize;
}
ALBuf->data = temp;
if(data != NULL)
ConvertData(ALBuf->data, (enum UserFmtType)DstType, data, SrcType, NewChannels, frames, align);
@@ -1021,7 +1072,7 @@ ALenum LoadData(ALbuffer *ALBuf, ALuint freq, ALenum NewFormat, ALsizei frames,
}
ALuint BytesFromUserFmt(enum UserFmtType type)
ALsizei BytesFromUserFmt(enum UserFmtType type)
{
switch(type)
{
@@ -1033,8 +1084,6 @@ ALuint BytesFromUserFmt(enum UserFmtType type)
case UserFmtUInt: return sizeof(ALuint);
case UserFmtFloat: return sizeof(ALfloat);
case UserFmtDouble: return sizeof(ALdouble);
case UserFmtByte3: return sizeof(ALbyte[3]);
case UserFmtUByte3: return sizeof(ALubyte[3]);
case UserFmtMulaw: return sizeof(ALubyte);
case UserFmtAlaw: return sizeof(ALubyte);
case UserFmtIMA4: break; /* not handled here */
@@ -1042,7 +1091,7 @@ ALuint BytesFromUserFmt(enum UserFmtType type)
}
return 0;
}
ALuint ChannelsFromUserFmt(enum UserFmtChannels chans)
ALsizei ChannelsFromUserFmt(enum UserFmtChannels chans)
{
switch(chans)
{
@@ -1137,7 +1186,7 @@ static ALboolean DecomposeUserFormat(ALenum format, enum UserFmtChannels *chans,
return AL_FALSE;
}
ALuint BytesFromFmt(enum FmtType type)
ALsizei BytesFromFmt(enum FmtType type)
{
switch(type)
{
@@ -1147,7 +1196,7 @@ ALuint BytesFromFmt(enum FmtType type)
}
return 0;
}
ALuint ChannelsFromFmt(enum FmtChannels chans)
ALsizei ChannelsFromFmt(enum FmtChannels chans)
{
switch(chans)
{
@@ -1201,13 +1250,13 @@ static ALboolean DecomposeFormat(ALenum format, enum FmtChannels *chans, enum Fm
{ AL_7POINT1_16_SOFT, FmtX71, FmtShort },
{ AL_7POINT1_32F_SOFT, FmtX71, FmtFloat },
{ AL_FORMAT_BFORMAT2D_8, FmtBFormat2D, FmtByte },
{ AL_FORMAT_BFORMAT2D_16, FmtBFormat2D, FmtShort },
{ AL_FORMAT_BFORMAT2D_FLOAT32, FmtBFormat2D, FmtFloat },
{ AL_BFORMAT2D_8_SOFT, FmtBFormat2D, FmtByte },
{ AL_BFORMAT2D_16_SOFT, FmtBFormat2D, FmtShort },
{ AL_BFORMAT2D_32F_SOFT, FmtBFormat2D, FmtFloat },
{ AL_FORMAT_BFORMAT3D_8, FmtBFormat3D, FmtByte },
{ AL_FORMAT_BFORMAT3D_16, FmtBFormat3D, FmtShort },
{ AL_FORMAT_BFORMAT3D_FLOAT32, FmtBFormat3D, FmtFloat },
{ AL_BFORMAT3D_8_SOFT, FmtBFormat3D, FmtByte },
{ AL_BFORMAT3D_16_SOFT, FmtBFormat3D, FmtShort },
{ AL_BFORMAT3D_32F_SOFT, FmtBFormat3D, FmtFloat },
};
ALuint i;
@@ -1275,8 +1324,7 @@ static ALboolean IsValidType(ALenum type)
case AL_UNSIGNED_INT_SOFT:
case AL_FLOAT_SOFT:
case AL_DOUBLE_SOFT:
case AL_BYTE3_SOFT:
case AL_UNSIGNED_BYTE3_SOFT:
case AL_MULAW_SOFT:
return AL_TRUE;
}
return AL_FALSE;
@@ -1293,6 +1341,8 @@ static ALboolean IsValidChannels(ALenum channels)
case AL_5POINT1_SOFT:
case AL_6POINT1_SOFT:
case AL_7POINT1_SOFT:
case AL_BFORMAT2D_SOFT:
case AL_BFORMAT3D_SOFT:
return AL_TRUE;
}
return AL_FALSE;
@@ -1305,7 +1355,7 @@ ALbuffer *NewBuffer(ALCcontext *context)
ALbuffer *buffer;
ALenum err;
buffer = calloc(1, sizeof(ALbuffer));
buffer = al_calloc(16, sizeof(ALbuffer));
if(!buffer)
SET_ERROR_AND_RETURN_VALUE(context, AL_OUT_OF_MEMORY, NULL);
RWLockInit(&buffer->lock);
@@ -1317,7 +1367,7 @@ ALbuffer *NewBuffer(ALCcontext *context)
{
FreeThunkEntry(buffer->id);
memset(buffer, 0, sizeof(ALbuffer));
free(buffer);
al_free(buffer);
SET_ERROR_AND_RETURN_VALUE(context, err, NULL);
}
@@ -1330,10 +1380,10 @@ void DeleteBuffer(ALCdevice *device, ALbuffer *buffer)
RemoveBuffer(device, buffer->id);
FreeThunkEntry(buffer->id);
free(buffer->data);
al_free(buffer->data);
memset(buffer, 0, sizeof(*buffer));
free(buffer);
al_free(buffer);
}
@@ -1347,13 +1397,13 @@ ALvoid ReleaseALBuffers(ALCdevice *device)
ALsizei i;
for(i = 0;i < device->BufferMap.size;i++)
{
ALbuffer *temp = device->BufferMap.array[i].value;
device->BufferMap.array[i].value = NULL;
ALbuffer *temp = device->BufferMap.values[i];
device->BufferMap.values[i] = NULL;
free(temp->data);
al_free(temp->data);
FreeThunkEntry(temp->id);
memset(temp, 0, sizeof(ALbuffer));
free(temp);
al_free(temp);
}
}
+34 -18
View File
@@ -34,6 +34,10 @@
ALboolean DisabledEffects[MAX_EFFECTS];
extern inline void LockEffectsRead(ALCdevice *device);
extern inline void UnlockEffectsRead(ALCdevice *device);
extern inline void LockEffectsWrite(ALCdevice *device);
extern inline void UnlockEffectsWrite(ALCdevice *device);
extern inline struct ALeffect *LookupEffect(ALCdevice *device, ALuint id);
extern inline struct ALeffect *RemoveEffect(ALCdevice *device, ALuint id);
extern inline ALboolean IsReverbEffect(ALenum type);
@@ -56,11 +60,11 @@ AL_API ALvoid AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects)
device = context->Device;
for(cur = 0;cur < n;cur++)
{
ALeffect *effect = calloc(1, sizeof(ALeffect));
ALeffect *effect = al_calloc(16, sizeof(ALeffect));
ALenum err = AL_OUT_OF_MEMORY;
if(!effect || (err=InitEffect(effect)) != AL_NO_ERROR)
{
free(effect);
al_free(effect);
alDeleteEffects(cur, effects);
SET_ERROR_AND_GOTO(context, err, done);
}
@@ -72,7 +76,7 @@ AL_API ALvoid AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects)
{
FreeThunkEntry(effect->id);
memset(effect, 0, sizeof(ALeffect));
free(effect);
al_free(effect);
alDeleteEffects(cur, effects);
SET_ERROR_AND_GOTO(context, err, done);
@@ -95,10 +99,10 @@ AL_API ALvoid AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects)
context = GetContextRef();
if(!context) return;
device = context->Device;
LockEffectsWrite(device);
if(!(n >= 0))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
device = context->Device;
for(i = 0;i < n;i++)
{
if(effects[i] && LookupEffect(device, effects[i]) == NULL)
@@ -111,10 +115,11 @@ AL_API ALvoid AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects)
FreeThunkEntry(effect->id);
memset(effect, 0, sizeof(*effect));
free(effect);
al_free(effect);
}
done:
UnlockEffectsWrite(device);
ALCcontext_DecRef(context);
}
@@ -126,8 +131,10 @@ AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect)
Context = GetContextRef();
if(!Context) return AL_FALSE;
LockEffectsRead(Context->Device);
result = ((!effect || LookupEffect(Context->Device, effect)) ?
AL_TRUE : AL_FALSE);
UnlockEffectsRead(Context->Device);
ALCcontext_DecRef(Context);
@@ -144,6 +151,7 @@ AL_API ALvoid AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint value)
if(!Context) return;
Device = Context->Device;
LockEffectsWrite(Device);
if((ALEffect=LookupEffect(Device, effect)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
@@ -170,6 +178,7 @@ AL_API ALvoid AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint value)
V(ALEffect,setParami)(Context, param, value);
}
}
UnlockEffectsWrite(Device);
ALCcontext_DecRef(Context);
}
@@ -191,6 +200,7 @@ AL_API ALvoid AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *v
if(!Context) return;
Device = Context->Device;
LockEffectsWrite(Device);
if((ALEffect=LookupEffect(Device, effect)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
@@ -198,6 +208,7 @@ AL_API ALvoid AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *v
/* Call the appropriate handler */
V(ALEffect,setParamiv)(Context, param, values);
}
UnlockEffectsWrite(Device);
ALCcontext_DecRef(Context);
}
@@ -212,6 +223,7 @@ AL_API ALvoid AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat value)
if(!Context) return;
Device = Context->Device;
LockEffectsWrite(Device);
if((ALEffect=LookupEffect(Device, effect)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
@@ -219,6 +231,7 @@ AL_API ALvoid AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat value)
/* Call the appropriate handler */
V(ALEffect,setParamf)(Context, param, value);
}
UnlockEffectsWrite(Device);
ALCcontext_DecRef(Context);
}
@@ -233,6 +246,7 @@ AL_API ALvoid AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat
if(!Context) return;
Device = Context->Device;
LockEffectsWrite(Device);
if((ALEffect=LookupEffect(Device, effect)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
@@ -240,6 +254,7 @@ AL_API ALvoid AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat
/* Call the appropriate handler */
V(ALEffect,setParamfv)(Context, param, values);
}
UnlockEffectsWrite(Device);
ALCcontext_DecRef(Context);
}
@@ -254,6 +269,7 @@ AL_API ALvoid AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *value
if(!Context) return;
Device = Context->Device;
LockEffectsRead(Device);
if((ALEffect=LookupEffect(Device, effect)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
@@ -266,6 +282,7 @@ AL_API ALvoid AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *value
V(ALEffect,getParami)(Context, param, value);
}
}
UnlockEffectsRead(Device);
ALCcontext_DecRef(Context);
}
@@ -287,6 +304,7 @@ AL_API ALvoid AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *valu
if(!Context) return;
Device = Context->Device;
LockEffectsRead(Device);
if((ALEffect=LookupEffect(Device, effect)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
@@ -294,6 +312,7 @@ AL_API ALvoid AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *valu
/* Call the appropriate handler */
V(ALEffect,getParamiv)(Context, param, values);
}
UnlockEffectsRead(Device);
ALCcontext_DecRef(Context);
}
@@ -308,6 +327,7 @@ AL_API ALvoid AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *val
if(!Context) return;
Device = Context->Device;
LockEffectsRead(Device);
if((ALEffect=LookupEffect(Device, effect)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
@@ -315,6 +335,7 @@ AL_API ALvoid AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *val
/* Call the appropriate handler */
V(ALEffect,getParamf)(Context, param, value);
}
UnlockEffectsRead(Device);
ALCcontext_DecRef(Context);
}
@@ -329,6 +350,7 @@ AL_API ALvoid AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *va
if(!Context) return;
Device = Context->Device;
LockEffectsRead(Device);
if((ALEffect=LookupEffect(Device, effect)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
@@ -336,6 +358,7 @@ AL_API ALvoid AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *va
/* Call the appropriate handler */
V(ALEffect,getParamfv)(Context, param, values);
}
UnlockEffectsRead(Device);
ALCcontext_DecRef(Context);
}
@@ -352,13 +375,13 @@ ALvoid ReleaseALEffects(ALCdevice *device)
ALsizei i;
for(i = 0;i < device->EffectMap.size;i++)
{
ALeffect *temp = device->EffectMap.array[i].value;
device->EffectMap.array[i].value = NULL;
ALeffect *temp = device->EffectMap.values[i];
device->EffectMap.values[i] = NULL;
// Release effect structure
FreeThunkEntry(temp->id);
memset(temp, 0, sizeof(ALeffect));
free(temp);
al_free(temp);
}
}
@@ -427,13 +450,6 @@ static void InitEffectParams(ALeffect *effect, ALenum type)
effect->Props.Reverb.DecayHFLimit = AL_REVERB_DEFAULT_DECAY_HFLIMIT;
SET_VTABLE1(ALreverb, effect);
break;
case AL_EFFECT_AUTOWAH:
effect->Props.Autowah.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME;
effect->Props.Autowah.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN;
effect->Props.Autowah.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME;
effect->Props.Autowah.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE;
SET_VTABLE1(ALautowah, effect);
break;
case AL_EFFECT_CHORUS:
effect->Props.Chorus.Waveform = AL_CHORUS_DEFAULT_WAVEFORM;
effect->Props.Chorus.Phase = AL_CHORUS_DEFAULT_PHASE;
@@ -651,9 +667,9 @@ ALvoid LoadReverbPreset(const char *name, ALeffect *effect)
return;
}
if(!DisabledEffects[EAXREVERB])
if(!DisabledEffects[AL__EAXREVERB])
InitEffectParams(effect, AL_EFFECT_EAXREVERB);
else if(!DisabledEffects[REVERB])
else if(!DisabledEffects[AL__REVERB])
InitEffectParams(effect, AL_EFFECT_REVERB);
else
InitEffectParams(effect, AL_EFFECT_NULL);
+7 -2
View File
@@ -36,6 +36,8 @@ ALboolean TrapALError = AL_FALSE;
ALvoid alSetError(ALCcontext *Context, ALenum errorCode)
{
ALenum curerr = AL_NO_ERROR;
WARN("Error generated on context %p, code 0x%04x\n", Context, errorCode);
if(TrapALError)
{
#ifdef _WIN32
@@ -46,7 +48,8 @@ ALvoid alSetError(ALCcontext *Context, ALenum errorCode)
raise(SIGTRAP);
#endif
}
ATOMIC_COMPARE_EXCHANGE_STRONG(ALenum, &Context->LastError, &curerr, errorCode);
(void)(ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(&Context->LastError, &curerr, errorCode));
}
AL_API ALenum AL_APIENTRY alGetError(void)
@@ -57,6 +60,8 @@ AL_API ALenum AL_APIENTRY alGetError(void)
Context = GetContextRef();
if(!Context)
{
WARN("Querying error state on null context (implicitly 0x%04x)\n",
AL_INVALID_OPERATION);
if(TrapALError)
{
#ifdef _WIN32
@@ -69,7 +74,7 @@ AL_API ALenum AL_APIENTRY alGetError(void)
return AL_INVALID_OPERATION;
}
errorCode = ATOMIC_EXCHANGE(ALenum, &Context->LastError, AL_NO_ERROR);
errorCode = ATOMIC_EXCHANGE_SEQ(&Context->LastError, AL_NO_ERROR);
ALCcontext_DecRef(Context);
+11 -14
View File
@@ -36,20 +36,17 @@
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 },
{ "eaxreverb", AL__EAXREVERB, "AL_EFFECT_EAXREVERB", AL_EFFECT_EAXREVERB },
{ "reverb", AL__REVERB, "AL_EFFECT_REVERB", AL_EFFECT_REVERB },
{ "chorus", AL__CHORUS, "AL_EFFECT_CHORUS", AL_EFFECT_CHORUS },
{ "compressor", AL__COMPRESSOR, "AL_EFFECT_COMPRESSOR", AL_EFFECT_COMPRESSOR },
{ "distortion", AL__DISTORTION, "AL_EFFECT_DISTORTION", AL_EFFECT_DISTORTION },
{ "echo", AL__ECHO, "AL_EFFECT_ECHO", AL_EFFECT_ECHO },
{ "equalizer", AL__EQUALIZER, "AL_EFFECT_EQUALIZER", AL_EFFECT_EQUALIZER },
{ "flanger", AL__FLANGER, "AL_EFFECT_FLANGER", AL_EFFECT_FLANGER },
{ "modulator", AL__MODULATOR, "AL_EFFECT_RING_MODULATOR", AL_EFFECT_RING_MODULATOR },
{ "dedicated", AL__DEDICATED, "AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT", AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT },
{ "dedicated", AL__DEDICATED, "AL_EFFECT_DEDICATED_DIALOGUE", AL_EFFECT_DEDICATED_DIALOGUE },
{ NULL, 0, NULL, (ALenum)0 }
};
+35 -14
View File
@@ -29,11 +29,15 @@
#include "alError.h"
extern inline void LockFiltersRead(ALCdevice *device);
extern inline void UnlockFiltersRead(ALCdevice *device);
extern inline void LockFiltersWrite(ALCdevice *device);
extern inline void UnlockFiltersWrite(ALCdevice *device);
extern inline struct ALfilter *LookupFilter(ALCdevice *device, ALuint id);
extern inline struct ALfilter *RemoveFilter(ALCdevice *device, ALuint id);
extern inline void ALfilterState_clear(ALfilterState *filter);
extern inline void ALfilterState_processPassthru(ALfilterState *filter, const ALfloat *src, ALuint numsamples);
extern inline ALfloat ALfilterState_processSingle(ALfilterState *filter, ALfloat sample);
extern inline void ALfilterState_copyParams(ALfilterState *restrict dst, const ALfilterState *restrict src);
extern inline void ALfilterState_processPassthru(ALfilterState *filter, const ALfloat *restrict src, ALsizei numsamples);
extern inline ALfloat calc_rcpQ_from_slope(ALfloat gain, ALfloat slope);
extern inline ALfloat calc_rcpQ_from_bandwidth(ALfloat freq_mult, ALfloat bandwidth);
@@ -56,7 +60,7 @@ AL_API ALvoid AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters)
device = context->Device;
for(cur = 0;cur < n;cur++)
{
ALfilter *filter = calloc(1, sizeof(ALfilter));
ALfilter *filter = al_calloc(16, sizeof(ALfilter));
if(!filter)
{
alDeleteFilters(cur, filters);
@@ -71,7 +75,7 @@ AL_API ALvoid AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters)
{
FreeThunkEntry(filter->id);
memset(filter, 0, sizeof(ALfilter));
free(filter);
al_free(filter);
alDeleteFilters(cur, filters);
SET_ERROR_AND_GOTO(context, err, done);
@@ -94,10 +98,10 @@ AL_API ALvoid AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters)
context = GetContextRef();
if(!context) return;
device = context->Device;
LockFiltersWrite(device);
if(!(n >= 0))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
device = context->Device;
for(i = 0;i < n;i++)
{
if(filters[i] && LookupFilter(device, filters[i]) == NULL)
@@ -110,10 +114,11 @@ AL_API ALvoid AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters)
FreeThunkEntry(filter->id);
memset(filter, 0, sizeof(*filter));
free(filter);
al_free(filter);
}
done:
UnlockFiltersWrite(device);
ALCcontext_DecRef(context);
}
@@ -125,8 +130,10 @@ AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter)
Context = GetContextRef();
if(!Context) return AL_FALSE;
LockFiltersRead(Context->Device);
result = ((!filter || LookupFilter(Context->Device, filter)) ?
AL_TRUE : AL_FALSE);
UnlockFiltersRead(Context->Device);
ALCcontext_DecRef(Context);
@@ -143,6 +150,7 @@ AL_API ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint value)
if(!Context) return;
Device = Context->Device;
LockFiltersWrite(Device);
if((ALFilter=LookupFilter(Device, filter)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
@@ -161,6 +169,7 @@ AL_API ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint value)
ALfilter_SetParami(ALFilter, Context, param, value);
}
}
UnlockFiltersWrite(Device);
ALCcontext_DecRef(Context);
}
@@ -182,6 +191,7 @@ AL_API ALvoid AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *v
if(!Context) return;
Device = Context->Device;
LockFiltersWrite(Device);
if((ALFilter=LookupFilter(Device, filter)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
@@ -189,6 +199,7 @@ AL_API ALvoid AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *v
/* Call the appropriate handler */
ALfilter_SetParamiv(ALFilter, Context, param, values);
}
UnlockFiltersWrite(Device);
ALCcontext_DecRef(Context);
}
@@ -203,6 +214,7 @@ AL_API ALvoid AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat value)
if(!Context) return;
Device = Context->Device;
LockFiltersWrite(Device);
if((ALFilter=LookupFilter(Device, filter)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
@@ -210,6 +222,7 @@ AL_API ALvoid AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat value)
/* Call the appropriate handler */
ALfilter_SetParamf(ALFilter, Context, param, value);
}
UnlockFiltersWrite(Device);
ALCcontext_DecRef(Context);
}
@@ -224,6 +237,7 @@ AL_API ALvoid AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat
if(!Context) return;
Device = Context->Device;
LockFiltersWrite(Device);
if((ALFilter=LookupFilter(Device, filter)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
@@ -231,6 +245,7 @@ AL_API ALvoid AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat
/* Call the appropriate handler */
ALfilter_SetParamfv(ALFilter, Context, param, values);
}
UnlockFiltersWrite(Device);
ALCcontext_DecRef(Context);
}
@@ -245,6 +260,7 @@ AL_API ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *value
if(!Context) return;
Device = Context->Device;
LockFiltersRead(Device);
if((ALFilter=LookupFilter(Device, filter)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
@@ -257,6 +273,7 @@ AL_API ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *value
ALfilter_GetParami(ALFilter, Context, param, value);
}
}
UnlockFiltersRead(Device);
ALCcontext_DecRef(Context);
}
@@ -278,6 +295,7 @@ AL_API ALvoid AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *valu
if(!Context) return;
Device = Context->Device;
LockFiltersRead(Device);
if((ALFilter=LookupFilter(Device, filter)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
@@ -285,6 +303,7 @@ AL_API ALvoid AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *valu
/* Call the appropriate handler */
ALfilter_GetParamiv(ALFilter, Context, param, values);
}
UnlockFiltersRead(Device);
ALCcontext_DecRef(Context);
}
@@ -299,6 +318,7 @@ AL_API ALvoid AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *val
if(!Context) return;
Device = Context->Device;
LockFiltersRead(Device);
if((ALFilter=LookupFilter(Device, filter)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
@@ -306,6 +326,7 @@ AL_API ALvoid AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *val
/* Call the appropriate handler */
ALfilter_GetParamf(ALFilter, Context, param, value);
}
UnlockFiltersRead(Device);
ALCcontext_DecRef(Context);
}
@@ -320,6 +341,7 @@ AL_API ALvoid AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *va
if(!Context) return;
Device = Context->Device;
LockFiltersRead(Device);
if((ALFilter=LookupFilter(Device, filter)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
@@ -327,6 +349,7 @@ AL_API ALvoid AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *va
/* Call the appropriate handler */
ALfilter_GetParamfv(ALFilter, Context, param, values);
}
UnlockFiltersRead(Device);
ALCcontext_DecRef(Context);
}
@@ -340,7 +363,7 @@ void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat g
ALfloat b[3] = { 1.0f, 0.0f, 0.0f };
// Limit gain to -100dB
gain = maxf(gain, 0.00001f);
assert(gain > 0.00001f);
w0 = F_TAU * freq_mult;
sin_w0 = sinf(w0);
@@ -406,11 +429,9 @@ void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat g
filter->a1 = a[1] / a[0];
filter->a2 = a[2] / a[0];
filter->b0 = b[0] / a[0];
filter->b1 = b[1] / a[0];
filter->b2 = b[2] / a[0];
filter->input_gain = b[0] / a[0];
filter->process = ALfilterState_processC;
}
@@ -613,13 +634,13 @@ ALvoid ReleaseALFilters(ALCdevice *device)
ALsizei i;
for(i = 0;i < device->FilterMap.size;i++)
{
ALfilter *temp = device->FilterMap.array[i].value;
device->FilterMap.array[i].value = NULL;
ALfilter *temp = device->FilterMap.values[i];
device->FilterMap.values[i] = NULL;
// Release filter structure
FreeThunkEntry(temp->id);
memset(temp, 0, sizeof(ALfilter));
free(temp);
al_free(temp);
}
}
+110 -42
View File
@@ -33,29 +33,29 @@ AL_API ALvoid AL_APIENTRY alListenerf(ALenum param, ALfloat value)
context = GetContextRef();
if(!context) return;
WriteLock(&context->PropLock);
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);
}
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
UpdateListenerProps(context);
done:
WriteUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
@@ -67,33 +67,33 @@ AL_API ALvoid AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat val
context = GetContextRef();
if(!context) return;
WriteLock(&context->PropLock);
switch(param)
{
case AL_POSITION:
if(!(isfinite(value1) && isfinite(value2) && isfinite(value3)))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
LockContext(context);
aluVectorSet(&context->Listener->Position, value1, value2, value3, 1.0f);
ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
UnlockContext(context);
context->Listener->Position[0] = value1;
context->Listener->Position[1] = value2;
context->Listener->Position[2] = value3;
break;
case AL_VELOCITY:
if(!(isfinite(value1) && isfinite(value2) && isfinite(value3)))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
LockContext(context);
aluVectorSet(&context->Listener->Velocity, value1, value2, value3, 0.0f);
ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
UnlockContext(context);
context->Listener->Velocity[0] = value1;
context->Listener->Velocity[1] = value2;
context->Listener->Velocity[2] = value3;
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
UpdateListenerProps(context);
done:
WriteUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
@@ -121,6 +121,7 @@ AL_API ALvoid AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values)
context = GetContextRef();
if(!context) return;
WriteLock(&context->PropLock);
if(!(values))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
switch(param)
@@ -129,8 +130,6 @@ AL_API ALvoid AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values)
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];
@@ -138,15 +137,16 @@ AL_API ALvoid AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values)
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);
}
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
UpdateListenerProps(context);
done:
WriteUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
@@ -158,13 +158,17 @@ AL_API ALvoid AL_APIENTRY alListeneri(ALenum param, ALint UNUSED(value))
context = GetContextRef();
if(!context) return;
WriteLock(&context->PropLock);
switch(param)
{
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
UpdateListenerProps(context);
done:
WriteUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
@@ -184,13 +188,17 @@ AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, A
context = GetContextRef();
if(!context) return;
WriteLock(&context->PropLock);
switch(param)
{
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
UpdateListenerProps(context);
done:
WriteUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
@@ -224,6 +232,7 @@ AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values)
context = GetContextRef();
if(!context) return;
WriteLock(&context->PropLock);
if(!(values))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
switch(param)
@@ -231,8 +240,11 @@ AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values)
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
UpdateListenerProps(context);
done:
WriteUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
@@ -244,6 +256,7 @@ AL_API ALvoid AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value)
context = GetContextRef();
if(!context) return;
ReadLock(&context->PropLock);
if(!(value))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
switch(param)
@@ -261,6 +274,7 @@ AL_API ALvoid AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value)
}
done:
ReadUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
@@ -272,24 +286,21 @@ AL_API ALvoid AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat
context = GetContextRef();
if(!context) return;
ReadLock(&context->PropLock);
if(!(value1 && value2 && value3))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
switch(param)
{
case AL_POSITION:
LockContext(context);
*value1 = context->Listener->Position.v[0];
*value2 = context->Listener->Position.v[1];
*value3 = context->Listener->Position.v[2];
UnlockContext(context);
*value1 = context->Listener->Position[0];
*value2 = context->Listener->Position[1];
*value3 = context->Listener->Position[2];
break;
case AL_VELOCITY:
LockContext(context);
*value1 = context->Listener->Velocity.v[0];
*value2 = context->Listener->Velocity.v[1];
*value3 = context->Listener->Velocity.v[2];
UnlockContext(context);
*value1 = context->Listener->Velocity[0];
*value2 = context->Listener->Velocity[1];
*value3 = context->Listener->Velocity[2];
break;
default:
@@ -297,6 +308,7 @@ AL_API ALvoid AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat
}
done:
ReadUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
@@ -321,12 +333,12 @@ AL_API ALvoid AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values)
context = GetContextRef();
if(!context) return;
ReadLock(&context->PropLock);
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];
@@ -334,7 +346,6 @@ AL_API ALvoid AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values)
values[3] = context->Listener->Up[0];
values[4] = context->Listener->Up[1];
values[5] = context->Listener->Up[2];
UnlockContext(context);
break;
default:
@@ -342,6 +353,7 @@ AL_API ALvoid AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values)
}
done:
ReadUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
@@ -353,6 +365,7 @@ AL_API ALvoid AL_APIENTRY alGetListeneri(ALenum param, ALint *value)
context = GetContextRef();
if(!context) return;
ReadLock(&context->PropLock);
if(!(value))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
switch(param)
@@ -362,6 +375,7 @@ AL_API ALvoid AL_APIENTRY alGetListeneri(ALenum param, ALint *value)
}
done:
ReadUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
@@ -373,24 +387,21 @@ AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *valu
context = GetContextRef();
if(!context) return;
ReadLock(&context->PropLock);
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.v[0];
*value2 = (ALint)context->Listener->Position.v[1];
*value3 = (ALint)context->Listener->Position.v[2];
UnlockContext(context);
*value1 = (ALint)context->Listener->Position[0];
*value2 = (ALint)context->Listener->Position[1];
*value3 = (ALint)context->Listener->Position[2];
break;
case AL_VELOCITY:
LockContext(context);
*value1 = (ALint)context->Listener->Velocity.v[0];
*value2 = (ALint)context->Listener->Velocity.v[1];
*value3 = (ALint)context->Listener->Velocity.v[2];
UnlockContext(context);
*value1 = (ALint)context->Listener->Velocity[0];
*value2 = (ALint)context->Listener->Velocity[1];
*value3 = (ALint)context->Listener->Velocity[2];
break;
default:
@@ -398,6 +409,7 @@ AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *valu
}
done:
ReadUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
@@ -417,12 +429,12 @@ AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint* values)
context = GetContextRef();
if(!context) return;
ReadLock(&context->PropLock);
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];
@@ -430,7 +442,6 @@ AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint* values)
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:
@@ -438,5 +449,62 @@ AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint* values)
}
done:
ReadUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
void UpdateListenerProps(ALCcontext *context)
{
ALlistener *listener = context->Listener;
struct ALlistenerProps *props;
/* Get an unused proprty container, or allocate a new one as needed. */
props = ATOMIC_LOAD(&listener->FreeList, almemory_order_acquire);
if(!props)
props = al_calloc(16, sizeof(*props));
else
{
struct ALlistenerProps *next;
do {
next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
} while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(&listener->FreeList, &props, next,
almemory_order_seq_cst, almemory_order_acquire) == 0);
}
/* Copy in current property values. */
props->Position[0] = listener->Position[0];
props->Position[1] = listener->Position[1];
props->Position[2] = listener->Position[2];
props->Velocity[0] = listener->Velocity[0];
props->Velocity[1] = listener->Velocity[1];
props->Velocity[2] = listener->Velocity[2];
props->Forward[0] = listener->Forward[0];
props->Forward[1] = listener->Forward[1];
props->Forward[2] = listener->Forward[2];
props->Up[0] = listener->Up[0];
props->Up[1] = listener->Up[1];
props->Up[2] = listener->Up[2];
props->Gain = listener->Gain;
props->MetersPerUnit = listener->MetersPerUnit;
props->DopplerFactor = context->DopplerFactor;
props->DopplerVelocity = context->DopplerVelocity;
props->SpeedOfSound = context->SpeedOfSound;
props->SourceDistanceModel = context->SourceDistanceModel;
props->DistanceModel = context->DistanceModel;;
/* Set the new container for updating internal parameters. */
props = ATOMIC_EXCHANGE_PTR(&listener->Update, props, almemory_order_acq_rel);
if(props)
{
/* If there was an unused update container, put it back in the
* freelist.
*/
ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &listener->FreeList, props);
}
}
+1132 -665
View File
File diff suppressed because it is too large Load Diff
+155 -11
View File
@@ -20,12 +20,15 @@
#include "config.h"
#include "version.h"
#include <stdlib.h>
#include "alMain.h"
#include "AL/alc.h"
#include "AL/al.h"
#include "AL/alext.h"
#include "alError.h"
#include "alListener.h"
#include "alSource.h"
#include "alAuxEffectSlot.h"
@@ -44,6 +47,12 @@ static const ALchar alErrInvalidValue[] = "Invalid Value";
static const ALchar alErrInvalidOp[] = "Invalid Operation";
static const ALchar alErrOutOfMemory[] = "Out of Memory";
/* Resampler strings */
static const ALchar alPointResampler[] = "Nearest";
static const ALchar alLinearResampler[] = "Linear";
static const ALchar alSinc4Resampler[] = "4-Point Sinc";
static const ALchar alBSincResampler[] = "Band-limited Sinc (12/24)";
AL_API ALvoid AL_APIENTRY alEnable(ALenum capability)
{
ALCcontext *context;
@@ -51,18 +60,21 @@ AL_API ALvoid AL_APIENTRY alEnable(ALenum capability)
context = GetContextRef();
if(!context) return;
WriteLock(&context->PropLock);
switch(capability)
{
case AL_SOURCE_DISTANCE_MODEL:
context->SourceDistanceModel = AL_TRUE;
ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
UpdateListenerProps(context);
done:
WriteUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
@@ -73,18 +85,21 @@ AL_API ALvoid AL_APIENTRY alDisable(ALenum capability)
context = GetContextRef();
if(!context) return;
WriteLock(&context->PropLock);
switch(capability)
{
case AL_SOURCE_DISTANCE_MODEL:
context->SourceDistanceModel = AL_FALSE;
ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
UpdateListenerProps(context);
done:
WriteUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
@@ -143,7 +158,22 @@ AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum pname)
break;
case AL_DEFERRED_UPDATES_SOFT:
value = context->DeferUpdates;
if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
value = AL_TRUE;
break;
case AL_GAIN_LIMIT_SOFT:
if(GAIN_MIX_MAX/context->GainBoost != 0.0f)
value = AL_TRUE;
break;
case AL_NUM_RESAMPLERS_SOFT:
/* Always non-0. */
value = AL_TRUE;
break;
case AL_DEFAULT_RESAMPLER_SOFT:
value = ResamplerDefault ? AL_TRUE : AL_FALSE;
break;
default:
@@ -183,7 +213,20 @@ AL_API ALdouble AL_APIENTRY alGetDouble(ALenum pname)
break;
case AL_DEFERRED_UPDATES_SOFT:
value = (ALdouble)context->DeferUpdates;
if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
value = (ALdouble)AL_TRUE;
break;
case AL_GAIN_LIMIT_SOFT:
value = (ALdouble)GAIN_MIX_MAX/context->GainBoost;
break;
case AL_NUM_RESAMPLERS_SOFT:
value = (ALdouble)(ResamplerMax + 1);
break;
case AL_DEFAULT_RESAMPLER_SOFT:
value = (ALdouble)ResamplerDefault;
break;
default:
@@ -223,7 +266,20 @@ AL_API ALfloat AL_APIENTRY alGetFloat(ALenum pname)
break;
case AL_DEFERRED_UPDATES_SOFT:
value = (ALfloat)context->DeferUpdates;
if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
value = (ALfloat)AL_TRUE;
break;
case AL_GAIN_LIMIT_SOFT:
value = GAIN_MIX_MAX/context->GainBoost;
break;
case AL_NUM_RESAMPLERS_SOFT:
value = (ALfloat)(ResamplerMax + 1);
break;
case AL_DEFAULT_RESAMPLER_SOFT:
value = (ALfloat)ResamplerDefault;
break;
default:
@@ -263,7 +319,20 @@ AL_API ALint AL_APIENTRY alGetInteger(ALenum pname)
break;
case AL_DEFERRED_UPDATES_SOFT:
value = (ALint)context->DeferUpdates;
if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
value = (ALint)AL_TRUE;
break;
case AL_GAIN_LIMIT_SOFT:
value = (ALint)(GAIN_MIX_MAX/context->GainBoost);
break;
case AL_NUM_RESAMPLERS_SOFT:
value = ResamplerMax + 1;
break;
case AL_DEFAULT_RESAMPLER_SOFT:
value = ResamplerDefault;
break;
default:
@@ -303,7 +372,20 @@ AL_API ALint64SOFT AL_APIENTRY alGetInteger64SOFT(ALenum pname)
break;
case AL_DEFERRED_UPDATES_SOFT:
value = (ALint64SOFT)context->DeferUpdates;
if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
value = (ALint64SOFT)AL_TRUE;
break;
case AL_GAIN_LIMIT_SOFT:
value = (ALint64SOFT)(GAIN_MIX_MAX/context->GainBoost);
break;
case AL_NUM_RESAMPLERS_SOFT:
value = (ALint64SOFT)(ResamplerMax + 1);
break;
case AL_DEFAULT_RESAMPLER_SOFT:
value = (ALint64SOFT)ResamplerDefault;
break;
default:
@@ -329,6 +411,9 @@ AL_API ALvoid AL_APIENTRY alGetBooleanv(ALenum pname, ALboolean *values)
case AL_DISTANCE_MODEL:
case AL_SPEED_OF_SOUND:
case AL_DEFERRED_UPDATES_SOFT:
case AL_GAIN_LIMIT_SOFT:
case AL_NUM_RESAMPLERS_SOFT:
case AL_DEFAULT_RESAMPLER_SOFT:
values[0] = alGetBoolean(pname);
return;
}
@@ -362,6 +447,9 @@ AL_API ALvoid AL_APIENTRY alGetDoublev(ALenum pname, ALdouble *values)
case AL_DISTANCE_MODEL:
case AL_SPEED_OF_SOUND:
case AL_DEFERRED_UPDATES_SOFT:
case AL_GAIN_LIMIT_SOFT:
case AL_NUM_RESAMPLERS_SOFT:
case AL_DEFAULT_RESAMPLER_SOFT:
values[0] = alGetDouble(pname);
return;
}
@@ -395,6 +483,9 @@ AL_API ALvoid AL_APIENTRY alGetFloatv(ALenum pname, ALfloat *values)
case AL_DISTANCE_MODEL:
case AL_SPEED_OF_SOUND:
case AL_DEFERRED_UPDATES_SOFT:
case AL_GAIN_LIMIT_SOFT:
case AL_NUM_RESAMPLERS_SOFT:
case AL_DEFAULT_RESAMPLER_SOFT:
values[0] = alGetFloat(pname);
return;
}
@@ -428,6 +519,9 @@ AL_API ALvoid AL_APIENTRY alGetIntegerv(ALenum pname, ALint *values)
case AL_DISTANCE_MODEL:
case AL_SPEED_OF_SOUND:
case AL_DEFERRED_UPDATES_SOFT:
case AL_GAIN_LIMIT_SOFT:
case AL_NUM_RESAMPLERS_SOFT:
case AL_DEFAULT_RESAMPLER_SOFT:
values[0] = alGetInteger(pname);
return;
}
@@ -459,6 +553,9 @@ AL_API void AL_APIENTRY alGetInteger64vSOFT(ALenum pname, ALint64SOFT *values)
case AL_DISTANCE_MODEL:
case AL_SPEED_OF_SOUND:
case AL_DEFERRED_UPDATES_SOFT:
case AL_GAIN_LIMIT_SOFT:
case AL_NUM_RESAMPLERS_SOFT:
case AL_DEFAULT_RESAMPLER_SOFT:
values[0] = alGetInteger64SOFT(pname);
return;
}
@@ -547,8 +644,11 @@ AL_API ALvoid AL_APIENTRY alDopplerFactor(ALfloat value)
if(!(value >= 0.0f && isfinite(value)))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
WriteLock(&context->PropLock);
context->DopplerFactor = value;
ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
UpdateListenerProps(context);
WriteUnlock(&context->PropLock);
done:
ALCcontext_DecRef(context);
@@ -564,8 +664,11 @@ AL_API ALvoid AL_APIENTRY alDopplerVelocity(ALfloat value)
if(!(value >= 0.0f && isfinite(value)))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
WriteLock(&context->PropLock);
context->DopplerVelocity = value;
ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
UpdateListenerProps(context);
WriteUnlock(&context->PropLock);
done:
ALCcontext_DecRef(context);
@@ -581,8 +684,11 @@ AL_API ALvoid AL_APIENTRY alSpeedOfSound(ALfloat value)
if(!(value > 0.0f && isfinite(value)))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
WriteLock(&context->PropLock);
context->SpeedOfSound = value;
ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
UpdateListenerProps(context);
WriteUnlock(&context->PropLock);
done:
ALCcontext_DecRef(context);
@@ -601,9 +707,14 @@ AL_API ALvoid AL_APIENTRY alDistanceModel(ALenum value)
value == AL_NONE))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
WriteLock(&context->PropLock);
context->DistanceModel = value;
if(!context->SourceDistanceModel)
ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
{
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
UpdateListenerProps(context);
}
WriteUnlock(&context->PropLock);
done:
ALCcontext_DecRef(context);
@@ -633,3 +744,36 @@ AL_API ALvoid AL_APIENTRY alProcessUpdatesSOFT(void)
ALCcontext_DecRef(context);
}
AL_API const ALchar* AL_APIENTRY alGetStringiSOFT(ALenum pname, ALsizei index)
{
const char *ResamplerNames[] = {
alPointResampler, alLinearResampler,
alSinc4Resampler, alBSincResampler,
};
const ALchar *value = NULL;
ALCcontext *context;
static_assert(COUNTOF(ResamplerNames) == ResamplerMax+1, "Incorrect ResamplerNames list");
context = GetContextRef();
if(!context) return NULL;
switch(pname)
{
case AL_RESAMPLER_NAME_SOFT:
if(index < 0 || (size_t)index >= COUNTOF(ResamplerNames))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
value = ResamplerNames[index];
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
return value;
}
+15 -10
View File
@@ -25,15 +25,17 @@
#include "alMain.h"
#include "alThunk.h"
#include "almalloc.h"
static ATOMIC(ALenum) *ThunkArray;
static ALuint ThunkArraySize;
static ATOMIC_FLAG *ThunkArray;
static ALsizei ThunkArraySize;
static RWLock ThunkLock;
void ThunkInit(void)
{
RWLockInit(&ThunkLock);
ThunkArraySize = 1;
ThunkArraySize = 1024;
ThunkArray = al_calloc(16, ThunkArraySize * sizeof(*ThunkArray));
}
@@ -47,12 +49,12 @@ void ThunkExit(void)
ALenum NewThunkEntry(ALuint *index)
{
void *NewList;
ALuint i;
ALsizei i;
ReadLock(&ThunkLock);
for(i = 0;i < ThunkArraySize;i++)
{
if(ATOMIC_EXCHANGE(ALenum, &ThunkArray[i], AL_TRUE) == AL_FALSE)
if(!ATOMIC_FLAG_TEST_AND_SET(&ThunkArray[i], almemory_order_acq_rel))
{
ReadUnlock(&ThunkLock);
*index = i+1;
@@ -67,7 +69,7 @@ ALenum NewThunkEntry(ALuint *index)
*/
for(;i < ThunkArraySize;i++)
{
if(ATOMIC_EXCHANGE(ALenum, &ThunkArray[i], AL_TRUE) == AL_FALSE)
if(!ATOMIC_FLAG_TEST_AND_SET(&ThunkArray[i], almemory_order_acq_rel))
{
WriteUnlock(&ThunkLock);
*index = i+1;
@@ -87,17 +89,20 @@ ALenum NewThunkEntry(ALuint *index)
ThunkArray = NewList;
ThunkArraySize *= 2;
ATOMIC_STORE(&ThunkArray[i], AL_TRUE);
ATOMIC_FLAG_TEST_AND_SET(&ThunkArray[i], almemory_order_seq_cst);
*index = ++i;
for(;i < ThunkArraySize;i++)
ATOMIC_FLAG_CLEAR(&ThunkArray[i], almemory_order_relaxed);
WriteUnlock(&ThunkLock);
*index = i+1;
return AL_NO_ERROR;
}
void FreeThunkEntry(ALuint index)
{
ReadLock(&ThunkLock);
if(index > 0 && index <= ThunkArraySize)
ATOMIC_STORE(&ThunkArray[index-1], AL_FALSE);
if(index > 0 && (ALsizei)index <= ThunkArraySize)
ATOMIC_FLAG_CLEAR(&ThunkArray[index-1], almemory_order_release);
ReadUnlock(&ThunkLock);
}
+115 -380
View File
@@ -174,14 +174,6 @@ typedef ALubyte ALmulaw;
typedef ALubyte ALalaw;
typedef ALubyte ALima4;
typedef ALubyte ALmsadpcm;
typedef struct {
ALbyte b[3];
} ALbyte3;
static_assert(sizeof(ALbyte3)==sizeof(ALbyte[3]), "ALbyte3 size is not 3");
typedef struct {
ALubyte b[3];
} ALubyte3;
static_assert(sizeof(ALubyte3)==sizeof(ALubyte[3]), "ALubyte3 size is not 3");
static inline ALshort DecodeMuLaw(ALmulaw val)
{ return muLawDecompressionTable[val]; }
@@ -498,320 +490,128 @@ static void EncodeMSADPCMBlock(ALmsadpcm *dst, const ALshort *src, ALint *sample
}
static inline ALint DecodeByte3(ALbyte3 val)
{
if(IS_LITTLE_ENDIAN)
return (val.b[2]<<16) | (((ALubyte)val.b[1])<<8) | ((ALubyte)val.b[0]);
return (val.b[0]<<16) | (((ALubyte)val.b[1])<<8) | ((ALubyte)val.b[2]);
}
/* Define same-type pass-through sample conversion functions (excludes ADPCM,
* which are block-based). */
#define DECL_TEMPLATE(T) \
static inline T Conv_##T##_##T(T val) { return val; }
static inline ALbyte3 EncodeByte3(ALint val)
{
if(IS_LITTLE_ENDIAN)
{
ALbyte3 ret = {{ val, val>>8, val>>16 }};
return ret;
}
else
{
ALbyte3 ret = {{ val>>16, val>>8, val }};
return ret;
}
}
DECL_TEMPLATE(ALbyte);
DECL_TEMPLATE(ALubyte);
DECL_TEMPLATE(ALshort);
DECL_TEMPLATE(ALushort);
DECL_TEMPLATE(ALint);
DECL_TEMPLATE(ALuint);
DECL_TEMPLATE(ALalaw);
DECL_TEMPLATE(ALmulaw);
static inline ALint DecodeUByte3(ALubyte3 val)
{
if(IS_LITTLE_ENDIAN)
return (val.b[2]<<16) | (val.b[1]<<8) | (val.b[0]);
return (val.b[0]<<16) | (val.b[1]<<8) | val.b[2];
}
static inline ALubyte3 EncodeUByte3(ALint val)
{
if(IS_LITTLE_ENDIAN)
{
ALubyte3 ret = {{ val, val>>8, val>>16 }};
return ret;
}
else
{
ALubyte3 ret = {{ val>>16, val>>8, val }};
return ret;
}
}
static inline ALbyte Conv_ALbyte_ALbyte(ALbyte val)
{ return val; }
static inline ALbyte Conv_ALbyte_ALubyte(ALubyte val)
{ return val-128; }
static inline ALbyte Conv_ALbyte_ALshort(ALshort val)
{ return val>>8; }
static inline ALbyte Conv_ALbyte_ALushort(ALushort val)
{ return (val>>8)-128; }
static inline ALbyte Conv_ALbyte_ALint(ALint val)
{ return val>>24; }
static inline ALbyte Conv_ALbyte_ALuint(ALuint val)
{ return (val>>24)-128; }
static inline ALbyte Conv_ALbyte_ALfloat(ALfloat val)
{
if(val > 1.0f) return 127;
if(val < -1.0f) return -128;
return (ALint)(val * 127.0f);
}
static inline ALbyte Conv_ALbyte_ALdouble(ALdouble val)
{
if(val > 1.0) return 127;
if(val < -1.0) return -128;
return (ALint)(val * 127.0);
}
static inline ALbyte Conv_ALbyte_ALmulaw(ALmulaw val)
{ return Conv_ALbyte_ALshort(DecodeMuLaw(val)); }
static inline ALbyte Conv_ALbyte_ALalaw(ALalaw val)
{ return Conv_ALbyte_ALshort(DecodeALaw(val)); }
static inline ALbyte Conv_ALbyte_ALbyte3(ALbyte3 val)
{ return DecodeByte3(val)>>16; }
static inline ALbyte Conv_ALbyte_ALubyte3(ALubyte3 val)
{ return (DecodeUByte3(val)>>16)-128; }
static inline ALubyte Conv_ALubyte_ALbyte(ALbyte val)
{ return val+128; }
static inline ALubyte Conv_ALubyte_ALubyte(ALubyte val)
{ return val; }
static inline ALubyte Conv_ALubyte_ALshort(ALshort val)
{ return (val>>8)+128; }
static inline ALubyte Conv_ALubyte_ALushort(ALushort val)
{ return val>>8; }
static inline ALubyte Conv_ALubyte_ALint(ALint val)
{ return (val>>24)+128; }
static inline ALubyte Conv_ALubyte_ALuint(ALuint val)
{ return val>>24; }
static inline ALubyte Conv_ALubyte_ALfloat(ALfloat val)
{
if(val > 1.0f) return 255;
if(val < -1.0f) return 0;
return (ALint)(val * 127.0f) + 128;
}
static inline ALubyte Conv_ALubyte_ALdouble(ALdouble val)
{
if(val > 1.0) return 255;
if(val < -1.0) return 0;
return (ALint)(val * 127.0) + 128;
}
static inline ALubyte Conv_ALubyte_ALmulaw(ALmulaw val)
{ return Conv_ALubyte_ALshort(DecodeMuLaw(val)); }
static inline ALubyte Conv_ALubyte_ALalaw(ALalaw val)
{ return Conv_ALubyte_ALshort(DecodeALaw(val)); }
static inline ALubyte Conv_ALubyte_ALbyte3(ALbyte3 val)
{ return (DecodeByte3(val)>>16)+128; }
static inline ALubyte Conv_ALubyte_ALubyte3(ALubyte3 val)
{ return DecodeUByte3(val)>>16; }
static inline ALshort Conv_ALshort_ALbyte(ALbyte val)
{ return val<<8; }
static inline ALshort Conv_ALshort_ALubyte(ALubyte val)
{ return (val-128)<<8; }
static inline ALshort Conv_ALshort_ALshort(ALshort val)
{ return val; }
static inline ALshort Conv_ALshort_ALushort(ALushort val)
{ return val-32768; }
static inline ALshort Conv_ALshort_ALint(ALint val)
{ return val>>16; }
static inline ALshort Conv_ALshort_ALuint(ALuint val)
{ return (val>>16)-32768; }
static inline ALshort Conv_ALshort_ALfloat(ALfloat val)
{
if(val > 1.0f) return 32767;
if(val < -1.0f) return -32768;
return (ALint)(val * 32767.0f);
}
static inline ALshort Conv_ALshort_ALdouble(ALdouble val)
{
if(val > 1.0) return 32767;
if(val < -1.0) return -32768;
return (ALint)(val * 32767.0);
}
static inline ALshort Conv_ALshort_ALmulaw(ALmulaw val)
{ return Conv_ALshort_ALshort(DecodeMuLaw(val)); }
static inline ALshort Conv_ALshort_ALalaw(ALalaw val)
{ return Conv_ALshort_ALshort(DecodeALaw(val)); }
static inline ALshort Conv_ALshort_ALbyte3(ALbyte3 val)
{ return DecodeByte3(val)>>8; }
static inline ALshort Conv_ALshort_ALubyte3(ALubyte3 val)
{ return (DecodeUByte3(val)>>8)-32768; }
static inline ALushort Conv_ALushort_ALbyte(ALbyte val)
{ return (val+128)<<8; }
static inline ALushort Conv_ALushort_ALubyte(ALubyte val)
{ return val<<8; }
static inline ALushort Conv_ALushort_ALshort(ALshort val)
{ return val+32768; }
static inline ALushort Conv_ALushort_ALushort(ALushort val)
{ return val; }
static inline ALushort Conv_ALushort_ALint(ALint val)
{ return (val>>16)+32768; }
static inline ALushort Conv_ALushort_ALuint(ALuint val)
{ return val>>16; }
static inline ALushort Conv_ALushort_ALfloat(ALfloat val)
{
if(val > 1.0f) return 65535;
if(val < -1.0f) return 0;
return (ALint)(val * 32767.0f) + 32768;
}
static inline ALushort Conv_ALushort_ALdouble(ALdouble val)
{
if(val > 1.0) return 65535;
if(val < -1.0) return 0;
return (ALint)(val * 32767.0) + 32768;
}
static inline ALushort Conv_ALushort_ALmulaw(ALmulaw val)
{ return Conv_ALushort_ALshort(DecodeMuLaw(val)); }
static inline ALushort Conv_ALushort_ALalaw(ALalaw val)
{ return Conv_ALushort_ALshort(DecodeALaw(val)); }
static inline ALushort Conv_ALushort_ALbyte3(ALbyte3 val)
{ return (DecodeByte3(val)>>8)+32768; }
static inline ALushort Conv_ALushort_ALubyte3(ALubyte3 val)
{ return DecodeUByte3(val)>>8; }
static inline ALint Conv_ALint_ALbyte(ALbyte val)
{ return val<<24; }
static inline ALint Conv_ALint_ALubyte(ALubyte val)
{ return (val-128)<<24; }
static inline ALint Conv_ALint_ALshort(ALshort val)
{ return val<<16; }
static inline ALint Conv_ALint_ALushort(ALushort val)
{ return (val-32768)<<16; }
static inline ALint Conv_ALint_ALint(ALint val)
{ return val; }
static inline ALint Conv_ALint_ALuint(ALuint val)
{ return val-2147483648u; }
static inline ALint Conv_ALint_ALfloat(ALfloat val)
{
if(val > 1.0f) return 2147483647;
if(val < -1.0f) return -2147483647-1;
return (ALint)(val*16777215.0f) << 7;
}
static inline ALint Conv_ALint_ALdouble(ALdouble val)
{
if(val > 1.0) return 2147483647;
if(val < -1.0) return -2147483647-1;
return (ALint)(val * 2147483647.0);
}
static inline ALint Conv_ALint_ALmulaw(ALmulaw val)
{ return Conv_ALint_ALshort(DecodeMuLaw(val)); }
static inline ALint Conv_ALint_ALalaw(ALalaw val)
{ return Conv_ALint_ALshort(DecodeALaw(val)); }
static inline ALint Conv_ALint_ALbyte3(ALbyte3 val)
{ return DecodeByte3(val)<<8; }
static inline ALint Conv_ALint_ALubyte3(ALubyte3 val)
{ return (DecodeUByte3(val)-8388608)<<8; }
static inline ALuint Conv_ALuint_ALbyte(ALbyte val)
{ return (val+128)<<24; }
static inline ALuint Conv_ALuint_ALubyte(ALubyte val)
{ return val<<24; }
static inline ALuint Conv_ALuint_ALshort(ALshort val)
{ return (val+32768)<<16; }
static inline ALuint Conv_ALuint_ALushort(ALushort val)
{ return val<<16; }
static inline ALuint Conv_ALuint_ALint(ALint val)
{ return val+2147483648u; }
static inline ALuint Conv_ALuint_ALuint(ALuint val)
{ return val; }
static inline ALuint Conv_ALuint_ALfloat(ALfloat val)
{
if(val > 1.0f) return 4294967295u;
if(val < -1.0f) return 0;
return ((ALint)(val*16777215.0f)<<7) + 2147483648u;
}
static inline ALuint Conv_ALuint_ALdouble(ALdouble val)
{
if(val > 1.0) return 4294967295u;
if(val < -1.0) return 0;
return (ALint)(val * 2147483647.0) + 2147483648u;
}
static inline ALuint Conv_ALuint_ALmulaw(ALmulaw val)
{ return Conv_ALuint_ALshort(DecodeMuLaw(val)); }
static inline ALuint Conv_ALuint_ALalaw(ALalaw val)
{ return Conv_ALuint_ALshort(DecodeALaw(val)); }
static inline ALuint Conv_ALuint_ALbyte3(ALbyte3 val)
{ return (DecodeByte3(val)+8388608)<<8; }
static inline ALuint Conv_ALuint_ALubyte3(ALubyte3 val)
{ return DecodeUByte3(val)<<8; }
static inline ALfloat Conv_ALfloat_ALbyte(ALbyte val)
{ return val * (1.0f/127.0f); }
static inline ALfloat Conv_ALfloat_ALubyte(ALubyte val)
{ return (val-128) * (1.0f/127.0f); }
static inline ALfloat Conv_ALfloat_ALshort(ALshort val)
{ return val * (1.0f/32767.0f); }
static inline ALfloat Conv_ALfloat_ALushort(ALushort val)
{ return (val-32768) * (1.0f/32767.0f); }
static inline ALfloat Conv_ALfloat_ALint(ALint val)
{ return (ALfloat)(val>>7) * (1.0f/16777215.0f); }
static inline ALfloat Conv_ALfloat_ALuint(ALuint val)
{ return (ALfloat)((ALint)(val>>7)-16777216) * (1.0f/16777215.0f); }
/* Slightly special handling for floats and doubles (converts NaN to 0, and
* allows float<->double pass-through).
*/
static inline ALfloat Conv_ALfloat_ALfloat(ALfloat val)
{ return (val==val) ? val : 0.0f; }
static inline ALfloat Conv_ALfloat_ALdouble(ALdouble val)
{ return (val==val) ? (ALfloat)val : 0.0f; }
static inline ALfloat Conv_ALfloat_ALmulaw(ALmulaw val)
{ return Conv_ALfloat_ALshort(DecodeMuLaw(val)); }
static inline ALfloat Conv_ALfloat_ALalaw(ALalaw val)
{ return Conv_ALfloat_ALshort(DecodeALaw(val)); }
static inline ALfloat Conv_ALfloat_ALbyte3(ALbyte3 val)
{ return (ALfloat)(DecodeByte3(val) * (1.0/8388607.0)); }
static inline ALfloat Conv_ALfloat_ALubyte3(ALubyte3 val)
{ return (ALfloat)((DecodeUByte3(val)-8388608) * (1.0/8388607.0)); }
static inline ALdouble Conv_ALdouble_ALbyte(ALbyte val)
{ return val * (1.0/127.0); }
static inline ALdouble Conv_ALdouble_ALubyte(ALubyte val)
{ return (val-128) * (1.0/127.0); }
static inline ALdouble Conv_ALdouble_ALshort(ALshort val)
{ return val * (1.0/32767.0); }
static inline ALdouble Conv_ALdouble_ALushort(ALushort val)
{ return (val-32768) * (1.0/32767.0); }
static inline ALdouble Conv_ALdouble_ALint(ALint val)
{ return val * (1.0/2147483647.0); }
static inline ALdouble Conv_ALdouble_ALuint(ALuint val)
{ return (ALint)(val-2147483648u) * (1.0/2147483647.0); }
static inline ALdouble Conv_ALdouble_ALfloat(ALfloat val)
{ return (val==val) ? val : 0.0f; }
{ return (val==val) ? (ALdouble)val : 0.0; }
static inline ALdouble Conv_ALdouble_ALdouble(ALdouble val)
{ return (val==val) ? val : 0.0; }
static inline ALdouble Conv_ALdouble_ALmulaw(ALmulaw val)
{ return Conv_ALdouble_ALshort(DecodeMuLaw(val)); }
static inline ALdouble Conv_ALdouble_ALalaw(ALalaw val)
{ return Conv_ALdouble_ALshort(DecodeALaw(val)); }
static inline ALdouble Conv_ALdouble_ALbyte3(ALbyte3 val)
{ return DecodeByte3(val) * (1.0/8388607.0); }
static inline ALdouble Conv_ALdouble_ALubyte3(ALubyte3 val)
{ return (DecodeUByte3(val)-8388608) * (1.0/8388607.0); }
#undef DECL_TEMPLATE
/* Define alternate-sign functions. */
#define DECL_TEMPLATE(T1, T2, O) \
static inline T1 Conv_##T1##_##T2(T2 val) { return (T1)val - O; } \
static inline T2 Conv_##T2##_##T1(T1 val) { return (T2)val + O; }
DECL_TEMPLATE(ALbyte, ALubyte, 128);
DECL_TEMPLATE(ALshort, ALushort, 32768);
DECL_TEMPLATE(ALint, ALuint, 2147483648u);
#undef DECL_TEMPLATE
/* Define int-type to int-type functions */
#define DECL_TEMPLATE(T, ST, UT, SH) \
static inline T Conv_##T##_##ST(ST val){ return val >> SH; } \
static inline T Conv_##T##_##UT(UT val){ return Conv_##ST##_##UT(val) >> SH; }\
static inline ST Conv_##ST##_##T(T val){ return val << SH; } \
static inline UT Conv_##UT##_##T(T val){ return Conv_##UT##_##ST(val << SH); }
#define DECL_TEMPLATE2(T1, T2, SH) \
DECL_TEMPLATE(AL##T1, AL##T2, ALu##T2, SH) \
DECL_TEMPLATE(ALu##T1, ALu##T2, AL##T2, SH)
DECL_TEMPLATE2(byte, short, 8)
DECL_TEMPLATE2(short, int, 16)
DECL_TEMPLATE2(byte, int, 24)
#undef DECL_TEMPLATE2
#undef DECL_TEMPLATE
/* Define int-type to fp functions */
#define DECL_TEMPLATE(T, ST, UT, OP) \
static inline T Conv_##T##_##ST(ST val) { return (T)val * OP; } \
static inline T Conv_##T##_##UT(UT val) { return (T)Conv_##ST##_##UT(val) * OP; }
#define DECL_TEMPLATE2(T1, T2, OP) \
DECL_TEMPLATE(T1, AL##T2, ALu##T2, OP)
DECL_TEMPLATE2(ALfloat, byte, (1.0f/128.0f))
DECL_TEMPLATE2(ALdouble, byte, (1.0/128.0))
DECL_TEMPLATE2(ALfloat, short, (1.0f/32768.0f))
DECL_TEMPLATE2(ALdouble, short, (1.0/32768.0))
DECL_TEMPLATE2(ALdouble, int, (1.0/2147483648.0))
/* Special handling for int32 to float32, since it would overflow. */
static inline ALfloat Conv_ALfloat_ALint(ALint val)
{ return (ALfloat)(val>>7) * (1.0f/16777216.0f); }
static inline ALfloat Conv_ALfloat_ALuint(ALuint val)
{ return (ALfloat)(Conv_ALint_ALuint(val)>>7) * (1.0f/16777216.0f); }
#undef DECL_TEMPLATE2
#undef DECL_TEMPLATE
/* Define fp to int-type functions */
#define DECL_TEMPLATE(FT, T, smin, smax) \
static inline AL##T Conv_AL##T##_##FT(FT val) \
{ \
val *= (FT)smax + 1; \
if(val >= (FT)smax) return smax; \
if(val <= (FT)smin) return smin; \
return (AL##T)val; \
} \
static inline ALu##T Conv_ALu##T##_##FT(FT val) \
{ return Conv_ALu##T##_AL##T(Conv_AL##T##_##FT(val)); }
DECL_TEMPLATE(ALfloat, byte, -128, 127)
DECL_TEMPLATE(ALdouble, byte, -128, 127)
DECL_TEMPLATE(ALfloat, short, -32768, 32767)
DECL_TEMPLATE(ALdouble, short, -32768, 32767)
DECL_TEMPLATE(ALdouble, int, -2147483647-1, 2147483647)
/* Special handling for float32 to int32, since it would overflow. */
static inline ALint Conv_ALint_ALfloat(ALfloat val)
{
val *= 16777216.0f;
if(val >= 16777215.0f) return 0x7fffff80/*16777215 << 7*/;
if(val <= -16777216.0f) return 0x80000000/*-16777216 << 7*/;
return (ALint)val << 7;
}
static inline ALuint Conv_ALuint_ALfloat(ALfloat val)
{ return Conv_ALuint_ALint(Conv_ALint_ALfloat(val)); }
#undef DECL_TEMPLATE
/* Define muLaw and aLaw functions (goes through short functions). */
#define DECL_TEMPLATE(T) \
static inline ALmulaw Conv_ALmulaw_##T(T val) \
{ return EncodeMuLaw(Conv_ALshort_##T(val)); }
DECL_TEMPLATE(ALbyte)
DECL_TEMPLATE(ALubyte)
DECL_TEMPLATE(ALshort)
DECL_TEMPLATE(ALushort)
DECL_TEMPLATE(ALint)
DECL_TEMPLATE(ALuint)
DECL_TEMPLATE(ALfloat)
DECL_TEMPLATE(ALdouble)
static inline ALmulaw Conv_ALmulaw_ALmulaw(ALmulaw val)
{ return val; }
DECL_TEMPLATE(ALalaw)
DECL_TEMPLATE(ALbyte3)
DECL_TEMPLATE(ALubyte3)
#undef DECL_TEMPLATE
#define DECL_TEMPLATE(T) \
{ return EncodeMuLaw(Conv_ALshort_##T(val)); } \
static inline T Conv_##T##_ALmulaw(ALmulaw val) \
{ return Conv_##T##_ALshort(DecodeMuLaw(val)); } \
\
static inline ALalaw Conv_ALalaw_##T(T val) \
{ return EncodeALaw(Conv_ALshort_##T(val)); }
{ return EncodeALaw(Conv_ALshort_##T(val)); } \
static inline T Conv_##T##_ALalaw(ALalaw val) \
{ return Conv_##T##_ALshort(DecodeALaw(val)); }
DECL_TEMPLATE(ALbyte)
DECL_TEMPLATE(ALubyte)
@@ -821,53 +621,14 @@ DECL_TEMPLATE(ALint)
DECL_TEMPLATE(ALuint)
DECL_TEMPLATE(ALfloat)
DECL_TEMPLATE(ALdouble)
DECL_TEMPLATE(ALmulaw)
static inline ALalaw Conv_ALalaw_ALalaw(ALalaw val)
{ return val; }
DECL_TEMPLATE(ALbyte3)
DECL_TEMPLATE(ALubyte3)
#undef DECL_TEMPLATE
#define DECL_TEMPLATE(T) \
static inline ALbyte3 Conv_ALbyte3_##T(T val) \
{ return EncodeByte3(Conv_ALint_##T(val)>>8); }
DECL_TEMPLATE(ALbyte)
DECL_TEMPLATE(ALubyte)
DECL_TEMPLATE(ALshort)
DECL_TEMPLATE(ALushort)
DECL_TEMPLATE(ALint)
DECL_TEMPLATE(ALuint)
DECL_TEMPLATE(ALfloat)
DECL_TEMPLATE(ALdouble)
DECL_TEMPLATE(ALmulaw)
DECL_TEMPLATE(ALalaw)
static inline ALbyte3 Conv_ALbyte3_ALbyte3(ALbyte3 val)
{ return val; }
DECL_TEMPLATE(ALubyte3)
#undef DECL_TEMPLATE
#define DECL_TEMPLATE(T) \
static inline ALubyte3 Conv_ALubyte3_##T(T val) \
{ return EncodeUByte3(Conv_ALuint_##T(val)>>8); }
DECL_TEMPLATE(ALbyte)
DECL_TEMPLATE(ALubyte)
DECL_TEMPLATE(ALshort)
DECL_TEMPLATE(ALushort)
DECL_TEMPLATE(ALint)
DECL_TEMPLATE(ALuint)
DECL_TEMPLATE(ALfloat)
DECL_TEMPLATE(ALdouble)
DECL_TEMPLATE(ALmulaw)
DECL_TEMPLATE(ALalaw)
DECL_TEMPLATE(ALbyte3)
static inline ALubyte3 Conv_ALubyte3_ALubyte3(ALubyte3 val)
{ return val; }
#undef DECL_TEMPLATE
/* Define muLaw <-> aLaw functions. */
static inline ALalaw Conv_ALalaw_ALmulaw(ALmulaw val)
{ return EncodeALaw(DecodeMuLaw(val)); }
static inline ALmulaw Conv_ALmulaw_ALalaw(ALalaw val)
{ return EncodeMuLaw(DecodeALaw(val)); }
#define DECL_TEMPLATE(T1, T2) \
@@ -892,9 +653,7 @@ DECL_TEMPLATE(T, ALuint) \
DECL_TEMPLATE(T, ALfloat) \
DECL_TEMPLATE(T, ALdouble) \
DECL_TEMPLATE(T, ALmulaw) \
DECL_TEMPLATE(T, ALalaw) \
DECL_TEMPLATE(T, ALbyte3) \
DECL_TEMPLATE(T, ALubyte3)
DECL_TEMPLATE(T, ALalaw)
DECL_TEMPLATE2(ALbyte)
DECL_TEMPLATE2(ALubyte)
@@ -906,8 +665,6 @@ DECL_TEMPLATE2(ALfloat)
DECL_TEMPLATE2(ALdouble)
DECL_TEMPLATE2(ALmulaw)
DECL_TEMPLATE2(ALalaw)
DECL_TEMPLATE2(ALbyte3)
DECL_TEMPLATE2(ALubyte3)
#undef DECL_TEMPLATE2
#undef DECL_TEMPLATE
@@ -957,8 +714,6 @@ DECL_TEMPLATE(ALfloat)
DECL_TEMPLATE(ALdouble)
DECL_TEMPLATE(ALmulaw)
DECL_TEMPLATE(ALalaw)
DECL_TEMPLATE(ALbyte3)
DECL_TEMPLATE(ALubyte3)
#undef DECL_TEMPLATE
@@ -1010,8 +765,6 @@ DECL_TEMPLATE(ALfloat)
DECL_TEMPLATE(ALdouble)
DECL_TEMPLATE(ALmulaw)
DECL_TEMPLATE(ALalaw)
DECL_TEMPLATE(ALbyte3)
DECL_TEMPLATE(ALubyte3)
#undef DECL_TEMPLATE
@@ -1063,8 +816,6 @@ DECL_TEMPLATE(ALfloat)
DECL_TEMPLATE(ALdouble)
DECL_TEMPLATE(ALmulaw)
DECL_TEMPLATE(ALalaw)
DECL_TEMPLATE(ALbyte3)
DECL_TEMPLATE(ALubyte3)
#undef DECL_TEMPLATE
@@ -1114,8 +865,6 @@ DECL_TEMPLATE(ALfloat)
DECL_TEMPLATE(ALdouble)
DECL_TEMPLATE(ALmulaw)
DECL_TEMPLATE(ALalaw)
DECL_TEMPLATE(ALbyte3)
DECL_TEMPLATE(ALubyte3)
#undef DECL_TEMPLATE
@@ -1192,12 +941,6 @@ static void Convert_##T(T *dst, const ALvoid *src, enum UserFmtType srcType, \
case UserFmtMSADPCM: \
Convert_##T##_ALmsadpcm(dst, src, numchans, len, align); \
break; \
case UserFmtByte3: \
Convert_##T##_ALbyte3(dst, src, numchans, len, align); \
break; \
case UserFmtUByte3: \
Convert_##T##_ALubyte3(dst, src, numchans, len, align); \
break; \
} \
}
@@ -1213,8 +956,6 @@ DECL_TEMPLATE(ALmulaw)
DECL_TEMPLATE(ALalaw)
DECL_TEMPLATE(ALima4)
DECL_TEMPLATE(ALmsadpcm)
DECL_TEMPLATE(ALbyte3)
DECL_TEMPLATE(ALubyte3)
#undef DECL_TEMPLATE
@@ -1259,11 +1000,5 @@ void ConvertData(ALvoid *dst, enum UserFmtType dstType, const ALvoid *src, enum
case UserFmtMSADPCM:
Convert_ALmsadpcm(dst, src, srcType, numchans, len, align);
break;
case UserFmtByte3:
Convert_ALbyte3(dst, src, srcType, numchans, len, align);
break;
case UserFmtUByte3:
Convert_ALubyte3(dst, src, srcType, numchans, len, align);
break;
}
}
+125 -19
View File
@@ -48,7 +48,10 @@
## channels:
# Sets the output channel configuration. If left unspecified, one will try to
# be detected from the system, and defaulting to stereo. The available values
# are: mono, stereo, quad, surround51, surround51rear, surround61, surround71
# are: mono, stereo, quad, surround51, surround51rear, surround61, surround71,
# ambi1, ambi2, ambi3. Note that the ambi* configurations provide ambisonic
# channels of the given order (using ACN ordering and SN3D normalization by
# default), which need to be decoded to play correctly on speakers.
#channels =
## sample-type:
@@ -78,7 +81,7 @@
# which helps protect against skips when the CPU is under load, but increases
# the delay between a sound getting mixed and being heard. Acceptable values
# range between 2 and 16.
#periods = 4
#periods = 3
## stereo-mode:
# Specifies if stereo output is treated as being headphones or speakers. With
@@ -86,6 +89,20 @@
# Valid settings are auto, speakers, and headphones.
#stereo-mode = auto
## stereo-encoding:
# Specifies the encoding method for non-HRTF stereo output. 'panpot' (default)
# uses standard amplitude panning (aka pair-wise, stereo pair, etc) between
# -30 and +30 degrees, while 'uhj' creates stereo-compatible two-channel UHJ
# output, which encodes some surround sound information into stereo output
# that can be decoded with a surround sound receiver. If crossfeed filters are
# used, UHJ is disabled.
#stereo-encoding = panpot
## ambi-format:
# Specifies the channel order and normalization for the "ambi*" set of channel
# configurations. Valid settings are: fuma, acn+sn3d, acn+n3d
#ambi-format = acn+sn3d
## hrtf:
# Controls HRTF processing. These filters provide better spatialization of
# sounds while using headphones, but do require a bit more CPU power. The
@@ -96,22 +113,24 @@
# respectively.
#hrtf = auto
## hrtf_tables:
# Specifies a comma-separated list of files containing HRTF data sets. The
# format of the files are described in hrtf.txt. The filenames may contain
# these markers, which will be replaced as needed:
# %r - Device sampling rate
# %s - Non-greedy string (up to the following matching characters)
# %% - Percent sign (%)
# The listed files are relative to system-dependant data directories. On
# Windows this is:
## default-hrtf:
# Specifies the default HRTF to use. When multiple HRTFs are available, this
# determines the preferred one to use if none are specifically requested. Note
# that this is the enumerated HRTF name, not necessarily the filename.
#default-hrtf =
## hrtf-paths:
# Specifies a comma-separated list of paths containing HRTF data sets. The
# format of the files are described in docs/hrtf.txt. The files within the
# directories must have the .mhr file extension to be recognized. By default,
# OS-dependent data paths will be used. They will also be used if the list
# ends with a comma. On Windows this is:
# $AppData\openal\hrtf
# And on other systems, it's (in order):
# $XDG_DATA_HOME/openal/hrtf (defaults to $HOME/.local/share/openal/hrtf)
# $XDG_DATA_DIRS/openal/hrtf (defaults to /usr/local/share/openal/hrtf and
# /usr/share/openal/hrtf)
# An absolute path may also be specified, if the given file is elsewhere.
#hrtf_tables = %s.mhr
#hrtf-paths =
## cf_level:
# Sets the crossfeed level for stereo output. Valid values are:
@@ -131,7 +150,6 @@
# point - nearest sample, no interpolation
# linear - extrapolates samples using a linear slope between samples
# sinc4 - extrapolates samples using a 4-point Sinc filter
# sinc8 - extrapolates samples using an 8-point Sinc filter
# bsinc - extrapolates samples using a band-limited Sinc filter (varying
# between 12 and 24 points, with anti-aliasing)
# Specifying other values will result in using the default (linear).
@@ -156,13 +174,38 @@
# can use a non-negligible amount of CPU time if an effect is set on it even
# if no sources are feeding it, so this may help when apps use more than the
# system can handle.
#slots = 4
#slots = 64
## sends:
# Sets the number of auxiliary sends per source. When not specified (default),
# it allows the app to request how many it wants. The maximum value currently
# possible is 4.
#sends =
# Limits the number of auxiliary sends allowed per source. Setting this higher
# than the default has no effect.
#sends = 16
## output-limiter:
# Applies a gain limiter on the final mixed output. This reduces the volume
# when the output samples would otherwise clamp, avoiding excessive clipping
# noise.
#output-limiter = true
## dither:
# Applies dithering on the final mix, for 8- and 16-bit output by default.
# This replaces the distortion created by nearest-value quantization with low-
# level whitenoise.
#dither = true
## dither-depth:
# Quantization bit-depth for dithered output. A value of 0 (or less) will
# match the output sample depth. For int32, uint32, and float32 output, 0 will
# disable dithering because they're at or beyond the rendered precision. The
# maximum dither depth is 24.
#dither-depth = 0
## volume-adjust:
# A global volume adjustment for source output, expressed in decibels. The
# value is logarithmic, so +6 will be a scale of (approximately) 2x, +12 will
# be a scale of 4x, etc. Similarly, -6 will be x1/2, and -12 is about x1/4. A
# value of 0 means no change.
#volume-adjust = 0
## excludefx: (global)
# Sets which effects to exclude, preventing apps from using them. This can
@@ -192,6 +235,69 @@
# of a context error. On Windows, a breakpoint exception is generated.
#trap-al-error = false
##
## Ambisonic decoder stuff
##
[decoder]
## hq-mode:
# Enables a high-quality ambisonic decoder. This mode is capable of frequency-
# dependent processing, creating a better reproduction of 3D sound rendering
# over surround sound speakers. Enabling this also requires specifying decoder
# configuration files for the appropriate speaker configuration you intend to
# use (see the quad, surround51, etc options below). Currently, up to third-
# order decoding is supported.
hq-mode = false
## distance-comp:
# Enables compensation for the speakers' relative distances to the listener.
# This applies the necessary delays and attenuation to make the speakers
# behave as though they are all equidistant, which is important for proper
# playback of 3D sound rendering. Requires the proper distances to be
# specified in the decoder configuration file.
distance-comp = true
## nfc:
# Enables near-field control filters. This simulates and compensates for low-
# frequency effects caused by the curvature of nearby sound-waves, which
# creates a more realistic perception of sound distance. Note that the effect
# may be stronger or weaker than intended if the application doesn't use or
# specify an appropriate unit scale, or if incorrect speaker distances are set
# in the decoder configuration file. Requires hq-mode to be enabled.
nfc = true
## nfc-ref-delay
# Specifies the reference delay value for ambisonic output. When channels is
# set to one of the ambi* formats, this option enables NFC-HOA output with the
# specified Reference Delay parameter. The specified value can then be shared
# with an appropriate NFC-HOA decoder to reproduce correct near-field effects.
# Keep in mind that despite being designed for higher-order ambisonics, this
# applies to first-order output all the same. When left unset, normal output
# is created with no near-field simulation.
nfc-ref-delay =
## quad:
# Decoder configuration file for Quadrophonic channel output. See
# docs/ambdec.txt for a description of the file format.
quad =
## surround51:
# Decoder configuration file for 5.1 Surround (Side and Rear) channel output.
# See docs/ambdec.txt for a description of the file format.
surround51 =
## surround61:
# Decoder configuration file for 6.1 Surround channel output. See
# docs/ambdec.txt for a description of the file format.
surround61 =
## surround71:
# Decoder configuration file for 7.1 Surround channel output. See
# docs/ambdec.txt for a description of the file format. Note: This can be used
# to enable 3D7.1 with the appropriate configuration and speaker placement,
# see docs/3D7.1.txt.
surround71 =
##
## Reverb effect stuff (includes EAX reverb)
##
+19
View File
@@ -0,0 +1,19 @@
version: 1.18.2.{build}
environment:
matrix:
- GEN: "Visual Studio 14 2015"
CFG: Release
- GEN: "Visual Studio 14 2015 Win64"
CFG: Release
install:
# Remove the VS Xamarin targets to reduce AppVeyor specific noise in build
# logs. See also http://help.appveyor.com/discussions/problems/4569
- del "C:\Program Files (x86)\MSBuild\14.0\Microsoft.Common.targets\ImportAfter\Xamarin.Common.targets"
build_script:
- cd build
- cmake -G"%GEN%" -DALSOFT_REQUIRE_WINMM=ON -DALSOFT_REQUIRE_DSOUND=ON -DALSOFT_REQUIRE_MMDEVAPI=ON -DALSOFT_EMBED_HRTF_DATA=YES ..
- cmake --build . --config %CFG% --clean-first
+2 -2
View File
@@ -34,7 +34,7 @@
# License text for the above reference.)
MACRO(CHECK_SHARED_FUNCTION_EXISTS SYMBOL FILES LIBRARY LOCATION VARIABLE)
IF("${VARIABLE}" MATCHES "^${VARIABLE}$")
IF(NOT DEFINED "${VARIABLE}" OR "x${${VARIABLE}}" STREQUAL "x${VARIABLE}")
SET(CMAKE_CONFIGURABLE_FILE_CONTENT "/* */\n")
SET(MACRO_CHECK_SYMBOL_EXISTS_FLAGS ${CMAKE_REQUIRED_FLAGS})
IF(CMAKE_REQUIRED_LIBRARIES)
@@ -88,5 +88,5 @@ MACRO(CHECK_SHARED_FUNCTION_EXISTS SYMBOL FILES LIBRARY LOCATION VARIABLE)
"${OUTPUT}\nFile ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckSymbolExists.c:\n"
"${CMAKE_CONFIGURABLE_FILE_CONTENT}\n")
ENDIF(${VARIABLE})
ENDIF("${VARIABLE}" MATCHES "^${VARIABLE}$")
ENDIF(NOT DEFINED "${VARIABLE}" OR "x${${VARIABLE}}" STREQUAL "x${VARIABLE}")
ENDMACRO(CHECK_SHARED_FUNCTION_EXISTS)
+18 -12
View File
@@ -8,24 +8,30 @@
# DSOUND_LIBRARY - the dsound library
#
find_path(DSOUND_INCLUDE_DIR
NAMES dsound.h
PATHS "${DXSDK_DIR}"
PATH_SUFFIXES include
DOC "The DirectSound include directory"
)
if (WIN32)
include(FindWindowsSDK)
if (WINDOWSSDK_FOUND)
get_windowssdk_library_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_LIB_DIRS)
get_windowssdk_include_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_INCLUDE_DIRS)
endif()
endif()
# DSOUND_INCLUDE_DIR
find_path(DSOUND_INCLUDE_DIR
NAMES "dsound.h"
PATHS "${DXSDK_DIR}" ${WINSDK_INCLUDE_DIRS}
PATH_SUFFIXES include
DOC "The DirectSound include directory")
# DSOUND_LIBRARY
find_library(DSOUND_LIBRARY
NAMES dsound
PATHS "${DXSDK_DIR}"
PATHS "${DXSDK_DIR}" ${WINSDK_LIB_DIRS}
PATH_SUFFIXES lib lib/x86 lib/x64
DOC "The DirectSound library"
)
DOC "The DirectSound library")
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(DSound
REQUIRED_VARS DSOUND_LIBRARY DSOUND_INCLUDE_DIR
)
find_package_handle_standard_args(DSound REQUIRED_VARS DSOUND_LIBRARY DSOUND_INCLUDE_DIR)
if(DSOUND_FOUND)
set(DSOUND_LIBRARIES ${DSOUND_LIBRARY})
+6
View File
@@ -142,6 +142,12 @@ foreach(_component ${FFmpeg_FIND_COMPONENTS})
endif()
endforeach()
# Add libz if it exists (needed for static ffmpeg builds)
find_library(_FFmpeg_HAVE_LIBZ NAMES z)
if(_FFmpeg_HAVE_LIBZ)
set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${_FFmpeg_HAVE_LIBZ})
endif()
# Build the include path and library list with duplicates removed.
if(FFMPEG_INCLUDE_DIRS)
list(REMOVE_DUPLICATES FFMPEG_INCLUDE_DIRS)
+13 -1
View File
@@ -2,8 +2,10 @@
#
# OSS_FOUND - True if OSS_INCLUDE_DIR is found
# OSS_INCLUDE_DIRS - Set when OSS_INCLUDE_DIR is found
# OSS_LIBRARIES - Set when OSS_LIBRARY is found
#
# OSS_INCLUDE_DIR - where to find sys/soundcard.h, etc.
# OSS_LIBRARY - where to find libossaudio (optional).
#
find_path(OSS_INCLUDE_DIR
@@ -11,11 +13,21 @@ find_path(OSS_INCLUDE_DIR
DOC "The OSS include directory"
)
find_library(OSS_LIBRARY
NAMES ossaudio
DOC "Optional OSS library"
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(OSS REQUIRED_VARS OSS_INCLUDE_DIR)
if(OSS_FOUND)
set(OSS_INCLUDE_DIRS ${OSS_INCLUDE_DIR})
if(OSS_LIBRARY)
set(OSS_LIBRARIES ${OSS_LIBRARY})
else()
unset(OSS_LIBRARIES)
endif()
endif()
mark_as_advanced(OSS_INCLUDE_DIR)
mark_as_advanced(OSS_INCLUDE_DIR OSS_LIBRARY)
+626
View File
@@ -0,0 +1,626 @@
# - Find the Windows SDK aka Platform SDK
#
# Relevant Wikipedia article: http://en.wikipedia.org/wiki/Microsoft_Windows_SDK
#
# Pass "COMPONENTS tools" to ignore Visual Studio version checks: in case
# you just want the tool binaries to run, rather than the libraries and headers
# for compiling.
#
# Variables:
# WINDOWSSDK_FOUND - if any version of the windows or platform SDK was found that is usable with the current version of visual studio
# WINDOWSSDK_LATEST_DIR
# WINDOWSSDK_LATEST_NAME
# WINDOWSSDK_FOUND_PREFERENCE - if we found an entry indicating a "preferred" SDK listed for this visual studio version
# WINDOWSSDK_PREFERRED_DIR
# WINDOWSSDK_PREFERRED_NAME
#
# WINDOWSSDK_DIRS - contains no duplicates, ordered most recent first.
# WINDOWSSDK_PREFERRED_FIRST_DIRS - contains no duplicates, ordered with preferred first, followed by the rest in descending recency
#
# Functions:
# windowssdk_name_lookup(<directory> <output variable>) - Find the name corresponding with the SDK directory you pass in, or
# NOTFOUND if not recognized. Your directory must be one of WINDOWSSDK_DIRS for this to work.
#
# windowssdk_build_lookup(<directory> <output variable>) - Find the build version number corresponding with the SDK directory you pass in, or
# NOTFOUND if not recognized. Your directory must be one of WINDOWSSDK_DIRS for this to work.
#
# get_windowssdk_from_component(<file or dir> <output variable>) - Given a library or include dir,
# find the Windows SDK root dir corresponding to it, or NOTFOUND if unrecognized.
#
# get_windowssdk_library_dirs(<directory> <output variable>) - Find the architecture-appropriate
# library directories corresponding to the SDK directory you pass in (or NOTFOUND if none)
#
# get_windowssdk_library_dirs_multiple(<output variable> <directory> ...) - Find the architecture-appropriate
# library directories corresponding to the SDK directories you pass in, in order, skipping those not found. NOTFOUND if none at all.
# Good for passing WINDOWSSDK_DIRS or WINDOWSSDK_DIRS to if you really just want a file and don't care where from.
#
# get_windowssdk_include_dirs(<directory> <output variable>) - Find the
# include directories corresponding to the SDK directory you pass in (or NOTFOUND if none)
#
# get_windowssdk_include_dirs_multiple(<output variable> <directory> ...) - Find the
# include directories corresponding to the SDK directories you pass in, in order, skipping those not found. NOTFOUND if none at all.
# Good for passing WINDOWSSDK_DIRS or WINDOWSSDK_DIRS to if you really just want a file and don't care where from.
#
# Requires these CMake modules:
# FindPackageHandleStandardArgs (known included with CMake >=2.6.2)
#
# Original Author:
# 2012 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
# http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC
#
# Copyright Iowa State University 2012.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
set(_preferred_sdk_dirs) # pre-output
set(_win_sdk_dirs) # pre-output
set(_win_sdk_versanddirs) # pre-output
set(_win_sdk_buildsanddirs) # pre-output
set(_winsdk_vistaonly) # search parameters
set(_winsdk_kits) # search parameters
set(_WINDOWSSDK_ANNOUNCE OFF)
if(NOT WINDOWSSDK_FOUND AND (NOT WindowsSDK_FIND_QUIETLY))
set(_WINDOWSSDK_ANNOUNCE ON)
endif()
macro(_winsdk_announce)
if(_WINSDK_ANNOUNCE)
message(STATUS ${ARGN})
endif()
endmacro()
set(_winsdk_win10vers
10.0.14393.0 # Redstone aka Win10 1607 "Anniversary Update"
10.0.10586.0 # TH2 aka Win10 1511
10.0.10240.0 # Win10 RTM
10.0.10150.0 # just ucrt
10.0.10056.0
)
if(WindowsSDK_FIND_COMPONENTS MATCHES "tools")
set(_WINDOWSSDK_IGNOREMSVC ON)
_winsdk_announce("Checking for tools from Windows/Platform SDKs...")
else()
set(_WINDOWSSDK_IGNOREMSVC OFF)
_winsdk_announce("Checking for Windows/Platform SDKs...")
endif()
# Appends to the three main pre-output lists used only if the path exists
# and is not already in the list.
function(_winsdk_conditional_append _vername _build _path)
if(("${_path}" MATCHES "registry") OR (NOT EXISTS "${_path}"))
# Path invalid - do not add
return()
endif()
list(FIND _win_sdk_dirs "${_path}" _win_sdk_idx)
if(_win_sdk_idx GREATER -1)
# Path already in list - do not add
return()
endif()
_winsdk_announce( " - ${_vername}, Build ${_build} @ ${_path}")
# Not yet in the list, so we'll add it
list(APPEND _win_sdk_dirs "${_path}")
set(_win_sdk_dirs "${_win_sdk_dirs}" CACHE INTERNAL "" FORCE)
list(APPEND
_win_sdk_versanddirs
"${_vername}"
"${_path}")
set(_win_sdk_versanddirs "${_win_sdk_versanddirs}" CACHE INTERNAL "" FORCE)
list(APPEND
_win_sdk_buildsanddirs
"${_build}"
"${_path}")
set(_win_sdk_buildsanddirs "${_win_sdk_buildsanddirs}" CACHE INTERNAL "" FORCE)
endfunction()
# Appends to the "preferred SDK" lists only if the path exists
function(_winsdk_conditional_append_preferred _info _path)
if(("${_path}" MATCHES "registry") OR (NOT EXISTS "${_path}"))
# Path invalid - do not add
return()
endif()
get_filename_component(_path "${_path}" ABSOLUTE)
list(FIND _win_sdk_preferred_sdk_dirs "${_path}" _win_sdk_idx)
if(_win_sdk_idx GREATER -1)
# Path already in list - do not add
return()
endif()
_winsdk_announce( " - Found \"preferred\" SDK ${_info} @ ${_path}")
# Not yet in the list, so we'll add it
list(APPEND _win_sdk_preferred_sdk_dirs "${_path}")
set(_win_sdk_preferred_sdk_dirs "${_win_sdk_dirs}" CACHE INTERNAL "" FORCE)
# Just in case we somehow missed it:
_winsdk_conditional_append("${_info}" "" "${_path}")
endfunction()
# Given a version like v7.0A, looks for an SDK in the registry under "Microsoft SDKs".
# If the given version might be in both HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows
# and HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots aka "Windows Kits",
# use this macro first, since these registry keys usually have more information.
#
# Pass a "default" build number as an extra argument in case we can't find it.
function(_winsdk_check_microsoft_sdks_registry _winsdkver)
set(SDKKEY "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\${_winsdkver}")
get_filename_component(_sdkdir
"[${SDKKEY};InstallationFolder]"
ABSOLUTE)
set(_sdkname "Windows SDK ${_winsdkver}")
# Default build number passed as extra argument
set(_build ${ARGN})
# See if the registry holds a Microsoft-mutilated, err, designated, product name
# (just using get_filename_component to execute the registry lookup)
get_filename_component(_sdkproductname
"[${SDKKEY};ProductName]"
NAME)
if(NOT "${_sdkproductname}" MATCHES "registry")
# Got a product name
set(_sdkname "${_sdkname} (${_sdkproductname})")
endif()
# try for a version to augment our name
# (just using get_filename_component to execute the registry lookup)
get_filename_component(_sdkver
"[${SDKKEY};ProductVersion]"
NAME)
if(NOT "${_sdkver}" MATCHES "registry" AND NOT MATCHES)
# Got a version
if(NOT "${_sdkver}" MATCHES "\\.\\.")
# and it's not an invalid one with two dots in it:
# use to override the default build
set(_build ${_sdkver})
if(NOT "${_sdkname}" MATCHES "${_sdkver}")
# Got a version that's not already in the name, let's use it to improve our name.
set(_sdkname "${_sdkname} (${_sdkver})")
endif()
endif()
endif()
_winsdk_conditional_append("${_sdkname}" "${_build}" "${_sdkdir}")
endfunction()
# Given a name for identification purposes, the build number, and a key (technically a "value name")
# corresponding to a Windows SDK packaged as a "Windows Kit", look for it
# in HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots
# Note that the key or "value name" tends to be something weird like KitsRoot81 -
# no easy way to predict, just have to observe them in the wild.
# Doesn't hurt to also try _winsdk_check_microsoft_sdks_registry for these:
# sometimes you get keys in both parts of the registry (in the wow64 portion especially),
# and the non-"Windows Kits" location is often more descriptive.
function(_winsdk_check_windows_kits_registry _winkit_name _winkit_build _winkit_key)
get_filename_component(_sdkdir
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots;${_winkit_key}]"
ABSOLUTE)
_winsdk_conditional_append("${_winkit_name}" "${_winkit_build}" "${_sdkdir}")
endfunction()
# Given a name for identification purposes and the build number
# corresponding to a Windows 10 SDK packaged as a "Windows Kit", look for it
# in HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots
# Doesn't hurt to also try _winsdk_check_microsoft_sdks_registry for these:
# sometimes you get keys in both parts of the registry (in the wow64 portion especially),
# and the non-"Windows Kits" location is often more descriptive.
function(_winsdk_check_win10_kits _winkit_build)
get_filename_component(_sdkdir
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots;KitsRoot10]"
ABSOLUTE)
if(("${_sdkdir}" MATCHES "registry") OR (NOT EXISTS "${_sdkdir}"))
return() # not found
endif()
if(EXISTS "${_sdkdir}/Include/${_winkit_build}/um")
_winsdk_conditional_append("Windows Kits 10 (Build ${_winkit_build})" "${_winkit_build}" "${_sdkdir}")
endif()
endfunction()
# Given a name for indentification purposes, the build number, and the associated package GUID,
# look in the registry under both HKLM and HKCU in \\SOFTWARE\\Microsoft\\MicrosoftSDK\\InstalledSDKs\\
# for that guid and the SDK it points to.
function(_winsdk_check_platformsdk_registry _platformsdkname _build _platformsdkguid)
foreach(_winsdk_hive HKEY_LOCAL_MACHINE HKEY_CURRENT_USER)
get_filename_component(_sdkdir
"[${_winsdk_hive}\\SOFTWARE\\Microsoft\\MicrosoftSDK\\InstalledSDKs\\${_platformsdkguid};Install Dir]"
ABSOLUTE)
_winsdk_conditional_append("${_platformsdkname} (${_build})" "${_build}" "${_sdkdir}")
endforeach()
endfunction()
###
# Detect toolchain information: to know whether it's OK to use Vista+ only SDKs
###
set(_winsdk_vistaonly_ok OFF)
if(MSVC AND NOT _WINDOWSSDK_IGNOREMSVC)
# VC 10 and older has broad target support
if(MSVC_VERSION LESS 1700)
# VC 11 by default targets Vista and later only, so we can add a few more SDKs that (might?) only work on vista+
elseif("${CMAKE_VS_PLATFORM_TOOLSET}" MATCHES "_xp")
# This is the XP-compatible v110+ toolset
elseif("${CMAKE_VS_PLATFORM_TOOLSET}" STREQUAL "v100" OR "${CMAKE_VS_PLATFORM_TOOLSET}" STREQUAL "v90")
# This is the VS2010/VS2008 toolset
else()
# OK, we're VC11 or newer and not using a backlevel or XP-compatible toolset.
# These versions have no XP (and possibly Vista pre-SP1) support
set(_winsdk_vistaonly_ok ON)
if(_WINDOWSSDK_ANNOUNCE AND NOT _WINDOWSSDK_VISTAONLY_PESTERED)
set(_WINDOWSSDK_VISTAONLY_PESTERED ON CACHE INTERNAL "" FORCE)
message(STATUS "FindWindowsSDK: Detected Visual Studio 2012 or newer, not using the _xp toolset variant: including SDK versions that drop XP support in search!")
endif()
endif()
endif()
if(_WINDOWSSDK_IGNOREMSVC)
set(_winsdk_vistaonly_ok ON)
endif()
###
# MSVC version checks - keeps messy conditionals in one place
# (messy because of _WINDOWSSDK_IGNOREMSVC)
###
set(_winsdk_msvc_greater_1200 OFF)
if(_WINDOWSSDK_IGNOREMSVC OR (MSVC AND (MSVC_VERSION GREATER 1200)))
set(_winsdk_msvc_greater_1200 ON)
endif()
# Newer than VS .NET/VS Toolkit 2003
set(_winsdk_msvc_greater_1310 OFF)
if(_WINDOWSSDK_IGNOREMSVC OR (MSVC AND (MSVC_VERSION GREATER 1310)))
set(_winsdk_msvc_greater_1310 ON)
endif()
# VS2005/2008
set(_winsdk_msvc_less_1600 OFF)
if(_WINDOWSSDK_IGNOREMSVC OR (MSVC AND (MSVC_VERSION LESS 1600)))
set(_winsdk_msvc_less_1600 ON)
endif()
# VS2013+
set(_winsdk_msvc_not_less_1800 OFF)
if(_WINDOWSSDK_IGNOREMSVC OR (MSVC AND (NOT MSVC_VERSION LESS 1800)))
set(_winsdk_msvc_not_less_1800 ON)
endif()
###
# START body of find module
###
if(_winsdk_msvc_greater_1310) # Newer than VS .NET/VS Toolkit 2003
###
# Look for "preferred" SDKs
###
# Environment variable for SDK dir
if(EXISTS "$ENV{WindowsSDKDir}" AND (NOT "$ENV{WindowsSDKDir}" STREQUAL ""))
_winsdk_conditional_append_preferred("WindowsSDKDir environment variable" "$ENV{WindowsSDKDir}")
endif()
if(_winsdk_msvc_less_1600)
# Per-user current Windows SDK for VS2005/2008
get_filename_component(_sdkdir
"[HKEY_CURRENT_USER\\Software\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]"
ABSOLUTE)
_winsdk_conditional_append_preferred("Per-user current Windows SDK" "${_sdkdir}")
# System-wide current Windows SDK for VS2005/2008
get_filename_component(_sdkdir
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]"
ABSOLUTE)
_winsdk_conditional_append_preferred("System-wide current Windows SDK" "${_sdkdir}")
endif()
###
# Begin the massive list of SDK searching!
###
if(_winsdk_vistaonly_ok AND _winsdk_msvc_not_less_1800)
# These require at least Visual Studio 2013 (VC12)
_winsdk_check_microsoft_sdks_registry(v10.0A)
# Windows Software Development Kit (SDK) for Windows 10
# Several different versions living in the same directory - if nothing else we can assume RTM (10240)
_winsdk_check_microsoft_sdks_registry(v10.0 10.0.10240.0)
foreach(_win10build ${_winsdk_win10vers})
_winsdk_check_win10_kits(${_win10build})
endforeach()
endif() # vista-only and 2013+
# Included in Visual Studio 2013
# Includes the v120_xp toolset
_winsdk_check_microsoft_sdks_registry(v8.1A 8.1.51636)
if(_winsdk_vistaonly_ok AND _winsdk_msvc_not_less_1800)
# Windows Software Development Kit (SDK) for Windows 8.1
# http://msdn.microsoft.com/en-gb/windows/desktop/bg162891
_winsdk_check_microsoft_sdks_registry(v8.1 8.1.25984.0)
_winsdk_check_windows_kits_registry("Windows Kits 8.1" 8.1.25984.0 KitsRoot81)
endif() # vista-only and 2013+
if(_winsdk_vistaonly_ok)
# Included in Visual Studio 2012
_winsdk_check_microsoft_sdks_registry(v8.0A 8.0.50727)
# Microsoft Windows SDK for Windows 8 and .NET Framework 4.5
# This is the first version to also include the DirectX SDK
# http://msdn.microsoft.com/en-US/windows/desktop/hh852363.aspx
_winsdk_check_microsoft_sdks_registry(v8.0 6.2.9200.16384)
_winsdk_check_windows_kits_registry("Windows Kits 8.0" 6.2.9200.16384 KitsRoot)
endif() # vista-only
# Included with VS 2012 Update 1 or later
# Introduces v110_xp toolset
_winsdk_check_microsoft_sdks_registry(v7.1A 7.1.51106)
if(_winsdk_vistaonly_ok)
# Microsoft Windows SDK for Windows 7 and .NET Framework 4
# http://www.microsoft.com/downloads/en/details.aspx?FamilyID=6b6c21d2-2006-4afa-9702-529fa782d63b
_winsdk_check_microsoft_sdks_registry(v7.1 7.1.7600.0.30514)
endif() # vista-only
# Included with VS 2010
_winsdk_check_microsoft_sdks_registry(v7.0A 6.1.7600.16385)
# Windows SDK for Windows 7 and .NET Framework 3.5 SP1
# Works with VC9
# http://www.microsoft.com/en-us/download/details.aspx?id=18950
_winsdk_check_microsoft_sdks_registry(v7.0 6.1.7600.16385)
# Two versions call themselves "v6.1":
# Older:
# Windows Vista Update & .NET 3.0 SDK
# http://www.microsoft.com/en-us/download/details.aspx?id=14477
# Newer:
# Windows Server 2008 & .NET 3.5 SDK
# may have broken VS9SP1? they recommend v7.0 instead, or a KB...
# http://www.microsoft.com/en-us/download/details.aspx?id=24826
_winsdk_check_microsoft_sdks_registry(v6.1 6.1.6000.16384.10)
# Included in VS 2008
_winsdk_check_microsoft_sdks_registry(v6.0A 6.1.6723.1)
# Microsoft Windows Software Development Kit for Windows Vista and .NET Framework 3.0 Runtime Components
# http://blogs.msdn.com/b/stanley/archive/2006/11/08/microsoft-windows-software-development-kit-for-windows-vista-and-net-framework-3-0-runtime-components.aspx
_winsdk_check_microsoft_sdks_registry(v6.0 6.0.6000.16384)
endif()
# Let's not forget the Platform SDKs, which sometimes are useful!
if(_winsdk_msvc_greater_1200)
_winsdk_check_platformsdk_registry("Microsoft Platform SDK for Windows Server 2003 R2" "5.2.3790.2075.51" "D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1")
_winsdk_check_platformsdk_registry("Microsoft Platform SDK for Windows Server 2003 SP1" "5.2.3790.1830.15" "8F9E5EF3-A9A5-491B-A889-C58EFFECE8B3")
endif()
###
# Finally, look for "preferred" SDKs
###
if(_winsdk_msvc_greater_1310) # Newer than VS .NET/VS Toolkit 2003
# Environment variable for SDK dir
if(EXISTS "$ENV{WindowsSDKDir}" AND (NOT "$ENV{WindowsSDKDir}" STREQUAL ""))
_winsdk_conditional_append_preferred("WindowsSDKDir environment variable" "$ENV{WindowsSDKDir}")
endif()
if(_winsdk_msvc_less_1600)
# Per-user current Windows SDK for VS2005/2008
get_filename_component(_sdkdir
"[HKEY_CURRENT_USER\\Software\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]"
ABSOLUTE)
_winsdk_conditional_append_preferred("Per-user current Windows SDK" "${_sdkdir}")
# System-wide current Windows SDK for VS2005/2008
get_filename_component(_sdkdir
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]"
ABSOLUTE)
_winsdk_conditional_append_preferred("System-wide current Windows SDK" "${_sdkdir}")
endif()
endif()
function(windowssdk_name_lookup _dir _outvar)
list(FIND _win_sdk_versanddirs "${_dir}" _diridx)
math(EXPR _idx "${_diridx} - 1")
if(${_idx} GREATER -1)
list(GET _win_sdk_versanddirs ${_idx} _ret)
else()
set(_ret "NOTFOUND")
endif()
set(${_outvar} "${_ret}" PARENT_SCOPE)
endfunction()
function(windowssdk_build_lookup _dir _outvar)
list(FIND _win_sdk_buildsanddirs "${_dir}" _diridx)
math(EXPR _idx "${_diridx} - 1")
if(${_idx} GREATER -1)
list(GET _win_sdk_buildsanddirs ${_idx} _ret)
else()
set(_ret "NOTFOUND")
endif()
set(${_outvar} "${_ret}" PARENT_SCOPE)
endfunction()
# If we found something...
if(_win_sdk_dirs)
list(GET _win_sdk_dirs 0 WINDOWSSDK_LATEST_DIR)
windowssdk_name_lookup("${WINDOWSSDK_LATEST_DIR}"
WINDOWSSDK_LATEST_NAME)
set(WINDOWSSDK_DIRS ${_win_sdk_dirs})
# Fallback, in case no preference found.
set(WINDOWSSDK_PREFERRED_DIR "${WINDOWSSDK_LATEST_DIR}")
set(WINDOWSSDK_PREFERRED_NAME "${WINDOWSSDK_LATEST_NAME}")
set(WINDOWSSDK_PREFERRED_FIRST_DIRS ${WINDOWSSDK_DIRS})
set(WINDOWSSDK_FOUND_PREFERENCE OFF)
endif()
# If we found indications of a user preference...
if(_win_sdk_preferred_sdk_dirs)
list(GET _win_sdk_preferred_sdk_dirs 0 WINDOWSSDK_PREFERRED_DIR)
windowssdk_name_lookup("${WINDOWSSDK_PREFERRED_DIR}"
WINDOWSSDK_PREFERRED_NAME)
set(WINDOWSSDK_PREFERRED_FIRST_DIRS
${_win_sdk_preferred_sdk_dirs}
${_win_sdk_dirs})
list(REMOVE_DUPLICATES WINDOWSSDK_PREFERRED_FIRST_DIRS)
set(WINDOWSSDK_FOUND_PREFERENCE ON)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(WindowsSDK
"No compatible version of the Windows SDK or Platform SDK found."
WINDOWSSDK_DIRS)
if(WINDOWSSDK_FOUND)
# Internal: Architecture-appropriate library directory names.
if("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "ARM")
if(CMAKE_SIZEOF_VOID_P MATCHES "8")
# Only supported in Win10 SDK and up.
set(_winsdk_arch8 arm64) # what the WDK for Win8+ calls this architecture
else()
set(_winsdk_archbare /arm) # what the architecture used to be called in oldest SDKs
set(_winsdk_arch arm) # what the architecture used to be called
set(_winsdk_arch8 arm) # what the WDK for Win8+ calls this architecture
endif()
else()
if(CMAKE_SIZEOF_VOID_P MATCHES "8")
set(_winsdk_archbare /x64) # what the architecture used to be called in oldest SDKs
set(_winsdk_arch amd64) # what the architecture used to be called
set(_winsdk_arch8 x64) # what the WDK for Win8+ calls this architecture
else()
set(_winsdk_archbare ) # what the architecture used to be called in oldest SDKs
set(_winsdk_arch i386) # what the architecture used to be called
set(_winsdk_arch8 x86) # what the WDK for Win8+ calls this architecture
endif()
endif()
function(get_windowssdk_from_component _component _var)
get_filename_component(_component "${_component}" ABSOLUTE)
file(TO_CMAKE_PATH "${_component}" _component)
foreach(_sdkdir ${WINDOWSSDK_DIRS})
get_filename_component(_sdkdir "${_sdkdir}" ABSOLUTE)
string(LENGTH "${_sdkdir}" _sdklen)
file(RELATIVE_PATH _rel "${_sdkdir}" "${_component}")
# If we don't have any "parent directory" items...
if(NOT "${_rel}" MATCHES "[.][.]")
set(${_var} "${_sdkdir}" PARENT_SCOPE)
return()
endif()
endforeach()
# Fail.
set(${_var} "NOTFOUND" PARENT_SCOPE)
endfunction()
function(get_windowssdk_library_dirs _winsdk_dir _var)
set(_dirs)
set(_suffixes
"lib${_winsdk_archbare}" # SDKs like 7.1A
"lib/${_winsdk_arch}" # just because some SDKs have x86 dir and root dir
"lib/w2k/${_winsdk_arch}" # Win2k min requirement
"lib/wxp/${_winsdk_arch}" # WinXP min requirement
"lib/wnet/${_winsdk_arch}" # Win Server 2003 min requirement
"lib/wlh/${_winsdk_arch}"
"lib/wlh/um/${_winsdk_arch8}" # Win Vista ("Long Horn") min requirement
"lib/win7/${_winsdk_arch}"
"lib/win7/um/${_winsdk_arch8}" # Win 7 min requirement
)
foreach(_ver
wlh # Win Vista ("Long Horn") min requirement
win7 # Win 7 min requirement
win8 # Win 8 min requirement
winv6.3 # Win 8.1 min requirement
)
list(APPEND _suffixes
"lib/${_ver}/${_winsdk_arch}"
"lib/${_ver}/um/${_winsdk_arch8}"
"lib/${_ver}/km/${_winsdk_arch8}"
)
endforeach()
# Look for WDF libraries in Win10+ SDK
foreach(_mode umdf kmdf)
file(GLOB _wdfdirs RELATIVE "${_winsdk_dir}" "${_winsdk_dir}/lib/wdf/${_mode}/${_winsdk_arch8}/*")
if(_wdfdirs)
list(APPEND _suffixes ${_wdfdirs})
endif()
endforeach()
# Look in each Win10+ SDK version for the components
foreach(_win10ver ${_winsdk_win10vers})
foreach(_component um km ucrt mmos)
list(APPEND _suffixes "lib/${_win10ver}/${_component}/${_winsdk_arch8}")
endforeach()
endforeach()
foreach(_suffix ${_suffixes})
# Check to see if a library actually exists here.
file(GLOB _libs "${_winsdk_dir}/${_suffix}/*.lib")
if(_libs)
list(APPEND _dirs "${_winsdk_dir}/${_suffix}")
endif()
endforeach()
if("${_dirs}" STREQUAL "")
set(_dirs NOTFOUND)
else()
list(REMOVE_DUPLICATES _dirs)
endif()
set(${_var} ${_dirs} PARENT_SCOPE)
endfunction()
function(get_windowssdk_include_dirs _winsdk_dir _var)
set(_dirs)
set(_subdirs shared um winrt km wdf mmos ucrt)
set(_suffixes Include)
foreach(_dir ${_subdirs})
list(APPEND _suffixes "Include/${_dir}")
endforeach()
foreach(_ver ${_winsdk_win10vers})
foreach(_dir ${_subdirs})
list(APPEND _suffixes "Include/${_ver}/${_dir}")
endforeach()
endforeach()
foreach(_suffix ${_suffixes})
# Check to see if a header file actually exists here.
file(GLOB _headers "${_winsdk_dir}/${_suffix}/*.h")
if(_headers)
list(APPEND _dirs "${_winsdk_dir}/${_suffix}")
endif()
endforeach()
if("${_dirs}" STREQUAL "")
set(_dirs NOTFOUND)
else()
list(REMOVE_DUPLICATES _dirs)
endif()
set(${_var} ${_dirs} PARENT_SCOPE)
endfunction()
function(get_windowssdk_library_dirs_multiple _var)
set(_dirs)
foreach(_sdkdir ${ARGN})
get_windowssdk_library_dirs("${_sdkdir}" _current_sdk_libdirs)
if(_current_sdk_libdirs)
list(APPEND _dirs ${_current_sdk_libdirs})
endif()
endforeach()
if("${_dirs}" STREQUAL "")
set(_dirs NOTFOUND)
else()
list(REMOVE_DUPLICATES _dirs)
endif()
set(${_var} ${_dirs} PARENT_SCOPE)
endfunction()
function(get_windowssdk_include_dirs_multiple _var)
set(_dirs)
foreach(_sdkdir ${ARGN})
get_windowssdk_include_dirs("${_sdkdir}" _current_sdk_incdirs)
if(_current_sdk_libdirs)
list(APPEND _dirs ${_current_sdk_incdirs})
endif()
endforeach()
if("${_dirs}" STREQUAL "")
set(_dirs NOTFOUND)
else()
list(REMOVE_DUPLICATES _dirs)
endif()
set(${_var} ${_dirs} PARENT_SCOPE)
endfunction()
endif()
View File
+62
View File
@@ -0,0 +1,62 @@
#include "config.h"
#include "almalloc.h"
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
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(((ptrdiff_t)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
}
+21
View File
@@ -0,0 +1,21 @@
#ifndef AL_MALLOC_H
#define AL_MALLOC_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Minimum alignment required by posix_memalign. */
#define DEF_ALIGN sizeof(void*)
void *al_malloc(size_t alignment, size_t size);
void *al_calloc(size_t alignment, size_t size);
void al_free(void *ptr);
#ifdef __cplusplus
}
#endif
#endif /* AL_MALLOC_H */
-3
View File
@@ -8,6 +8,3 @@ extern inline void InitRef(RefCount *ptr, uint value);
extern inline uint ReadRef(RefCount *ptr);
extern inline uint IncrementRef(RefCount *ptr);
extern inline uint DecrementRef(RefCount *ptr);
extern inline int ExchangeInt(volatile int *ptr, int newval);
extern inline void *ExchangePtr(XchgPtr *ptr, void *newval);
+425
View File
@@ -0,0 +1,425 @@
#ifndef AL_ATOMIC_H
#define AL_ATOMIC_H
#include "static_assert.h"
#include "bool.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Atomics using C11 */
#ifdef HAVE_C11_ATOMIC
#include <stdatomic.h>
#define almemory_order memory_order
#define almemory_order_relaxed memory_order_relaxed
#define almemory_order_consume memory_order_consume
#define almemory_order_acquire memory_order_acquire
#define almemory_order_release memory_order_release
#define almemory_order_acq_rel memory_order_acq_rel
#define almemory_order_seq_cst memory_order_seq_cst
#define ATOMIC(T) T _Atomic
#define ATOMIC_FLAG atomic_flag
#define ATOMIC_INIT atomic_init
#define ATOMIC_INIT_STATIC ATOMIC_VAR_INIT
/*#define ATOMIC_FLAG_INIT ATOMIC_FLAG_INIT*/
#define ATOMIC_LOAD atomic_load_explicit
#define ATOMIC_STORE atomic_store_explicit
#define ATOMIC_ADD atomic_fetch_add_explicit
#define ATOMIC_SUB atomic_fetch_sub_explicit
#define ATOMIC_EXCHANGE atomic_exchange_explicit
#define ATOMIC_COMPARE_EXCHANGE_STRONG atomic_compare_exchange_strong_explicit
#define ATOMIC_COMPARE_EXCHANGE_WEAK atomic_compare_exchange_weak_explicit
#define ATOMIC_FLAG_TEST_AND_SET atomic_flag_test_and_set_explicit
#define ATOMIC_FLAG_CLEAR atomic_flag_clear_explicit
#define ATOMIC_THREAD_FENCE atomic_thread_fence
/* Atomics using GCC intrinsics */
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) && !defined(__QNXNTO__)
enum almemory_order {
almemory_order_relaxed,
almemory_order_consume,
almemory_order_acquire,
almemory_order_release,
almemory_order_acq_rel,
almemory_order_seq_cst
};
#define ATOMIC(T) struct { T volatile value; }
#define ATOMIC_FLAG ATOMIC(int)
#define ATOMIC_INIT(_val, _newval) do { (_val)->value = (_newval); } while(0)
#define ATOMIC_INIT_STATIC(_newval) {(_newval)}
#define ATOMIC_FLAG_INIT ATOMIC_INIT_STATIC(0)
#define ATOMIC_LOAD(_val, _MO) __extension__({ \
__typeof((_val)->value) _r = (_val)->value; \
__asm__ __volatile__("" ::: "memory"); \
_r; \
})
#define ATOMIC_STORE(_val, _newval, _MO) do { \
__asm__ __volatile__("" ::: "memory"); \
(_val)->value = (_newval); \
} while(0)
#define ATOMIC_ADD(_val, _incr, _MO) __sync_fetch_and_add(&(_val)->value, (_incr))
#define ATOMIC_SUB(_val, _decr, _MO) __sync_fetch_and_sub(&(_val)->value, (_decr))
#define ATOMIC_EXCHANGE(_val, _newval, _MO) __extension__({ \
__asm__ __volatile__("" ::: "memory"); \
__sync_lock_test_and_set(&(_val)->value, (_newval)); \
})
#define ATOMIC_COMPARE_EXCHANGE_STRONG(_val, _oldval, _newval, _MO1, _MO2) __extension__({ \
__typeof(*(_oldval)) _o = *(_oldval); \
*(_oldval) = __sync_val_compare_and_swap(&(_val)->value, _o, (_newval)); \
*(_oldval) == _o; \
})
#define ATOMIC_FLAG_TEST_AND_SET(_val, _MO) __extension__({ \
__asm__ __volatile__("" ::: "memory"); \
__sync_lock_test_and_set(&(_val)->value, 1); \
})
#define ATOMIC_FLAG_CLEAR(_val, _MO) __extension__({ \
__sync_lock_release(&(_val)->value); \
__asm__ __volatile__("" ::: "memory"); \
})
#define ATOMIC_THREAD_FENCE(order) do { \
enum { must_be_constant = (order) }; \
const int _o = must_be_constant; \
if(_o > almemory_order_relaxed) \
__asm__ __volatile__("" ::: "memory"); \
} while(0)
/* Atomics using x86/x86-64 GCC inline assembly */
#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
#define WRAP_ADD(S, ret, dest, incr) __asm__ __volatile__( \
"lock; xadd"S" %0,(%1)" \
: "=r" (ret) \
: "r" (dest), "0" (incr) \
: "memory" \
)
#define WRAP_SUB(S, ret, dest, decr) __asm__ __volatile__( \
"lock; xadd"S" %0,(%1)" \
: "=r" (ret) \
: "r" (dest), "0" (-(decr)) \
: "memory" \
)
#define WRAP_XCHG(S, ret, dest, newval) __asm__ __volatile__( \
"lock; xchg"S" %0,(%1)" \
: "=r" (ret) \
: "r" (dest), "0" (newval) \
: "memory" \
)
#define WRAP_CMPXCHG(S, ret, dest, oldval, newval) __asm__ __volatile__( \
"lock; cmpxchg"S" %2,(%1)" \
: "=a" (ret) \
: "r" (dest), "r" (newval), "0" (oldval) \
: "memory" \
)
enum almemory_order {
almemory_order_relaxed,
almemory_order_consume,
almemory_order_acquire,
almemory_order_release,
almemory_order_acq_rel,
almemory_order_seq_cst
};
#define ATOMIC(T) struct { T volatile value; }
#define ATOMIC_INIT(_val, _newval) do { (_val)->value = (_newval); } while(0)
#define ATOMIC_INIT_STATIC(_newval) {(_newval)}
#define ATOMIC_LOAD(_val, _MO) __extension__({ \
__typeof((_val)->value) _r = (_val)->value; \
__asm__ __volatile__("" ::: "memory"); \
_r; \
})
#define ATOMIC_STORE(_val, _newval, _MO) do { \
__asm__ __volatile__("" ::: "memory"); \
(_val)->value = (_newval); \
} while(0)
#define ATOMIC_ADD(_val, _incr, _MO) __extension__({ \
static_assert(sizeof((_val)->value)==4 || sizeof((_val)->value)==8, "Unsupported size!"); \
__typeof((_val)->value) _r; \
if(sizeof((_val)->value) == 4) WRAP_ADD("l", _r, &(_val)->value, _incr); \
else if(sizeof((_val)->value) == 8) WRAP_ADD("q", _r, &(_val)->value, _incr); \
_r; \
})
#define ATOMIC_SUB(_val, _decr, _MO) __extension__({ \
static_assert(sizeof((_val)->value)==4 || sizeof((_val)->value)==8, "Unsupported size!"); \
__typeof((_val)->value) _r; \
if(sizeof((_val)->value) == 4) WRAP_SUB("l", _r, &(_val)->value, _decr); \
else if(sizeof((_val)->value) == 8) WRAP_SUB("q", _r, &(_val)->value, _decr); \
_r; \
})
#define ATOMIC_EXCHANGE(_val, _newval, _MO) __extension__({ \
__typeof((_val)->value) _r; \
if(sizeof((_val)->value) == 4) WRAP_XCHG("l", _r, &(_val)->value, (_newval)); \
else if(sizeof((_val)->value) == 8) WRAP_XCHG("q", _r, &(_val)->value, (_newval)); \
_r; \
})
#define ATOMIC_COMPARE_EXCHANGE_STRONG(_val, _oldval, _newval, _MO1, _MO2) __extension__({ \
__typeof(*(_oldval)) _old = *(_oldval); \
if(sizeof((_val)->value) == 4) WRAP_CMPXCHG("l", *(_oldval), &(_val)->value, _old, (_newval)); \
else if(sizeof((_val)->value) == 8) WRAP_CMPXCHG("q", *(_oldval), &(_val)->value, _old, (_newval)); \
*(_oldval) == _old; \
})
#define ATOMIC_EXCHANGE_PTR(_val, _newval, _MO) __extension__({ \
void *_r; \
if(sizeof(void*) == 4) WRAP_XCHG("l", _r, &(_val)->value, (_newval)); \
else if(sizeof(void*) == 8) WRAP_XCHG("q", _r, &(_val)->value, (_newval));\
_r; \
})
#define ATOMIC_COMPARE_EXCHANGE_PTR_STRONG(_val, _oldval, _newval, _MO1, _MO2) __extension__({ \
void *_old = *(_oldval); \
if(sizeof(void*) == 4) WRAP_CMPXCHG("l", *(_oldval), &(_val)->value, _old, (_newval)); \
else if(sizeof(void*) == 8) WRAP_CMPXCHG("q", *(_oldval), &(_val)->value, _old, (_newval)); \
*(_oldval) == _old; \
})
#define ATOMIC_THREAD_FENCE(order) do { \
enum { must_be_constant = (order) }; \
const int _o = must_be_constant; \
if(_o > almemory_order_relaxed) \
__asm__ __volatile__("" ::: "memory"); \
} while(0)
/* Atomics using Windows methods */
#elif defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
/* NOTE: This mess is *extremely* touchy. It lacks quite a bit of safety
* checking due to the lack of multi-statement expressions, typeof(), and C99
* compound literals. It is incapable of properly exchanging floats, which get
* casted to LONG/int, and could cast away potential warnings.
*
* Unfortunately, it's the only semi-safe way that doesn't rely on C99 (because
* MSVC).
*/
inline LONG AtomicAdd32(volatile LONG *dest, LONG incr)
{
return InterlockedExchangeAdd(dest, incr);
}
inline LONGLONG AtomicAdd64(volatile LONGLONG *dest, LONGLONG incr)
{
return InterlockedExchangeAdd64(dest, incr);
}
inline LONG AtomicSub32(volatile LONG *dest, LONG decr)
{
return InterlockedExchangeAdd(dest, -decr);
}
inline LONGLONG AtomicSub64(volatile LONGLONG *dest, LONGLONG decr)
{
return InterlockedExchangeAdd64(dest, -decr);
}
inline LONG AtomicSwap32(volatile LONG *dest, LONG newval)
{
return InterlockedExchange(dest, newval);
}
inline LONGLONG AtomicSwap64(volatile LONGLONG *dest, LONGLONG newval)
{
return InterlockedExchange64(dest, newval);
}
inline void *AtomicSwapPtr(void *volatile *dest, void *newval)
{
return InterlockedExchangePointer(dest, newval);
}
inline bool CompareAndSwap32(volatile LONG *dest, LONG newval, LONG *oldval)
{
LONG old = *oldval;
*oldval = InterlockedCompareExchange(dest, newval, *oldval);
return old == *oldval;
}
inline bool CompareAndSwap64(volatile LONGLONG *dest, LONGLONG newval, LONGLONG *oldval)
{
LONGLONG old = *oldval;
*oldval = InterlockedCompareExchange64(dest, newval, *oldval);
return old == *oldval;
}
inline bool CompareAndSwapPtr(void *volatile *dest, void *newval, void **oldval)
{
void *old = *oldval;
*oldval = InterlockedCompareExchangePointer(dest, newval, *oldval);
return old == *oldval;
}
#define WRAP_ADDSUB(T, _func, _ptr, _amnt) _func((T volatile*)(_ptr), (_amnt))
#define WRAP_XCHG(T, _func, _ptr, _newval) _func((T volatile*)(_ptr), (_newval))
#define WRAP_CMPXCHG(T, _func, _ptr, _newval, _oldval) _func((T volatile*)(_ptr), (_newval), (T*)(_oldval))
enum almemory_order {
almemory_order_relaxed,
almemory_order_consume,
almemory_order_acquire,
almemory_order_release,
almemory_order_acq_rel,
almemory_order_seq_cst
};
#define ATOMIC(T) struct { T volatile value; }
#define ATOMIC_INIT(_val, _newval) do { (_val)->value = (_newval); } while(0)
#define ATOMIC_INIT_STATIC(_newval) {(_newval)}
#define ATOMIC_LOAD(_val, _MO) ((_val)->value)
#define ATOMIC_STORE(_val, _newval, _MO) do { \
(_val)->value = (_newval); \
} while(0)
int _al_invalid_atomic_size(); /* not defined */
#define ATOMIC_ADD(_val, _incr, _MO) \
((sizeof((_val)->value)==4) ? WRAP_ADDSUB(LONG, AtomicAdd32, &(_val)->value, (_incr)) : \
(sizeof((_val)->value)==8) ? WRAP_ADDSUB(LONGLONG, AtomicAdd64, &(_val)->value, (_incr)) : \
_al_invalid_atomic_size())
#define ATOMIC_SUB(_val, _decr, _MO) \
((sizeof((_val)->value)==4) ? WRAP_ADDSUB(LONG, AtomicSub32, &(_val)->value, (_decr)) : \
(sizeof((_val)->value)==8) ? WRAP_ADDSUB(LONGLONG, AtomicSub64, &(_val)->value, (_decr)) : \
_al_invalid_atomic_size())
#define ATOMIC_EXCHANGE(_val, _newval, _MO) \
((sizeof((_val)->value)==4) ? WRAP_XCHG(LONG, AtomicSwap32, &(_val)->value, (_newval)) : \
(sizeof((_val)->value)==8) ? WRAP_XCHG(LONGLONG, AtomicSwap64, &(_val)->value, (_newval)) : \
(LONG)_al_invalid_atomic_size())
#define ATOMIC_COMPARE_EXCHANGE_STRONG(_val, _oldval, _newval, _MO1, _MO2) \
((sizeof((_val)->value)==4) ? WRAP_CMPXCHG(LONG, CompareAndSwap32, &(_val)->value, (_newval), (_oldval)) : \
(sizeof((_val)->value)==8) ? WRAP_CMPXCHG(LONGLONG, CompareAndSwap64, &(_val)->value, (_newval), (_oldval)) : \
(bool)_al_invalid_atomic_size())
#define ATOMIC_EXCHANGE_PTR(_val, _newval, _MO) \
((sizeof((_val)->value)==sizeof(void*)) ? AtomicSwapPtr((void*volatile*)&(_val)->value, (_newval)) : \
(void*)_al_invalid_atomic_size())
#define ATOMIC_COMPARE_EXCHANGE_PTR_STRONG(_val, _oldval, _newval, _MO1, _MO2)\
((sizeof((_val)->value)==sizeof(void*)) ? CompareAndSwapPtr((void*volatile*)&(_val)->value, (_newval), (void**)(_oldval)) : \
(bool)_al_invalid_atomic_size())
#define ATOMIC_THREAD_FENCE(order) do { \
enum { must_be_constant = (order) }; \
const int _o = must_be_constant; \
if(_o > almemory_order_relaxed) \
_ReadWriteBarrier(); \
} while(0)
#else
#error "No atomic functions available on this platform!"
#define ATOMIC(T) T
#define ATOMIC_INIT(_val, _newval) ((void)0)
#define ATOMIC_INIT_STATIC(_newval) (0)
#define ATOMIC_LOAD(...) (0)
#define ATOMIC_STORE(...) ((void)0)
#define ATOMIC_ADD(...) (0)
#define ATOMIC_SUB(...) (0)
#define ATOMIC_EXCHANGE(...) (0)
#define ATOMIC_COMPARE_EXCHANGE_STRONG(...) (0)
#define ATOMIC_THREAD_FENCE(...) ((void)0)
#endif
/* If no PTR xchg variants are provided, the normal ones can handle it. */
#ifndef ATOMIC_EXCHANGE_PTR
#define ATOMIC_EXCHANGE_PTR ATOMIC_EXCHANGE
#define ATOMIC_COMPARE_EXCHANGE_PTR_STRONG ATOMIC_COMPARE_EXCHANGE_STRONG
#define ATOMIC_COMPARE_EXCHANGE_PTR_WEAK ATOMIC_COMPARE_EXCHANGE_WEAK
#endif
/* If no weak cmpxchg is provided (not all systems will have one), substitute a
* strong cmpxchg. */
#ifndef ATOMIC_COMPARE_EXCHANGE_WEAK
#define ATOMIC_COMPARE_EXCHANGE_WEAK ATOMIC_COMPARE_EXCHANGE_STRONG
#endif
#ifndef ATOMIC_COMPARE_EXCHANGE_PTR_WEAK
#define ATOMIC_COMPARE_EXCHANGE_PTR_WEAK ATOMIC_COMPARE_EXCHANGE_PTR_STRONG
#endif
/* If no ATOMIC_FLAG is defined, simulate one with an atomic int using exchange
* and store ops.
*/
#ifndef ATOMIC_FLAG
#define ATOMIC_FLAG ATOMIC(int)
#define ATOMIC_FLAG_INIT ATOMIC_INIT_STATIC(0)
#define ATOMIC_FLAG_TEST_AND_SET(_val, _MO) ATOMIC_EXCHANGE(_val, 1, _MO)
#define ATOMIC_FLAG_CLEAR(_val, _MO) ATOMIC_STORE(_val, 0, _MO)
#endif
#define ATOMIC_LOAD_SEQ(_val) ATOMIC_LOAD(_val, almemory_order_seq_cst)
#define ATOMIC_STORE_SEQ(_val, _newval) ATOMIC_STORE(_val, _newval, almemory_order_seq_cst)
#define ATOMIC_ADD_SEQ(_val, _incr) ATOMIC_ADD(_val, _incr, almemory_order_seq_cst)
#define ATOMIC_SUB_SEQ(_val, _decr) ATOMIC_SUB(_val, _decr, almemory_order_seq_cst)
#define ATOMIC_EXCHANGE_SEQ(_val, _newval) ATOMIC_EXCHANGE(_val, _newval, almemory_order_seq_cst)
#define ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(_val, _oldval, _newval) \
ATOMIC_COMPARE_EXCHANGE_STRONG(_val, _oldval, _newval, almemory_order_seq_cst, almemory_order_seq_cst)
#define ATOMIC_COMPARE_EXCHANGE_WEAK_SEQ(_val, _oldval, _newval) \
ATOMIC_COMPARE_EXCHANGE_WEAK(_val, _oldval, _newval, almemory_order_seq_cst, almemory_order_seq_cst)
#define ATOMIC_EXCHANGE_PTR_SEQ(_val, _newval) ATOMIC_EXCHANGE_PTR(_val, _newval, almemory_order_seq_cst)
#define ATOMIC_COMPARE_EXCHANGE_PTR_STRONG_SEQ(_val, _oldval, _newval) \
ATOMIC_COMPARE_EXCHANGE_PTR_STRONG(_val, _oldval, _newval, almemory_order_seq_cst, almemory_order_seq_cst)
#define ATOMIC_COMPARE_EXCHANGE_PTR_WEAK_SEQ(_val, _oldval, _newval) \
ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(_val, _oldval, _newval, almemory_order_seq_cst, almemory_order_seq_cst)
typedef unsigned int uint;
typedef ATOMIC(uint) RefCount;
inline void InitRef(RefCount *ptr, uint value)
{ ATOMIC_INIT(ptr, value); }
inline uint ReadRef(RefCount *ptr)
{ return ATOMIC_LOAD_SEQ(ptr); }
inline uint IncrementRef(RefCount *ptr)
{ return ATOMIC_ADD_SEQ(ptr, 1)+1; }
inline uint DecrementRef(RefCount *ptr)
{ return ATOMIC_SUB_SEQ(ptr, 1)-1; }
/* WARNING: A livelock is theoretically possible if another thread keeps
* changing the head without giving this a chance to actually swap in the new
* one (practically impossible with this little code, but...).
*/
#define ATOMIC_REPLACE_HEAD(T, _head, _entry) do { \
T _first = ATOMIC_LOAD(_head, almemory_order_acquire); \
do { \
ATOMIC_STORE(&(_entry)->next, _first, almemory_order_relaxed); \
} while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(_head, &_first, _entry, \
almemory_order_acq_rel, almemory_order_acquire) == 0); \
} while(0)
#ifdef __cplusplus
}
#endif
#endif /* AL_ATOMIC_H */
View File
+35
View File
@@ -0,0 +1,35 @@
#ifndef AL_MATH_DEFS_H
#define AL_MATH_DEFS_H
#include <math.h>
#ifdef HAVE_FLOAT_H
#include <float.h>
#endif
#define F_PI (3.14159265358979323846f)
#define F_PI_2 (1.57079632679489661923f)
#define F_TAU (6.28318530717958647692f)
#ifndef FLT_EPSILON
#define FLT_EPSILON (1.19209290e-07f)
#endif
#ifndef HUGE_VALF
static const union msvc_inf_hack {
unsigned char b[4];
float f;
} msvc_inf_union = {{ 0x00, 0x00, 0x80, 0x7F }};
#define HUGE_VALF (msvc_inf_union.f)
#endif
#ifndef HAVE_LOG2F
static inline float log2f(float f)
{
return logf(f) / logf(2.0f);
}
#endif
#define DEG2RAD(x) ((float)(x) * (F_PI/180.0f))
#define RAD2DEG(x) ((float)(x) * (180.0f/F_PI))
#endif /* AL_MATH_DEFS_H */
+11 -9
View File
@@ -11,26 +11,27 @@
/* A simple spinlock. Yield the thread while the given integer is set by
* another. Could probably be improved... */
#define LOCK(l) do { \
while(ATOMIC_EXCHANGE(int, &(l), true) == true) \
while(ATOMIC_FLAG_TEST_AND_SET(&(l), almemory_order_acq_rel) == true) \
althrd_yield(); \
} while(0)
#define UNLOCK(l) ATOMIC_STORE(&(l), false)
#define UNLOCK(l) ATOMIC_FLAG_CLEAR(&(l), almemory_order_release)
void RWLockInit(RWLock *lock)
{
InitRef(&lock->read_count, 0);
InitRef(&lock->write_count, 0);
ATOMIC_INIT(&lock->read_lock, false);
ATOMIC_INIT(&lock->read_entry_lock, false);
ATOMIC_INIT(&lock->write_lock, false);
ATOMIC_FLAG_CLEAR(&lock->read_lock, almemory_order_relaxed);
ATOMIC_FLAG_CLEAR(&lock->read_entry_lock, almemory_order_relaxed);
ATOMIC_FLAG_CLEAR(&lock->write_lock, almemory_order_relaxed);
}
void ReadLock(RWLock *lock)
{
LOCK(lock->read_entry_lock);
LOCK(lock->read_lock);
if(IncrementRef(&lock->read_count) == 1)
/* NOTE: ATOMIC_ADD returns the *old* value! */
if(ATOMIC_ADD(&lock->read_count, 1, almemory_order_acq_rel) == 0)
LOCK(lock->write_lock);
UNLOCK(lock->read_lock);
UNLOCK(lock->read_entry_lock);
@@ -38,13 +39,14 @@ void ReadLock(RWLock *lock)
void ReadUnlock(RWLock *lock)
{
if(DecrementRef(&lock->read_count) == 0)
/* NOTE: ATOMIC_SUB returns the *old* value! */
if(ATOMIC_SUB(&lock->read_count, 1, almemory_order_acq_rel) == 1)
UNLOCK(lock->write_lock);
}
void WriteLock(RWLock *lock)
{
if(IncrementRef(&lock->write_count) == 1)
if(ATOMIC_ADD(&lock->write_count, 1, almemory_order_acq_rel) == 0)
LOCK(lock->read_lock);
LOCK(lock->write_lock);
}
@@ -52,6 +54,6 @@ void WriteLock(RWLock *lock)
void WriteUnlock(RWLock *lock)
{
UNLOCK(lock->write_lock);
if(DecrementRef(&lock->write_count) == 0)
if(ATOMIC_SUB(&lock->write_count, 1, almemory_order_acq_rel) == 1)
UNLOCK(lock->read_lock);
}

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