Compare commits

...

1355 Commits

Author SHA1 Message Date
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
Chris Robinson 0189b923a2 Release 1.17.2 2016-01-24 19:12:39 -08:00
Chris Robinson 4fb2526779 Fix "signed and unsigned type in conditional expression" warning 2016-01-24 02:29:26 -08:00
Chris Robinson 5761aa9400 Remove unneeded check for io.h 2016-01-24 01:51:43 -08:00
Chris Robinson ddc885604d Clear SSE3 switch before testing if one's recognized 2016-01-24 01:35:09 -08:00
Chris Robinson 7de8db2960 Replace multiple fputc calls with one fwrite 2016-01-23 21:47:43 -08:00
Chris Robinson 5d552980a4 Move a couple extern inline declarations to the others 2016-01-23 18:19:19 -08:00
Chris Robinson 94816d0073 Reorder filterstate properties 2016-01-23 03:38:15 -08:00
Chris Robinson 352d9afd64 Inline a couple filterstate methods 2016-01-23 01:22:08 -08:00
Chris Robinson 87b6c30932 Reorder some cmake checks 2016-01-21 21:51:20 -08:00
Chris Robinson fb599b5b83 Lower the _POSIX_C_SOURCE and _XOPEN_SOURCE version requirements 2016-01-21 17:18:08 -08:00
Chris Robinson 26f5d385d5 Only set cmake policy CMP0042 if it's valid 2016-01-21 06:18:54 -08:00
Chris Robinson 0a9e9858f9 Always define _XOPEN_SOURCE if _POSIX_C_SOURCE is too 2016-01-21 06:08:18 -08:00
Chris Robinson 8f26ad4ba2 Avoid using scandir/alphasort
They require POSIX 2008, which is a bit too "new" for my current liking. We can
do well enough with opendir/readdir/closedir and qsort.
2016-01-21 04:42:22 -08:00
Chris Robinson bb0ac26ce1 Explicit check if pthread_setname_np accepts only one parameter 2016-01-21 02:33:45 -08:00
Chris Robinson 9c05a16c9a Fix for systems that don't have strnlen 2016-01-21 02:09:32 -08:00
Chris Robinson bbd2876afb Improve the OSS enumeration code 2016-01-21 01:05:29 -08:00
Chris Robinson 3395b361fa Silence a CMake warning on OSX about MACOSX_RPATH 2016-01-20 18:42:23 -08:00
Chris Robinson 0eb18e9711 Update ChangeLog 2016-01-17 22:38:27 -08:00
Chris Robinson 0bb838bbaf Add an option to static-link libgcc
Mainly just for MinGW to make OpenAL32.dll not rely on libgcc_s_sjlj-1.dll.
2016-01-17 15:45:42 -08:00
kcat 1930ee3e5f Merge pull request #29 from olesalscheider/master
Allow to override the share install dir
2015-12-20 03:26:20 -08:00
Chris Robinson 3dc5f0bf76 Merge branch 'master' of github.com:/kcat/openal-soft 2015-12-19 14:26:00 -08:00
kcat 78ca33a0c7 Merge pull request #27 from kdhp/master
Added device enumeration support for OSSv4
2015-12-19 14:22:36 -08:00
Niels Ole Salscheider d93398a361 Allow to override the share install dir
This is needed for multiarch layouts where the prefix is /usr/${host} but
where arch-independet files are installed to /usr/share.
2015-12-19 17:54:47 +01:00
Keegan Drake H.P 5a73aa2502 Added a check for an empty path 2015-12-17 20:05:21 -06:00
Keegan Drake H.P df2ee7ba87 Add device enumeration support for OSSv4 2015-12-17 19:20:08 -06:00
Chris Robinson bce20d1f6b Don't overwrite the output with the dedicated effect 2015-12-12 13:53:17 -08:00
Chris Robinson abd568f9d8 Build on Linux and OSX with Travis 2015-12-11 10:34:38 -08:00
Chris Robinson 2850719ea2 Add a .travis.yml for travis-ci 2015-12-11 10:13:02 -08:00
Chris Robinson e034afe107 Check if _XOPEN_SOURCE needs to be set 2015-12-11 09:41:46 -08:00
Chris Robinson 01e43e5d4d Add includes to work around broken GNU headers and POSIX-2001 2015-12-11 09:24:40 -08:00
Chris Robinson d7b2bba2e6 Add missing closing parenthesis 2015-12-11 07:35:39 -08:00
Chris Robinson 13bf636a34 Use Apple's pthread_setname_np before GNU's 2015-12-11 07:26:27 -08:00
Chris Robinson 14df326876 Release 1.17.1 2015-12-10 04:45:30 -08:00
Chris Robinson e3d2df58b3 Update changelog 2015-12-10 04:01:15 -08:00
Chris Robinson 4612378593 Update alsoft-config tooltip for HRTF tables 2015-12-10 01:58:08 -08:00
Chris Robinson 26f8676b9b Fix slashes on the local path on Windows 2015-12-08 19:43:00 -08:00
Chris Robinson f8a4ef6f51 Use the ALSOFT_LOCAL_PATH env var instead of the CWD 2015-12-08 19:17:59 -08:00
Chris Robinson c08e975aa5 Update changelog 2015-12-06 06:36:00 -08:00
Chris Robinson 455d5f48c0 Use %s.mhr for the default hrtf table list 2015-12-05 17:49:05 -08:00
Chris Robinson 21998924d8 Support %s as a string matching marker 2015-12-05 17:45:56 -08:00
Chris Robinson 48447e2864 Also install the HRTF example 2015-12-04 19:51:48 -08:00
Chris Robinson 482372577e Avoid using usleep in the examples
We already have an al_nssleep wrapper in the common lib we can use.
2015-12-04 19:09:48 -08:00
Chris Robinson 4e6acfa0d5 Only set _POSIX_C_SOURCE if needed 2015-12-04 18:52:46 -08:00
Chris Robinson 675ec99d33 Update changelog 2015-12-04 16:00:53 -08:00
Chris Robinson e45ff47361 Reformat Windows device name dressing 2015-12-04 15:52:43 -08:00
Chris Robinson 63567526b0 Better handle inexact ALSA period counts 2015-11-23 16:50:31 -08:00
Chris Robinson c8a3e51296 Fix the allow-resampler check for ALSA 2015-11-23 16:29:32 -08:00
kcat 6bb4bdfa4c Merge pull request #25 from v4hn/fix-jack-libraries
fix-jack-libraries
2015-11-20 12:39:41 -08:00
v4hn 8ac8830478 include correct libraries in case jack support is requested and found
It just doesn't make sense to add pulseaudio libraries here...
Also it breaks in case jack is requested, but pulseaudio is disabled :)
2015-11-20 14:33:44 +01:00
kcat b2b7be6561 Merge pull request #24 from slime73/master
CoreAudio: replace deprecated Carbon API calls with AudioComponent APIs
2015-11-13 17:05:12 -08:00
Alex Szpakowski 21c84bcd96 Replace deprecated Carbon API calls with modern AudioComponent APIs in the CoreAudio backend.
This prevents a deprecation notice from being output to stdout when alcOpenDevice is called in Mac OS X 10.11.

The new API calls require Mac OS X 10.6 or newer.
2015-11-13 20:11:12 -04:00
Chris Robinson f903a7aa64 Release 1.17.0 2015-11-12 07:40:17 -08:00
Chris Robinson 5d039309b3 Use doubles for the constructed listener matrix
This helps the stability of transforms to local space for sources that are at
or near the listener. With a single-precision matrix, even FLT_EPSILON might
not be enough to detect matching positions.
2015-11-11 08:19:33 -08:00
Chris Robinson ff62d22d31 Move the bsincTable to a separate file 2015-11-10 18:48:30 -08:00
Chris Robinson ffcdcbd5fa Update the bsinc table
Precision is increased to cover the full 32-bit float range.
2015-11-10 18:40:33 -08:00
Chris Robinson a37cda0214 Add a tool to generate the bsinc tables 2015-11-10 18:40:11 -08:00
Chris Robinson 10bcda27a9 Remove autowah from the excludefx list
The autowah effect hasn't been enabled for some time due to issues with it
breaking output, and doesn't seem likely to get fixed any time soon.
2015-11-09 14:25:20 -08:00
Chris Robinson 062d7e3487 Update the fix-rate config option description 2015-11-09 11:47:56 -08:00
Chris Robinson de53ff4aff Build with -std=c11 if possible 2015-11-09 11:36:02 -08:00
Chris Robinson ceee5c8ffb Remove some unused function checks 2015-11-08 00:19:55 -08:00
Chris Robinson 9891d61206 Add options to disable Pulse's and ALSA's resamplers 2015-11-07 23:05:45 -08:00
Chris Robinson fe00b65d9a Correct a comment about the new bsinc resampler
It always performs anti-aliasing, even when up-sampling.
2015-11-07 06:16:54 -08:00
Chris Robinson 91258cc192 Simplify reverb panning rotations 2015-11-07 06:15:29 -08:00
Chris Robinson ec7b21cd14 Remove a const to silence some warnings 2015-11-06 22:17:15 -08:00
Chris Robinson de74498cd9 Use more accurate floating point literals 2015-11-06 10:27:28 -08:00
Chris Robinson 858230f452 Include float.h if present before defining math stuff 2015-11-06 02:38:34 -08:00
Chris Robinson d6f4e5139f Add another cast for MSVC 2015-11-06 02:23:42 -08:00
Chris Robinson 368f2d6907 Use ALuint instead of size_t for a loop iterator 2015-11-06 02:21:24 -08:00
Chris Robinson 16a6e010fd Add a cast to silence an MSVC warning 2015-11-06 02:18:42 -08:00
Chris Robinson 3e992309a5 Use a more appropriate type in MatchFilter 2015-11-06 02:13:28 -08:00
Chris Robinson b6682ede61 Cast a double->float return to silence MSVC 2015-11-06 02:05:22 -08:00
Chris Robinson 203c249bf7 Add missing math.h include for sin() 2015-11-06 00:13:02 -08:00
Chris Robinson 8c6d891694 Pan each early/late delay line separately 2015-11-05 23:46:39 -08:00
Chris Robinson 431c89ece9 Manually inline and condense the bsinc resampler 2015-11-05 21:57:12 -08:00
Chris Robinson 46e72a48ad Remove an unneeded memset that was overwriting memory 2015-11-05 19:45:04 -08:00
Chris Robinson b9e192b78a Implement a band-limited sinc resampler
This is essentially a 12-point sinc resampler, unless it's resampling to a rate
higher than the output, at which point it will vary between 12 and 24 points
and do anti-aliasing to avoid/reduce frequencies going over nyquist.

Code provided by Christopher Fitzgerald.
2015-11-05 09:42:08 -08:00
Chris Robinson dce3d0c7bf Change the Kaiser rippling limit to -60dB
This improves the transition cutoff, shortening its width and reducing the
amount of error.
2015-11-04 19:13:25 -08:00
Chris Robinson bd0acf2843 Replace the Lanczos window with Kaiser for the sinc resampler 2015-11-04 06:40:54 -08:00
Chris Robinson c57f571920 Pass in the Q parameter for setting the filter parameters
Also better handle the peaking filter gain.
2015-11-01 05:41:06 -08:00
Chris Robinson f094d94608 Remove an unused struct field 2015-11-01 01:01:27 -07:00
Chris Robinson 3121c30396 Fix a comment 2015-11-01 00:13:02 -07:00
Chris Robinson 4a9dd6576f Add an impulse "waveform" to altonegen 2015-10-31 05:15:13 -07:00
Chris Robinson 0644ebbaa2 Use modff to split the modulation delay components 2015-10-31 01:35:01 -07:00
Chris Robinson 19c437de6d Add missing config.h entry for modff 2015-10-30 01:37:39 -07:00
Chris Robinson 0713b8c322 Check for modff 2015-10-29 16:38:58 -07:00
Chris Robinson c3d58947c3 Mix reverb to output in the inner loop 2015-10-29 03:23:50 -07:00
Chris Robinson 79ad54f0c3 Include the echo's attenuation of the late reverb in the late reverb gain 2015-10-29 02:10:07 -07:00
Chris Robinson 62f1df77d7 Do up to 256 samples per reverb inner loop iteration 2015-10-29 01:40:23 -07:00
Chris Robinson f4a53cf609 Add an HRTF example
Doubles as a psuedo-test to play a sound with HRTF.
2015-10-28 18:52:49 -07:00
Chris Robinson bd73cdab0d Pass the appropriate BackendInfo explicitly to ProbeDevices 2015-10-28 18:10:12 -07:00
Chris Robinson 59c6fc966d Print enumerated HRTFs in openal-info 2015-10-28 14:47:58 -07:00
Chris Robinson 35c4aae3d6 Finalize ALC_SOFT_HRTF 2015-10-28 13:38:30 -07:00
Chris Robinson b7a6a7da6a Right-align the HRTF Mode label 2015-10-28 03:15:01 -07:00
Chris Robinson 63259872d1 Remove the Reverb Bost option from alsoft-config
It's not really an appropriate setting for most users to play with. The option
still exists, but it's no longer editable in the GUI.
2015-10-28 03:09:15 -07:00
Chris Robinson 9ce2b8f9a7 Always update all reverb properties
The EAX-only effect properties will be set to compatible defaults when standard
reverb is set, and the EAX-only effects will be skipped during sample
processing.
2015-10-28 02:36:48 -07:00
Chris Robinson 8f8bf1f605 Do multiple samples at once in each reverb component 2015-10-28 01:57:51 -07:00
Chris Robinson e472cfcc53 Rename ALC_NUM_HRTF_SPECIFIER_SOFT to ALC_NUM_HRTF_SPECIFIERS_SOFT 2015-10-26 22:34:02 -07:00
Chris Robinson 6b627caf52 Add a comment about fegetenv possibly saving the SSE register for us 2015-10-26 01:49:56 -07:00
Chris Robinson 45f11d7b64 Set the current gain immediately if the target is close enough 2015-10-26 01:48:12 -07:00
Chris Robinson 90349b384f Use the correct position in the SSE resamplers for left-over processing 2015-10-25 14:07:58 -07:00
Chris Robinson fc03a955e9 Fix the SSE4.1 resamplers
Apparently the given _mm_extract_epi32 index obeys memory order, rather than
component order.
2015-10-24 17:37:53 -07:00
Chris Robinson 8d89c8be9e Remove a couple unused parameters 2015-10-24 16:57:32 -07:00
Chris Robinson a57fe89050 Lock the source queue for writing when updating the playback offset 2015-10-24 16:31:28 -07:00
Chris Robinson 298fdb220d Fix usage of modf 2015-10-24 15:13:56 -07:00
Chris Robinson 2a62b38530 Update filter histories even when they're not used
If the filter properties are continually updated, and the HF or LF gain goes
from <1, to 1, and later back to <1, the history shouldn't hold stale values
from before it was at 1.
2015-10-24 00:47:58 -07:00
Chris Robinson 714354caee Set XYZ channel gains for source sends to 0
It's cleaner to just set the gains to 0 rather than to special-case B-Format in
the mixer.
2015-10-23 20:16:11 -07:00
Chris Robinson bca854baac Use one send gain per buffer channel 2015-10-23 15:11:34 -07:00
Chris Robinson 4813125d31 Return the new vector result from aluMatrixVector 2015-10-22 23:39:56 -07:00
Chris Robinson 6fc8cd3b29 Convert the PortAudio backend to the new backend API 2015-10-22 10:46:36 -07:00
Chris Robinson 6689c61ff4 Remove the MIDI code
The extension's not going anywhere, and it can't do anything fluidsynth can't.
The code maintenance and bloat is not worth keeping around, and ideally the AL
API would be able to facilitate MIDI-like behavior anyway (envelopes, start-at-
time, etc).
2015-10-20 18:01:27 -07:00
Chris Robinson d9a77a7edc Use NEW_OBJ in a few more places 2015-10-20 11:20:15 -07:00
Chris Robinson acb37edc4f Make VerifyDevice and VerifyContext modify its parameter 2015-10-19 18:48:33 -07:00
Chris Robinson 9b6b084d60 Use the correct realignment size for post-stepping mixing 2015-10-18 13:46:52 -07:00
Chris Robinson fa32093906 Remove unused channel labels 2015-10-18 10:25:56 -07:00
Chris Robinson c4ba9d21dc Use the correct array indices for SSE register components
SSE uses reverse ordering, such that component 0 is the last in memory.
_mm_load_* and _mm_loadu_*, and the corresponding stores, do not change the
memory ordering.
2015-10-17 09:59:26 -07:00
Chris Robinson d9e9a7b05f Include the fractional part with the source sample/sec offset 2015-10-16 10:52:10 -07:00
Chris Robinson 83b41a7676 Update changelog 2015-10-16 08:22:17 -07:00
Chris Robinson 2730d8e76f Round the calculated stepping value 2015-10-15 15:38:07 -07:00
Chris Robinson 2ff3bf5ab0 Use a constant value for the post-position padding 2015-10-15 15:13:19 -07:00
Chris Robinson 97f53d941c Store the source's previous samples with the voice
This helps avoid different results when looping is toggled within a couple
samples of the loop point, or when a processed buffer is removed while the
source is only a couple samples into the next buffer.
2015-10-15 07:29:25 -07:00
Chris Robinson 3c54ba3901 Fix absolute path detection on Windows 2015-10-14 05:01:34 -07:00
Chris Robinson f654767395 Silence implicit double-to-float cast MSVC warning 2015-10-14 05:00:11 -07:00
Chris Robinson 98eda6b35b Properly limit the calculated source offset components 2015-10-14 03:23:19 -07:00
Chris Robinson d386675efe Print the wave type being played 2015-10-14 02:41:39 -07:00
Chris Robinson 9012bcf327 Make the tonegen sampling rate match the device rate by default 2015-10-14 01:54:50 -07:00
Chris Robinson c18e23477c Add a tone generator test program
Currently used to test the general output, particularly the resampler, by
checking the results with a spectrum analyzer and/or oscilloscope (for example
using PulseAudio's "Monitor of ..." devices feeding an external app).
2015-10-13 11:43:25 -07:00
Chris Robinson d7ec9d355f Shut GCC up 2015-10-13 11:38:53 -07:00
Chris Robinson 0498a4d35e Rename a couple HRTF Mode combo box entries 2015-10-13 03:21:00 -07:00
Chris Robinson 2fba5c1f5e Properly apply fractional source offsets when a user offset is set 2015-10-13 03:01:34 -07:00
Chris Robinson be2028ba4a Fix HRTF group box size 2015-10-12 06:42:28 -07:00
Chris Robinson 67455c66a1 Replace the resample_fir6 declaration with resample_fir8 2015-10-12 06:34:55 -07:00
Chris Robinson db4253d3bd Remove 96khz from the combo box
It can still be entered in manually, as can any value between 8khz and 192khz,
but having it selectable can create the mistaken idea that selecting it is
better than picking 44.1khz or 48khz.
2015-10-12 06:23:50 -07:00
Chris Robinson b4bc36c4a0 Reorganize the HRTF group box
The three radio buttons are moved to a combo box, which gives a bit more width
for the file list contents.
2015-10-12 05:52:34 -07:00
Chris Robinson ed0f3bc1b1 Only show certain options that are available at build-time 2015-10-11 23:56:45 -07:00
Chris Robinson f33d6a5211 Reformat the CPU Extensions' checkboxes 2015-10-11 12:03:50 -07:00
Chris Robinson b859f1bdc3 Avoid multiple sin, cos, and sqrt calls for filter calculations 2015-10-11 10:21:37 -07:00
Chris Robinson db22465c1d Update changelog 2015-10-11 07:40:09 -07:00
Chris Robinson 00e419e948 Replace the sinc6 resampler with sinc8, and make SSE versions 2015-10-11 07:37:22 -07:00
Chris Robinson 0211831858 Move the FIR4 from SSE2 to SSE3
SSE3 can avoid the slow _MM_TRANSPOSE_PS4 call thanks to the inclusion of
horizontal adds.
2015-10-11 06:38:00 -07:00
Chris Robinson 75d8e5989f Slightly improve the FIR4 SSE resamplers 2015-10-11 05:31:14 -07:00
Chris Robinson 0149dded34 Use doubles to calculate the Lanczos coefficients 2015-10-09 08:30:33 -07:00
Chris Robinson 34e55c0034 Update changelog 2015-10-08 00:41:40 -07:00
Chris Robinson 4f4cadd756 Allow apps to request a specific HRTF 2015-10-07 03:29:53 -07:00
Chris Robinson 0eef6d9d51 Use the enumerated HRTF list for selecting an HRTF
Also report the proper specifier of the one currently in use.
2015-10-06 06:48:53 -07:00
Chris Robinson 1b51ee8b87 Store the Hrtf struct with the corresponding HrtfEntry
Also store the filename with the Hrtf struct so it can be reused for multiple
HrtfEntry objects.
2015-10-06 04:01:53 -07:00
Chris Robinson 946b3d5746 Try to use the full path for the CWD instead of . 2015-10-06 01:26:15 -07:00
Chris Robinson a4c378dd00 Enumerate and list HRTFs per-device 2015-10-06 00:23:11 -07:00
Chris Robinson f590af510d Skip empty data directory entries 2015-10-04 17:24:12 -07:00
Chris Robinson 074983b19c Implement SearchDataFiles for Windows 2015-10-04 16:41:54 -07:00
Chris Robinson 70fbc2b1ff Add a function to get a list of data files
The method takes a marked-up filename (e.g. may include %r for a sample rate,
%% for %, etc), and returns a vector of strings of found filenames that match.
It will search the CWD, the local, and global data directories, in that order.
2015-10-03 20:41:18 -07:00
Chris Robinson aa10068ca2 Add methods to enumerate and query device HRTFs
Currently just returns a dummy entry.
2015-10-03 19:59:21 -07:00
Chris Robinson 0fcd497796 Combine two arrays 2015-10-01 01:05:08 -07:00
Chris Robinson 07f80eb4e1 Move the resampler stuff to mixer.c where it's used 2015-10-01 00:34:13 -07:00
Chris Robinson d5675d5395 Search more aggressively for libs needed by SDL_sound 2015-09-30 21:34:30 -07:00
Chris Robinson da3b9568dd Revert "Only use SDL1 with SDL_sound"
This reverts commit 8883ce1cef.
2015-09-30 18:45:19 -07:00
Chris Robinson 904cdfda32 Avoid double-checks for the stepping mixer loops 2015-09-30 17:25:28 -07:00
Chris Robinson b68fbe3628 Replace separate vaddq_f32/vmulq_f32 calls with a vmlaq_f32 2015-09-30 13:34:09 -07:00
Chris Robinson 6d53ffdbf3 Remove the --make-tab option from makehrtf
We haven't used a built-in HRTF table for a while now.
2015-09-29 23:09:45 -07:00
Chris Robinson dc10e56bab Implement a 6-point sinc-lanczos filter 2015-09-29 20:39:12 -07:00
kcat e13d553aef Merge pull request #21 from aaronmjacobs/master
Fix resample_fir4 link error
2015-09-29 12:39:48 -07:00
Aaron Jacobs a7084b1051 Fix resample_fir4 link error 2015-09-29 12:34:03 -07:00
Chris Robinson 8883ce1cef Only use SDL1 with SDL_sound 2015-09-28 23:29:52 -07:00
Chris Robinson 848a4a0fcc Auto-replace the "cubic" resampler with "sinc4" 2015-09-28 12:59:52 -07:00
Chris Robinson 9992c4da99 Update changelog 2015-09-27 23:57:50 -07:00
Chris Robinson ab6622a8d6 Replace the cubic resampler with a 4-point sinc/lanczos filter 2015-09-27 23:57:25 -07:00
Chris Robinson 3e60b18989 Don't keep selecting the mixer to use 2015-09-27 20:55:39 -07:00
Chris Robinson 86ff35bf71 Increase the max pitch to 255
Note that this is the multiple above the device sample rate, rather than the
source property limit. It could theoretically be increased to 511 by testing
against UINT_MAX instead of INT_MAX, since the increment and positions are
using unsigned integers. I'm just being paranoid about overflows.
2015-09-26 11:18:30 -07:00
Chris Robinson 30d88cf547 Use the macros to allocate the wrapper backends 2015-09-24 15:54:32 -07:00
Chris Robinson 17e2b4b3a5 Remove unneeded clamping 2015-09-24 13:44:28 -07:00
Chris Robinson d8e2308adb Loop over the gain values only once 2015-09-24 12:36:02 -07:00
Chris Robinson 83659db996 Return the number of properties written 2015-09-24 10:45:34 -07:00
Chris Robinson ef43370927 Use a local var instead of multiple double-dereferencing 2015-09-24 10:18:29 -07:00
Chris Robinson 64858e3e94 Fix B-Format HRTF decoding 2015-09-23 16:02:46 -07:00
Chris Robinson 4bdd58bc83 Use N3D scaling instead of FuMa 2015-09-23 15:03:53 -07:00
Chris Robinson ba43582278 Use a single enum list for source properties 2015-09-22 08:48:26 -07:00
Chris Robinson 07d0bddb4f Avoid a potential race condition with NewThunkEntry
It's possible for another invocation to increase the array size in between the
ReadUnlock and WriteLock calls, causing the 'i' index to refer to a taken
entry.
2015-09-21 13:51:56 -07:00
Chris Robinson 33ac3095dd Handle up to 6 values with alSourcedvSOFT and alGetSourcefv 2015-09-21 09:43:51 -07:00
Chris Robinson a774408753 Get rid of ALCdevice_GetLatency 2015-09-21 05:52:01 -07:00
Chris Robinson 5f5eebc4df Allow the hrtf_tables option to be device-specific 2015-09-20 08:28:34 -07:00
Chris Robinson 46bbf95bae Disable mmdevapi capture and update changelog
Unfortunately mmdevapi does not do channel remixing or resampling, even for
capture, so the device can only be opened in the mode it's configured for.
For now, fallback to dsound or winmm to get the conversion until we can do it
ourselves.
2015-09-18 03:19:16 -07:00
Chris Robinson db0f29f6d8 Fix updating listener params when forcing updates 2015-09-18 00:48:43 -07:00
Chris Robinson 2f1bfb5945 Add missing enum values
Spotted by Xavier Bouchoux.
2015-09-17 04:01:46 -07:00
Chris Robinson 4570345534 Return the data file handle on Windows if it was opened 2015-09-16 18:20:28 -07:00
Chris Robinson 3c72865775 Use sizeof T for the size of the object type 2015-09-15 19:23:32 -07:00
Chris Robinson b348abf5c3 Rename F_2PI to F_TAU 2015-09-13 08:46:48 -07:00
Chris Robinson 2763f4096c Update properties and clear wet buffers before mixing/processing 2015-09-13 06:51:55 -07:00
Chris Robinson 87f3babe2c Append "on OpenAL Soft" to mmdevapi, dsound, and winmm device names
Would be nicer to have a more backend-agnostic method of doing this, Perhaps
even also only when the router is being used.
2015-09-10 22:15:40 -07:00
Chris Robinson 9469c4c79b Slight changelog update 2015-09-08 15:12:13 -07:00
Chris Robinson 483352f32b Set both BS2B parameters at once 2015-09-08 15:04:42 -07:00
Chris Robinson 475a566e35 Play a 1khz sine wave for 4 seconds in the loopback example 2015-09-07 21:17:26 -07:00
Chris Robinson d240077bc5 Explicitly convert to int in the aluF2I/S/B functions 2015-09-07 03:27:25 -07:00
Chris Robinson 5d8a1c11b5 Avoid setting an HRTF-compatible format when it's disabled 2015-09-05 14:33:21 -07:00
Chris Robinson 365a010732 Don't store the HRTF request mode in the device flags 2015-09-05 01:32:12 -07:00
Chris Robinson 804909e1d7 Set a proper HRTF status when non-stereo output is used 2015-09-05 00:42:44 -07:00
Chris Robinson d6290482cc Add word wrapping to the changelog 2015-09-05 00:07:05 -07:00
Chris Robinson 703f1fdec2 Specify the pa_channel_map directly instead of through a string 2015-09-04 23:59:24 -07:00
Chris Robinson 28a516e8a5 Change the hrtf config option to expect auto/true/false instead of a bool 2015-09-04 20:16:48 -07:00
Chris Robinson 6fc567f1ab Reorganize some HRTF config options 2015-09-04 17:06:23 -07:00
Chris Robinson 0eb83fe462 Make option and block names case-sensitive
This is to avoid any issues with device names that can be case-sensitive, and
strcasecmp not working properly for non-ASCII-7 uppercase characters.
2015-09-04 16:09:09 -07:00
Chris Robinson 5f40daabc2 Reverse the device and block names for device-specific options 2015-09-04 15:47:48 -07:00
Chris Robinson 30398083f4 Update the changelog for initial 1.17 changes 2015-09-04 14:53:38 -07:00
Chris Robinson 9d6bb05ac6 Reposition the JACK backend entry 2015-09-04 14:52:23 -07:00
Chris Robinson ce487d1e43 Use the correct device name for PulseAudio too
Like mmdevapi. duplicate device names will have a '#2' or such appended, so the
device's reported name may be incorrect.
2015-09-03 14:59:59 -07:00
Chris Robinson 23729c2083 Replace another vector loop with VECTOR_FIND_IF 2015-09-03 14:53:46 -07:00
Chris Robinson 87e4ec6620 Use the correct device name when opening a device by name
Duplicate device names will have a '#2' or such appended, so the device's
reported name may be incorrect.
2015-09-03 11:56:46 -07:00
Chris Robinson 7b4f1cfb4a Handle the stereo-mode config option with alsoft-config 2015-08-28 22:09:07 -07:00
Chris Robinson 2cb974df40 Document the bformat config option 2015-08-28 15:53:17 -07:00
Chris Robinson 70c63dd576 Allow '#' in config block names
Since some devices may have it appended.
2015-08-28 14:59:45 -07:00
Chris Robinson 8fe421cba1 Document device-specific config options 2015-08-28 14:48:33 -07:00
Chris Robinson e5fa4ee25d Allow for device-specific config values 2015-08-28 14:10:39 -07:00
Chris Robinson bbc16e2c15 Use ACN ordering for ambisonics coefficients arrays
Note that it still uses FuMa scalings internally. Coefficients loaded from
config files specify if they're FuMa (in both ordering and scaling) or N3D,
and will get reordered or rescaled as needed.
2015-08-28 10:58:30 -07:00
Chris Robinson d17534332f Update README acknowledgements 2015-08-26 13:06:19 -07:00
Chris Robinson 762cb00327 Update the hrtf.txt about the 48khz default dataset. 2015-08-26 12:52:18 -07:00
Chris Robinson 01f9cb4072 Update the default value for JACK's spawn-server option 2015-08-26 12:50:54 -07:00
Chris Robinson baf1bd5b54 Avoid temporary vector objects 2015-08-24 03:02:58 -07:00
Chris Robinson c292b9e4ff Revert "Fix B-Format rotation"
This reverts commit 7ffb9b3056.

It was behaving as appropriate before (orienting left did pan it left for the
listener), I was apparently just misinterpreting the matrix.
2015-08-24 00:19:47 -07:00
Chris Robinson 7ffb9b3056 Fix B-Format rotation
The rotation erroneously specified the orientation of the source relative to
the sound field, whereas it should be the orientation of the sound field *and*
source relative to the listener. So now when the source is oriented left, the
front of the sound field is to the left of the listener.
2015-08-23 22:01:29 -07:00
Chris Robinson 8559af5d48 Minor reformating 2015-08-22 07:23:43 -07:00
Chris Robinson 6f59ebab27 Don't explicitly store first-order coefficients
It seems a simple scaling on the coefficients will allow first-order content to
work with second- and third-order coefficients, although obviously not with any
improved locality. That may be something to look into for the future, but this
is good enough for now.
2015-08-18 07:44:17 -07:00
Chris Robinson 53f6590b17 Set the WAVEFORMATEX cbSize field for mmdevapi capture 2015-08-18 00:19:22 -07:00
Chris Robinson d5ab883da2 Define MixHrtf directly instead of through a SUFFIX macro 2015-08-15 01:37:46 -07:00
Chris Robinson f6e52debb5 Fix handling of PulseAudio devices that have the same description 2015-08-14 19:05:16 -07:00
Chris Robinson c1146d27c6 Also recognize "DirectSound" and "MMSYSTEM" on Windows 2015-08-14 17:02:11 -07:00
Chris Robinson a5aaa3c9aa Recognize "DirectSound3D" as a device name on Windows 2015-08-13 21:18:39 -07:00
Chris Robinson 7d4e3688e1 Wait for the OpenSL buffer queue to empty when stopping 2015-08-13 16:01:31 -07:00
Chris Robinson f134fbac5c Check for pthread_*_np functions in pthread_np.h when it exists 2015-08-13 15:20:34 -07:00
kcat c2f88ffad6 Merge pull request #15 from aaronmjacobs/master
Make installation of headers and libraries optional
2015-08-12 15:23:30 -07:00
Aaron Jacobs 11e6e07a72 Make installation of headers and libraries optional 2015-08-12 15:08:14 -07:00
Chris Robinson 8fa4f276f8 Allow specifying ALC_DONT_CARE_SOFT for ALC_HRTF_SOFT
ALC_FALSE now indicates explicitly no HRTF mixing, while ALC_DONT_CARE_SOFT
is autodetect.
2015-07-06 12:37:21 -07:00
Chris Robinson f58d985789 Change source radius behavior
For sources with a non-0 radius:
When distance <= radius, factor = distance/radius*0.5
When distance > radius, factor = 1 - asinf(radius/distance)/PI

Also, avoid using Position after calculating the localized direction and
distance.
2015-07-05 08:54:29 -07:00
Chris Robinson 4efe0d8696 Update a couple comments 2015-07-04 10:12:57 -07:00
Chris Robinson d90dfc8d1f Check for duplicate device names in the mmdevapi backend 2015-06-30 07:44:56 -07:00
Chris Robinson c24f4f230f Use the lockless ringbuffer for mmdevapi capture
The backend's capture funcs are already called while under a lock, so multiple
threads shouldn't be able to read from it at once.
2015-06-30 07:42:09 -07:00
Chris Robinson e5964d6dc4 Double check the mmdevapi capture format
This isn't a real solution, but it should get IAudioClient_IsFormatSupported to
stop failing.
2015-06-07 13:05:07 -07:00
Chris Robinson ed3f32b087 Don't define struct timespec with VS2015 2015-06-07 12:01:15 -07:00
Chris Robinson 5543000c5b Check for snprintf in stdio.h
This works better for VS2015, which adds support for the function.
2015-06-07 11:16:11 -07:00
Chris Robinson 9f49ac0fda Add a macro to simplify allocating and constructing an object 2015-05-18 16:39:44 -07:00
Chris Robinson 9479ea656b Convert the solaris backend to the new API 2015-05-17 04:18:38 -07:00
Chris Robinson 3058205963 Add a method to reset the device
This basically acts as if the app created a new context with the specified
attributes (causing the device to reset with new parameters), then immediately
delete it. Existing contexts remain undisturbed, except for a temporary pause
while the device output is reconfigured.
2015-05-16 02:18:09 -07:00
Chris Robinson 80f0b5e736 Add a few more HRTF status values
DISABLED - Generic disabled status
ENABLED - Generic enabled status
DENIED - Not allowed (user has configured HRTF to be off)
REQUIRED - Forced (user has forced HRTF to be used)
HEADPHONES_DETECTED - Enabled because headphones were detected
UNSUPPORTED_FORMAT - Device format is not compatible with available filters
2015-05-16 01:06:26 -07:00
Chris Robinson 0f51b6df4b Add an HRTF status query
This can report the status of HRTF, specifying if it's enabled or not and why
(currently only reports unsupported formats, but this may be extended).
2015-05-15 23:28:03 -07:00
kcat c6dc1376e4 Merge pull request #11 from alexxvk/master
Fix mingw build
2015-05-03 16:08:33 -07:00
alexxvk 8846ad967f Fix mingw64 build 2015-05-02 17:44:06 +06:00
Chris Robinson 8dfb1bc9db Fix EFX_REVERB_PRESET_DRIVING_COMMENTATOR 2015-04-04 03:29:57 -07:00
Chris Robinson 19f79be57b Fix static_assert when __COUNTER__ isn't available 2015-03-02 23:05:25 -08:00
Chris Robinson 6fcaccc964 Don't assert the increment value in the copy32 resampler 2015-02-15 13:15:49 -08:00
Chris Robinson b4c9744d6a Fix the HRTF B-Format decoder's W coefficient
And limit it to first-order again, since there will likely need to be extra
scalings applied.
2015-02-14 12:11:50 -08:00
Chris Robinson 18fed4e79a Cheange some more unnecessarily small coefficients to 0 2015-02-11 22:23:33 -08:00
Chris Robinson 71b6e9bfe0 Add an option for "basic" HRTF rendering
This method is intended to help development by easily testing the quality of
the B-Format encode and B-Format-to-HRTF decode. When used with HRTF, all
sources are renderer using the virtual B-Format output, rather than just
B-Format sources.

Despite the CPU cost savings (only four channels need to be filtered with HRTF,
while sources all render normally), the spatial acuity offered by the B-Format
output is pretty poor since it's only first-order ambisonics, so "full" HRTF
rendering is definitely preferred.

It's /possible/ for some systems to be edge cases that prefer the CPU cost
savings provided by basic over the sharper localization provided by full, and
you do still get 3D positional cues, but this is unlikely to be an actual use-
case in practice.
2015-02-11 09:32:05 -08:00
Chris Robinson a6e574ba9e Calculate HRTF coefficients for all B-Format channels at once
It's possible to calculate HRTF coefficients for full third-order ambisonics
now, but it's still not possible to use them here without upmixing first-order
content.
2015-02-10 11:22:28 -08:00
Chris Robinson 3ed79cc229 Use a single statement to declare the buffer format channel maps 2015-02-10 10:04:59 -08:00
Chris Robinson b9e81794e4 Pass the (FuMa) channel number to GetBFormatHrtfCoeffs 2015-02-10 06:46:02 -08:00
Chris Robinson cf5856ed48 Correct conversion from polar to cartesian coords for HRTF
A functional no-op (cos(a) == cos(-a), -sin(a) == sin(-a)), but Ambisonics
expects the azimuth angle to go counter-clockwise.
2015-02-10 05:38:16 -08:00
Chris Robinson a1d4847d07 Use B-Format for HRTF's virtual output format
This adds the ability to directly decode B-Format with HRTF, though only first-
order (WXYZ) for now. Second- and third-order would be easilly doable, however
we'd need to be able to up-mix first-order content (from the BFORMAT2D and
BFORMAT3D buffer formats) since it would be inappropriate to decode lower-order
content with a higher-order decoder.
2015-02-09 15:59:29 -08:00
Chris Robinson 8933e21ef2 Properly handle a mono output buffer with the MIDI synths 2015-02-09 08:47:48 -08:00
Chris Robinson 1393af4f9f Add a comment detailing how the HRTF channel buffer is set up 2015-02-09 08:11:07 -08:00
Chris Robinson a925fb8111 Make sure the BS2B filter is freed if HRTF is enabled 2015-02-09 05:56:37 -08:00
Chris Robinson 61755f35c1 Move HRTF params and state closer together 2015-02-09 05:54:14 -08:00
Chris Robinson c297b51a6e Add a note to the JACK backend about a (minor) race condition 2015-02-07 10:09:01 -08:00
Chris Robinson f5dd5c1330 Fix some ringbuffer function comments 2015-02-07 10:08:27 -08:00
Chris Robinson 4a5daa2e91 Clean up another vector iterator loop 2015-02-07 04:47:02 -08:00
Chris Robinson 50cdc0ac1e Avoid tracing wide-char strings
Because on Windows, traced strings are written to a char string, which causes
UTF-16 strings to be converted to a narrow (non-UTF-8) encoding, potentially
losing characters.
2015-02-07 04:19:50 -08:00
Chris Robinson 61743a3a00 Avoid unnecessary uses of ExchangeInt/Ptr 2015-02-04 23:39:51 -08:00
Chris Robinson efb7b11d7f Avoid unnecessary ExchangeInt uses 2015-02-04 05:57:54 -08:00
Chris Robinson 33ced1a7c2 Try the JACK backend before PulseAudio
The backend will not spawn a server by default, so PulseAudio will still be
preferred as long as a JACK server isn't already running.
2015-02-03 05:11:46 -08:00
Chris Robinson e6f4648924 Signal the mixer thread outside of the mutex lock 2015-02-03 02:40:22 -08:00
Chris Robinson 12e443d61d Don't try to spawn a JACK server by default
And don't print an error if one couldn't be started when not requested.
2015-02-03 01:23:19 -08:00
Chris Robinson e53861bfb9 Always use the JACK server buffer size as the update size
The buffer-size config option now only specifies an additional mix ahead to
keep ready for audio requests, rather than a pretend period size.
2015-02-03 01:08:38 -08:00
Chris Robinson 290badea95 Ensure PulseAudio's mixing loop is signaled when stopping 2015-01-21 22:02:02 -08:00
Chris Robinson 5cbafaa014 Remove a couple unused variables 2015-01-20 16:44:47 -08:00
Chris Robinson 0b6bb76259 Fix variable check 2015-01-20 16:42:23 -08:00
Chris Robinson 199646ac2b Fix X7DOT1_NARROW name to X7DOT1_WIDE 2015-01-15 09:27:37 -08:00
Chris Robinson c1607300bb Accept a "narrow" layout for 7.1 with mmdevapi 2015-01-15 01:56:04 -08:00
Chris Robinson 205e258d1a Fix high-pass coefficients 2015-01-13 09:38:13 -08:00
Chris Robinson f9a9235333 Add missing alignas to CubicLUT declaration 2015-01-13 09:29:03 -08:00
Chris Robinson ed05c83ee7 Fix the flags type 2015-01-11 15:02:26 -08:00
Chris Robinson 4dc9956a12 Remove some IN_IDE_PARSER uses 2014-12-24 17:15:50 -08:00
Chris Robinson bc17d6fc45 Remove some unnecessary restrict uses 2014-12-24 17:14:45 -08:00
Chris Robinson ca28d81b36 Update some function comments (bytes -> elements) 2014-12-24 16:42:57 -08:00
Chris Robinson c71d8c0fc5 Update a comment 2014-12-24 01:17:55 -08:00
Chris Robinson ad11a90720 Don't update the channel format if all JACK ports were allocated 2014-12-23 22:02:56 -08:00
Chris Robinson 93b69583d3 Try the device-specified channel configuration first
This does basically no checking that the channel config is correct, but should
be good enough for when non-stereo modes are requested.
2014-12-23 21:06:10 -08:00
Chris Robinson 2f162f0fc3 Use the custom ringbuffer instead of JACK's 2014-12-23 20:24:10 -08:00
Chris Robinson a9cce5de3e Add a lockless ringbuffer
Largely copied from JACK, it's extended to work with user-specified element
sizes instead of bytes. This is necessary to be able to work with 6- and 7-
channel output modes.
2014-12-23 20:17:34 -08:00
Chris Robinson 6ccf10bbde Don't set unused pointers to NULL, don't try to write 0 samples 2014-12-23 11:56:55 -08:00
Chris Robinson e84b3f4707 At least measure the ringbuffer size for JACK's latency
We should also add the port latency, but there's currently no way to ensure
it's synchronized with the ringbuffer (the ringbuffer will update before the
port latency gets updated).
2014-12-23 11:42:57 -08:00
Chris Robinson beb670a939 Add an option to prevent spawning a JACK server 2014-12-22 19:29:55 -08:00
Chris Robinson 5e189624b7 Add JACK to the config utility 2014-12-22 18:56:15 -08:00
Chris Robinson b8d98dd6d0 Add an option for an increased ringbuffer size with JACK 2014-12-22 18:48:54 -08:00
Chris Robinson 7e6d0d6b4f Fill out the JACK playback backend
A few notes about it:

The OpenAL device's requested buffer metrics are ignored, and instead the
device will keep one JACK-sized buffer's worth of audio prepared for JACK's
next process request.

Output is restricted to 32-bit float stereo. Part of this is because JACK
requires a buffer size that's a power of 2 (measured in samples), and the
ringbuffer requires a buffer size that's a power of 2 (measured in bytes). A
channel count of 6 (5.1) or 7 (6.1) will not work without causing a sample to
split over the edge of the ringbuffer. Additioanlly, JACK doesn't provide
information about what channel configuration a device or set of ports has, so
there's no way to know what ports 3 and up map to (even the first two ports are
unknown, but assuming stereo seems to work well enough).

There is no device latency measurement (for AL_SOFT_source_latency) due to the
non-atomicity between the ringbuffer's read size and port latency. A method is
needed to ensure the ringbuffer's read size and port latency can both be
measured between the end of a JACK update cycle (when the port latency has been
updated with the newly-retrieved audio) and the following ringbuffer read
update.
2014-12-22 13:30:35 -08:00
Chris Robinson 23197ddbc3 Add a skeleton backend for JACK 2014-12-21 15:51:16 -08:00
Chris Robinson f2d59671b2 Add a cmake module to find JACK 2014-12-21 15:48:40 -08:00
Chris Robinson b56d50912e Set error if capture device fails to start 2014-12-21 12:46:04 -08:00
Chris Robinson c2975b5f44 Trace lparam and wparam message values 2014-12-21 12:37:02 -08:00
Chris Robinson a91bca64f8 Check the PROPVARIANT type before use 2014-12-21 12:20:31 -08:00
Chris Robinson 13092d94b7 Use VECTOR_FOR_EACH instead of a manual loop 2014-12-21 10:45:10 -08:00
Chris Robinson 28a9f0826c Use a macro for the record thread name 2014-12-21 10:38:40 -08:00
Chris Robinson 6574fa4863 Cast to the pointer-to-type to increment the buffer 2014-12-21 09:48:36 -08:00
Chris Robinson aa4cf99e63 Fix logging on Windows 2014-12-20 06:24:55 -08:00
Chris Robinson abf0bd13ca Support capture with mmdevapi 2014-12-19 15:49:37 -08:00
Chris Robinson f300be00ae Handle logging Unicode strings on Windows 2014-12-19 10:14:02 -08:00
Chris Robinson 7f696edf13 Set the right variable when assuming CPU extensions 2014-12-19 08:50:30 -08:00
Chris Robinson c3a36d9b19 Do up to 256 samples at a time with multi-step loops 2014-12-18 09:23:55 -08:00
Chris Robinson 9897fca794 Inline a couple functions 2014-12-18 08:52:04 -08:00
Chris Robinson 13c2dd7115 Offset to the buffer's channel start first 2014-12-18 08:42:03 -08:00
Chris Robinson 94ad5b289f Avoid duplicate calculations 2014-12-18 07:42:14 -08:00
Chris Robinson 6086c344dc Assert that there's a buffer for mixing
For Clang's static analysis.
2014-12-17 10:57:35 -08:00
Chris Robinson 4b91d34d58 Set a couple pulse callbacks to NULL during reset, and minor cleanups 2014-12-17 10:10:28 -08:00
Chris Robinson af193e9fb3 Don't kill pulseaudio's mixer thread if it's already killed 2014-12-17 09:47:58 -08:00
Chris Robinson c37275efdc Use aluVector and aluMatrix in a couple more places 2014-12-16 10:36:44 -08:00
Chris Robinson c48a6196ad Constify some variables 2014-12-16 09:20:35 -08:00
Chris Robinson d2adefe063 Pass a vectory to aluMatrixVector 2014-12-16 07:42:17 -08:00
Chris Robinson 3b8f54d572 Use aluVector in some more places 2014-12-16 07:20:27 -08:00
Chris Robinson 97f6d302fe Add explicit matrix and vector types to operate with 2014-12-16 06:29:31 -08:00
Chris Robinson 1b7c554068 Multiply samples with the cubic coeffs before transposing
This avoids having to transpose the cubic coefficients.
2014-12-15 17:13:31 -08:00
Chris Robinson a0e7ad493b Load samples with _mm_loadu_ps in the cubic SSE resamplers 2014-12-15 15:27:31 -08:00
Chris Robinson 177dd95686 Add SSE2 and SSE4.1 cubic resamplers 2014-12-15 13:58:41 -08:00
Chris Robinson a606bbc7f1 Use a lookup table to do cubic resampling 2014-12-15 12:23:28 -08:00
Chris Robinson 4b77f4ef07 Transpose the cubic matrix op 2014-12-15 11:06:44 -08:00
Chris Robinson e60e0761f5 Use the default channel layout in alffplay if one isn't specified 2014-12-12 06:10:23 -08:00
Chris Robinson 05529d66ae Don't pass float literals for unsigned ints 2014-12-06 18:58:16 -08:00
Chris Robinson 3d1f1ffd7f Fix a potential leak when opening a data file on Windows 2014-12-01 19:27:09 -08:00
Chris Robinson 0c74a14543 Remove IrSize from DirectParams 2014-11-29 03:32:25 -08:00
Chris Robinson cf50f44f4d Remove an unnecessary maxf() 2014-11-29 02:06:08 -08:00
Chris Robinson 6883619b14 Check mmdevice endpoint for being headphones 2014-11-27 17:43:37 -08:00
Chris Robinson 8d6d2e9a2b Avoid unnecessary uses of ALCdevice_Lock and ALCdevice_Unlock 2014-11-27 15:27:43 -08:00
Chris Robinson 82780a703b Set headphones when dsound reports headphones 2014-11-27 02:30:52 -08:00
Chris Robinson 1db8392344 Improve ambient gain calculations 2014-11-25 22:24:29 -08:00
Chris Robinson bdbf6613ef Support B-Format output with the wave file writer 2014-11-25 22:20:00 -08:00
Chris Robinson be476b3729 Halve the gain of the Cube8 coefficients 2014-11-25 18:11:45 -08:00
Chris Robinson 0a3e11b75e Fix reverb panning direction 2014-11-25 17:55:17 -08:00
Chris Robinson aa231d25a7 Shorten a couple lines 2014-11-25 16:47:39 -08:00
Chris Robinson efb5c5eecd Use linear gain stepping 2014-11-25 02:08:48 -08:00
Chris Robinson 42ef85d3f6 Pass the step count to the Update*Stepping methods 2014-11-25 01:38:27 -08:00
Chris Robinson 70ddca33ba Fix __ALSOFT_REVERSE_Z with non-HRTF output 2014-11-25 01:15:26 -08:00
Chris Robinson 8e6c131b36 Use a separate method to set initial HRTF coefficients 2014-11-24 22:26:42 -08:00
Chris Robinson cbe22763ee Require at least pulse client 0.9.16 2014-11-24 13:15:04 -08:00
Chris Robinson fc00c1c009 Trace PulseAudio's active port 2014-11-24 12:45:51 -08:00
Chris Robinson d6ebf5d1b6 Make CalcHrtfDelta more generic 2014-11-24 01:53:45 -08:00
Chris Robinson 13608d4d61 Move the voice's last position and gain out of the Hrtf container 2014-11-24 01:31:38 -08:00
Chris Robinson c37a971947 Rename step to steps 2014-11-24 00:24:12 -08:00
Chris Robinson 2111fad92f Use a macro to reduce code duplication 2014-11-23 23:45:30 -08:00
Chris Robinson 391ab19b02 Avoid using ALCdevice_Lock and ALCdevice_Unlock unnecessarily 2014-11-23 21:05:42 -08:00
Chris Robinson 65620e0ae5 Update a comment 2014-11-23 20:43:17 -08:00
Chris Robinson 2c533fdc36 Set headphones when pulse reports using the headphones port 2014-11-23 20:41:29 -08:00
Chris Robinson 033cf3dca9 Add a boolean to specify if a device is headphones 2014-11-23 18:23:24 -08:00
Chris Robinson 837b38b166 Swap delta and step, remove double semicolons 2014-11-23 18:07:19 -08:00
Chris Robinson 5464a5c630 Remove unused channel enums 2014-11-23 14:50:31 -08:00
Chris Robinson e5601a062e Remove the cube+diamond virtual layout 2014-11-23 14:43:29 -08:00
Chris Robinson 5796e7eae5 Add an option for a simpler virtual channel setup
With HRTF mixing, certain things are mixed to virtual channels to be filtered
with HRTF later. This allows for using an 8-channel cube instead of a 14-
channel cube+diamond.
2014-11-23 13:49:19 -08:00
Chris Robinson 45d6bb58a4 Partially revert "Use a different method for HRTF mixing"
The sound localization with virtual channel mixing was just too poor, so while
it's more costly to do per-source HRTF mixing, it's unavoidable if you want
good localization.

This is only partially reverted because having the virtual channel is still
beneficial, particularly with B-Format rendering and effect mixing which
otherwise skip HRTF processing. As before, the number of virtual channels can
potentially be customized, specifying more or less channels depending on the
system's needs.
2014-11-23 10:49:54 -08:00
Chris Robinson fc3608b381 Handle loopback devices when deciding to use HRTF 2014-11-23 08:38:33 -08:00
Chris Robinson 7e716bfc4a Remove a couple unnecessary traces 2014-11-22 22:33:19 -08:00
Chris Robinson 4f279557a6 Attempt to use BS2B when using headphones without HRTF 2014-11-22 22:28:36 -08:00
Chris Robinson ccbb3f069f Update some config descriptions 2014-11-22 22:00:44 -08:00
Chris Robinson d77c6153e7 Document the stereo-mode config option 2014-11-22 21:02:25 -08:00
Chris Robinson 312330dbaa Rework HRTF decision logic
This way takes into account a new stereo-mode config option, which when set to
"headphones" will default to using HRTF. Eventually the device will also be
able to specify if headphones are being used.
2014-11-22 20:52:08 -08:00
Chris Robinson f7d7afb7c1 Remove an unused macro 2014-11-22 16:39:08 -08:00
Chris Robinson a217be1539 Rename Voice's NumChannels to OutChannels 2014-11-22 16:23:08 -08:00
Chris Robinson 637993a793 Only update the necessary channels 2014-11-22 13:16:24 -08:00
Chris Robinson 4ebf0ad717 Mix DirectChannel sources to the non-virtual channel buffers 2014-11-22 13:10:32 -08:00
Chris Robinson dece86f61f Store the number of output channels in the voice 2014-11-22 13:08:19 -08:00
Chris Robinson fb511351df Remove an unnecessary union container 2014-11-22 12:58:54 -08:00
Chris Robinson e41cfeca84 Move a warning check earlier 2014-11-22 04:51:34 -08:00
Chris Robinson a27e5e1652 Use a different method for HRTF mixing
This new method mixes sources normally into a 14-channel buffer with the
channels placed all around the listener. HRTF is then applied to the channels
given their positions and written to a 2-channel buffer, which gets written out
to the device.

This method has the benefit that HRTF processing becomes more scalable. The
costly HRTF filters are applied to the 14-channel buffer after the mix is done,
turning it into a post-process with a fixed overhead. Mixing sources is done
with normal non-HRTF methods, so increasing the number of playing sources only
incurs normal mixing costs.

Another benefit is that it improves B-Format playback since the soundfield gets
mixed into speakers covering all three dimensions, which then get filtered
based on their locations.

The main downside to this is that the spatial resolution of the HRTF dataset
does not play a big role anymore. However, the hope is that with ambisonics-
based panning, the perceptual position of panned sounds will still be good. It
is also an option to increase the number of virtual channels for systems that
can handle it, or maybe even decrease it for weaker systems.
2014-11-22 04:20:17 -08:00
Chris Robinson 38383671d7 Balance the left and right channels for quad output 2014-11-21 14:41:02 -08:00
Chris Robinson c8d82712da Reformat a couple lines 2014-11-21 14:40:10 -08:00
Chris Robinson ccba33169b Initialize panning after setting up HRTF 2014-11-21 14:35:19 -08:00
Chris Robinson 4ad6905c13 Allocate the DryBuffer dynamically 2014-11-21 13:45:57 -08:00
Chris Robinson d608f0e9f0 Only warn when no data file could be opened 2014-11-19 13:07:37 -08:00
Chris Robinson 6e198b3476 Align UpdateSize for Neon, too 2014-11-17 21:33:09 -08:00
Chris Robinson 1dcc54c986 Don't force UpdateSize to a multiple of 4 after buffer metrics were set 2014-11-17 21:23:29 -08:00
Chris Robinson 40a08bed4f Always get the pulseaudio sink info on device reset 2014-11-17 17:57:52 -08:00
Chris Robinson d4e52b64d8 Mark a function as const 2014-11-15 23:20:38 -08:00
Chris Robinson 308e6b697a Remove the unused angle and elevation from the device channel config 2014-11-15 04:26:11 -08:00
Chris Robinson b10085cc63 Slightly increase the ambient gain volume 2014-11-15 04:11:22 -08:00
Chris Robinson 9681c5a2bb Interpolate directional reverb gains with ambient based on vector length 2014-11-15 00:21:26 -08:00
Chris Robinson 55a13456d4 Add a method to convert channel enums to a label string 2014-11-15 00:19:56 -08:00
Chris Robinson a095645970 Don't bother with LFE in the channel setup, set the values explicitly 2014-11-12 19:26:53 -08:00
Chris Robinson 283c2b20d4 Add a toolchain for Android cross-compiling 2014-11-10 17:56:48 -08:00
Chris Robinson e036cca1c9 Add the ability to use custom output channel coefficients
I'm not sure exactly how I want to do this yet, but this is a good starting
point.
2014-11-10 17:53:42 -08:00
Chris Robinson 1c4055419e Don't use the device lock/unlock wrappers needlessly 2014-11-08 18:04:01 -08:00
Chris Robinson 3eab44001d Remove the unused layout config options 2014-11-08 15:44:07 -08:00
Chris Robinson a50ba603ab Remove the unused wide-stereo option 2014-11-08 06:36:45 -08:00
Chris Robinson c053617066 Reorder some options in the config sample 2014-11-08 05:59:08 -08:00
Chris Robinson 3904a3f033 Allow selecting the 5.1-rear channel config from the config file 2014-11-08 05:53:17 -08:00
Chris Robinson 74994e9d11 Remove the long-deprecated 'format' config option 2014-11-08 05:41:55 -08:00
Chris Robinson 907cd3dd01 Add a workaround for compilers without __COUNTER__
This can make GCC pretty noisey, complaining "declaration does not declare
anything" for each static_assert, but it should still function on such older
compilers.
2014-11-07 19:43:14 -08:00
Chris Robinson b34a374fa7 Only enable the BS2B filter with stereo output 2014-11-07 16:11:47 -08:00
Chris Robinson 35eacf058e Go to the next voice when a source is stopped 2014-11-07 16:05:06 -08:00
Chris Robinson 1a326c758b Rename a couple parameters 2014-11-07 16:00:07 -08:00
Chris Robinson 57387505d8 Move a declaration 2014-11-07 15:47:41 -08:00
Chris Robinson 01a1e6f102 Fallback to 16-bit samples in alffmpeg if a float32 format isn't supported 2014-11-07 04:50:16 -08:00
Chris Robinson 3f7cb8392e Pas the output device channel count to ALeffectState::process 2014-11-07 03:43:33 -08:00
Chris Robinson 713e9dd4cc Rename speakers to channels, and remove an old incorrect comment 2014-11-07 03:12:32 -08:00
Chris Robinson 4c3f27193f Use a separate macro for the max output channel count 2014-11-07 02:18:24 -08:00
Chris Robinson 6c954e71db Use shorter display names for the surround sound config options 2014-11-07 01:13:20 -08:00
Chris Robinson 0051a96376 Fix 5.1 surround sound
Apparently, 5.1 surround sound is supposed to use the "side" channels, not the
back channels, and we've been wrong this whole time. That means the "5.1 Side"
is actually the correct 5.1 setup, and using the back channels is anomalous.

Additionally, this means the 5.1 buffer format should also use the the side
channels instead of the back channels.

A final note: the 5.1 mixing coefficients are changed so both use the original
5.1 surround sound set (with the surround channels at +/-110 degrees). So the
only difference now between 5.1 "side" and 5.1 "back" is the channel labels.
2014-11-07 00:54:16 -08:00
Chris Robinson 7a98e92117 Update cross-compiler toolchain to work better with Qt 2014-11-06 23:18:42 -08:00
Chris Robinson ad247fe5cd Initialize a couple variables mingw complains about 2014-11-06 23:05:21 -08:00
Chris Robinson 4126ccf744 Use SZFMT for printing size_t 2014-11-06 23:02:39 -08:00
Chris Robinson 61a56ce120 Play zero-distance/zero-radius sources from the front 2014-11-05 04:48:48 -08:00
Chris Robinson 6aed34cfb5 Don't use FrontLeft and FrontRight to reference the dry buffer 2014-11-05 04:11:18 -08:00
Chris Robinson 59a2858239 Don't increment the output buffer in the Write_ methods 2014-11-05 04:07:06 -08:00
Chris Robinson 0185bdd8ca Support 5.1 and 7.1 in alffplay 2014-11-05 03:55:36 -08:00
Chris Robinson da765ec53b Remove the channel name from ChannelConfig 2014-11-05 03:46:00 -08:00
Chris Robinson e39117467c Fix panning of multi-channel sources 2014-11-05 03:06:32 -08:00
Chris Robinson c834c3fae5 Set gains using the device channel index 2014-11-05 02:54:11 -08:00
Chris Robinson e5016f814a Add LFE to the speaker arrays 2014-11-04 04:33:35 -08:00
Chris Robinson 6083a684d1 Use a method to set omni-directional channel gains 2014-11-04 03:33:35 -08:00
Chris Robinson d8cfdb3f34 Use COUNTOF to set the number of speakers 2014-11-04 03:00:15 -08:00
Chris Robinson deba996769 Add some missing breaks 2014-11-02 02:30:45 -08:00
Chris Robinson 857d68b0ce Minor update for ambisonics coefficients
Small tweaks to balance the left and right speakers, and change
unreasonably small values to 0.
2014-11-02 00:58:48 -07:00
Chris Robinson 9d82ab9d1f Use the copy resampler only when there's no sub-sample offset 2014-11-02 00:27:26 -07:00
Chris Robinson c82f39decd Avoid the ALCdevice_Lock/Unlock wrapper in some places 2014-11-01 15:55:18 -07:00
Chris Robinson 28c1ec830e Support AL_EXT_MULAW_BFORMAT 2014-10-31 22:52:30 -07:00
Chris Robinson dd6e622206 Add AL_EXT_MULAW_BFORMAT to alext.h 2014-10-31 22:46:34 -07:00
Chris Robinson 3d2853274d Support B-Format source rotation with AL_ORIENTATION 2014-10-31 22:43:13 -07:00
Chris Robinson 336aba6f1f Rename the source's Orientation to Direction 2014-10-31 17:24:46 -07:00
Chris Robinson ac51c9cce6 Add preliminary AL_EXT_BFORMAT support
Currently missing the AL_ORIENTATION source property. Gain stepping also does
not work.
2014-10-31 17:18:45 -07:00
Chris Robinson 1c0596c233 Check the absolute gain value for silence
Future B-Format support will be using negative gains, which still need to be
applied.
2014-10-31 16:55:19 -07:00
Chris Robinson 0e09e779f9 Use %zu (C99) for printing size_t 2014-10-30 12:27:59 -07:00
Chris Robinson 3585021a18 Minor clarification for __ALSOFT_SUSPEND_CONTEXT 2014-10-13 10:48:54 -07:00
Chris Robinson 87dcf65013 Use the minimum of the two string lengths for comparison 2014-10-13 07:04:36 -07:00
Chris Robinson 84582ceb03 Use more appropriate size types 2014-10-13 07:00:43 -07:00
Chris Robinson f05a2b86cd Don't attempt to match a channel input to output
I don't like this, but it's currently necessary. The problem is that the
ambisonics-based panning does not maintain consistent energy output, which
causes sounds mapped directly to an output channel to be louder compared to
when being panned. The inconcistent energy output is partly by design, as it's
trying to render a full 3D sound field and at least attempts to correct for
imbalanced speaker layouts.
2014-10-12 12:39:27 -07:00
Chris Robinson 4320a1483b Make alcSuspendContext and alcProcessContext batch updates
This behavior better matches Creative's hardware drivers and Rapture3D's OpenAL
driver. A compatibility environment variable is provided to restore the old
no-op behavior for any app that behaves badly from this change (set
__ALSOFT_SUSPEND_CONTEXT to "ignore").

If too many apps have a problem with this, the default behavior may need to be
changed to ignore, with the env var providing an option to defer/batch instead.
2014-10-12 09:17:13 -07:00
Chris Robinson a77387b549 Avoid taking the square-root of the ambient gain
Although it is more correct for preserving the apparent volume, the ambisonics-
based panning does not work on the same power scale, making it louder by
comparison.
2014-10-11 09:35:32 -07:00
Chris Robinson 79163b0755 Fix stereo device configuration 2014-10-11 04:07:33 -07:00
Chris Robinson 70b23ab77f Add a helper to search for a channel index by name 2014-10-02 21:19:34 -07:00
Chris Robinson 79fb86d8f7 Store default speaker configurations in a struct 2014-10-02 20:25:30 -07:00
Chris Robinson 95ba18cf4e Make ComputeAngleGains use ComputeDirectionalGains 2014-10-02 18:05:42 -07:00
Chris Robinson 9377d0f237 Don't use ComputeAngleGains for SetGains 2014-10-02 01:02:25 -07:00
Chris Robinson 918c773a59 Use helpers to set the gain step values 2014-10-02 00:56:29 -07:00
Chris Robinson 3aa63e3e9b Use VECTOR_FIND_IF instead of manual loops 2014-09-30 22:42:07 -07:00
Chris Robinson 2d89d33dd1 Add a cast for MSVC 2014-09-30 21:50:29 -07:00
Chris Robinson af7330de88 Copy the null terminator from the string instead of appending it 2014-09-30 21:49:29 -07:00
Chris Robinson d82d6c701d Use size_t for the vector size and capacity 2014-09-30 21:47:22 -07:00
Chris Robinson 4d36ef65b2 Use an ambisonics-based panning method
For mono sources, third-order ambisonics is utilized to generate panning gains.
The general idea is that a panned mono sound can be encoded into b-format
ambisonics as:

w[i] = sample[i] * 0.7071;
x[i] = sample[i] * dir[0];
y[i] = sample[i] * dir[1];
...

and subsequently rendered using:

output[chan][i] = w[i] * w_coeffs[chan] +
                  x[i] * x_coeffs[chan] +
                  y[i] * y_coeffs[chan] +
                  ...;

By reordering the math, channel gains can be generated by doing:

gain[chan] = 0.7071 * w_coeffs[chan] +
             dir[0] * x_coeffs[chan] +
             dir[1] * y_coeffs[chan] +
             ...;

which then get applied as normal:

output[chan][i] = sample[i] * gain[chan];

One of the reasons to use ambisonics for panning is that it provides arguably
better reproduction for sounds emanating from between two speakers. As well,
this makes it easier to pan in all 3 dimensions, with for instance a "3D7.1" or
8-channel cube speaker configuration by simply providing the necessary
coefficients (this will need some work since some methods still use angle-based
panpot, particularly multi-channel sources).

Unfortunately, the math to reliably generate the coefficients for a given
speaker configuration is too costly to do at run-time. They have to be pre-
generated based on a pre-specified speaker arangement, which means the config
options for tweaking speaker angles are no longer supportable. Eventually I
hope to provide config options for custom coefficients, which can either be
generated and written in manually, or via alsoft-config from user-specified
speaker positions.

The current default set of coefficients were generated using the MATLAB scripts
(compatible with GNU Octave) from the excellent Ambisonic Decoder Toolbox, at
https://bitbucket.org/ambidecodertoolbox/adt/
2014-09-30 07:33:13 -07:00
Chris Robinson 4955824c2d Use better GUI item names for the resampler option 2014-09-26 20:45:46 -07:00
Chris Robinson 57623741f6 Show prettier names in the alsoft-config sample format combo boxes 2014-09-13 23:12:40 -07:00
Chris Robinson 4e66224d6c Combine some fields into a struct 2014-09-10 17:53:01 -07:00
Chris Robinson 01adfde199 Invert the ChannelOffsets array 2014-09-10 16:52:54 -07:00
Chris Robinson 49cb2421c7 Use a wave file channel mask based on the actual format 2014-09-10 08:30:07 -07:00
Chris Robinson d714b90962 Add AL_EXT_BFORMAT to alext.h 2014-09-09 21:40:06 -07:00
Chris Robinson 4a712dc612 Remove some unnecessary config options 2014-09-08 15:29:07 -07:00
Chris Robinson 7a31847f7d Use a vector instead of a manual dynamic array 2014-09-08 15:24:27 -07:00
Chris Robinson 5ff1730e02 Don't modify a capture device's format
OpenAL's capture API guarantees the application gets the format requested, or
else the device will fail to open. The only valid change is that the capture
buffer can be larger than requested.
2014-09-08 14:29:59 -07:00
Chris Robinson a234fc11e5 Remove the GetLatency method from the old BackendFuncs 2014-09-08 06:59:58 -07:00
Chris Robinson 9dacd89a02 Convert the winmm backend to the new backend API 2014-09-08 06:46:20 -07:00
Chris Robinson 2be33d8a77 Only pass nano seconds to al_nssleep 2014-09-08 04:37:52 -07:00
Chris Robinson f38e88bc29 Allow optional memory ordering to atomic methods
Currently only C11 atomics make use of the memory order. If not
specified, it defaults to almemory_order_seq_cst.
2014-09-07 00:42:50 -07:00
Chris Robinson 4d19d4f416 Check that atomic_load works with a const _Atomic
The original C11 spec does not allow atomic_load to work on const _Atomic
variables, which we sometimes do in alGet* methods, despite the fact that it
does not modify the variable. An update to the C spec corrects this oversight,
and GCC 4.9 has allowed it anyway, while Clang 3.4 does not.
2014-09-05 17:47:32 -07:00
Chris Robinson ab829fd906 Fix the __get_cpuid cmake check 2014-09-05 02:57:24 -07:00
Chris Robinson 0b250b246d Use a standard pointer-sized integer type 2014-09-04 23:23:48 -07:00
Chris Robinson 4b53d0e90c Make ExchangeInt and ExchangePtr non-atomic 2014-09-03 17:37:07 -07:00
Chris Robinson 2f2768e7c3 Make the fontsound's buffer and link fields atomic 2014-09-03 16:29:17 -07:00
Chris Robinson 30e01a7dba Protect alProcessUpdatesSOFT with a lock 2014-09-03 16:02:00 -07:00
Chris Robinson 7f5497b863 Use proper atomics for the thunk array 2014-09-03 15:49:31 -07:00
Chris Robinson 5840f5e76f Make the buffer's pack and unpack properties atomic 2014-09-03 15:40:15 -07:00
Chris Robinson ba827cdfff Fix Neon mixer definition 2014-08-31 23:46:43 -07:00
Chris Robinson 705c60d924 Use al_calloc/al_free to allocate contexts and voices 2014-08-30 20:59:46 -07:00
Chris Robinson f04b15692e Setup the HRTF format before tracing the pre-reset format 2014-08-29 19:34:20 -07:00
Chris Robinson f18d2f2445 Check mmdevapi device ids to match the default device
Seems Windows can return different IMMDevice object pointers for the same
endpoint.
2014-08-28 18:22:17 -07:00
Chris Robinson 2b87ffece5 Return the correct default capture device name 2014-08-26 14:52:36 -07:00
Chris Robinson 368bf037dd Check the given CoreAudio capture device name 2014-08-26 14:50:14 -07:00
Chris Robinson 99b71278db Remove a couple unnecessary typedefs 2014-08-24 23:48:42 -07:00
Chris Robinson a3bba200c0 Convert the wave writer backend to the new API 2014-08-24 22:20:02 -07:00
Chris Robinson 4f7b3f0c38 Use al_malloc/al_free for default allocators 2014-08-24 20:16:28 -07:00
Chris Robinson 0d0222e91b Include the common sources when building statically 2014-08-23 13:37:14 -07:00
Chris Robinson 259e265920 Rename activesource to voice 2014-08-21 03:41:13 -07:00
Chris Robinson 65d2e0eb8d Use an array of objects for active sources instead of pointers 2014-08-21 02:27:56 -07:00
Chris Robinson b92e643e97 Use a NULL source for inactive activesources
Also only access the activesource's source field once per update.
2014-08-21 00:29:42 -07:00
Chris Robinson 3a6baab495 Use the current binary dir for shared function checks too 2014-08-20 13:47:56 -07:00
Jesper Särnesjö ba9995a8de Use directories relative to current project in CheckFileOffsetBits.cmake
Enables building OpenAL Soft in a subdirectory of another project.
2014-08-20 13:31:24 -07:00
Chris Robinson e3d72ccd15 Support brace-enclosed environment variable names
This makes it possible to append alpha-numeric characters directly to an
environment variable value, e.g. ${FOO}bar will use "FOO" as the variable name
and keep the "bar" as-is, whereas $FOObar will take "FOObar" as the variable
name.
2014-08-19 16:54:53 -07:00
François Cami 3c13e1e333 Update COPYING to the latest https://www.gnu.org/licenses/old-licenses/lgpl-2.0.txt to fix the FSF' address Fix the FSF' address in the source 2014-08-18 11:34:29 -07:00
Chris Robinson 864528bb7b Use the POSITION_INDEPENDENT_CODE target property when available 2014-08-16 20:46:55 -07:00
Chris Robinson 4a7e04add3 Don't force specific compile flags for the different build types 2014-08-16 19:57:56 -07:00
Chris Robinson 8630e8c7fc Don't try to include stdalign.h if C11 _Alignas isn't available
Some compilers will allow including stdalign.h, and even define alignas to
_Alignas, even if that C11 feature is unavailable (e.g. because it requires a
suitable -std= setting).
2014-08-16 10:17:30 -07:00
Chris Robinson dd1df64537 Search for the correct include and lib directories for the DX SDK
And only set them when needed.
2014-08-15 14:26:06 -07:00
Chris Robinson d60ce35846 Add a changelog, detailing notable changes for each release 2014-08-15 00:06:59 -07:00
171 changed files with 41506 additions and 22088 deletions
+70
View File
@@ -0,0 +1,70 @@
language: c
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
+1862 -959
View File
File diff suppressed because it is too large Load Diff
+1428 -919
View File
File diff suppressed because it is too large Load Diff
+173 -36
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -33,11 +33,13 @@
#include <ctype.h>
#include <string.h>
#ifdef _WIN32_IE
#include <windows.h>
#include <shlobj.h>
#endif
#include "alMain.h"
#include "compat.h"
#include "bool.h"
typedef struct ConfigEntry {
@@ -137,13 +139,21 @@ static char *expdup(const char *str)
}
else
{
bool hasbraces;
char envname[1024];
size_t k = 0;
hasbraces = (*str == '{');
if(hasbraces) str++;
while((isalnum(*str) || *str == '_') && k < sizeof(envname)-1)
envname[k++] = *(str++);
envname[k++] = '\0';
if(hasbraces && *str != '}')
continue;
if(hasbraces) str++;
if((addstr=getenv(envname)) == NULL)
continue;
addstrlen = strlen(addstr);
@@ -192,12 +202,8 @@ static void LoadConfigFromFile(FILE *f)
char key[256] = "";
char value[256] = "";
comment = strchr(buffer, '#');
if(comment) *(comment++) = 0;
line = rstrip(lstrip(buffer));
if(!line[0])
continue;
if(!line[0]) continue;
if(line[0] == '[')
{
@@ -205,10 +211,21 @@ static void LoadConfigFromFile(FILE *f)
char *endsection;
endsection = strchr(section, ']');
if(!endsection || section == endsection || endsection[1] != 0)
if(!endsection || section == endsection)
{
ERR("config parse error: bad line \"%s\"\n", line);
continue;
ERR("config parse error: bad line \"%s\"\n", line);
continue;
}
if(endsection[1] != 0)
{
char *end = endsection+1;
while(isspace(*end))
++end;
if(*end != 0 && *end != '#')
{
ERR("config parse error: bad line \"%s\"\n", line);
continue;
}
}
*endsection = 0;
@@ -216,13 +233,71 @@ 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;
}
continue;
}
comment = strchr(line, '#');
if(comment) *(comment++) = 0;
if(!line[0]) continue;
if(sscanf(line, "%255[^=] = \"%255[^\"]\"", key, value) == 2 ||
sscanf(line, "%255[^=] = '%255[^\']'", key, value) == 2 ||
sscanf(line, "%255[^=] = %255[^\n]", key, value) == 2)
@@ -292,15 +367,31 @@ void ReadALConfig(void)
{
WCHAR buffer[PATH_MAX];
const WCHAR *str;
al_string ppath;
FILE *f;
if(SHGetSpecialFolderPathW(NULL, buffer, CSIDL_APPDATA, FALSE) != FALSE)
{
size_t p = lstrlenW(buffer);
_snwprintf(buffer+p, PATH_MAX-p, L"\\alsoft.ini");
al_string filepath = AL_STRING_INIT_STATIC();
alstr_copy_wcstr(&filepath, buffer);
alstr_append_cstr(&filepath, "\\alsoft.ini");
TRACE("Loading config %ls...\n", buffer);
f = _wfopen(buffer, L"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);
@@ -310,20 +401,27 @@ void ReadALConfig(void)
if((str=_wgetenv(L"ALSOFT_CONF")) != NULL && *str)
{
TRACE("Loading config %ls...\n", str);
f = _wfopen(str, L"rt");
al_string filepath = AL_STRING_INIT_STATIC();
alstr_copy_wcstr(&filepath, str);
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);
}
alstr_reset(&ppath);
}
#else
void ReadALConfig(void)
{
char buffer[PATH_MAX];
const char *str;
al_string ppath;
FILE *f;
str = "/etc/openal/alsoft.conf";
@@ -403,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);
@@ -413,6 +524,8 @@ void ReadALConfig(void)
fclose(f);
}
}
alstr_reset(&ppath);
}
#endif
@@ -428,7 +541,7 @@ void FreeALConfig(void)
free(cfgBlock.entries);
}
const char *GetConfigValue(const char *blockName, const char *keyName, const char *def)
const char *GetConfigValue(const char *devName, const char *blockName, const char *keyName, const char *def)
{
unsigned int i;
char key[256];
@@ -437,16 +550,26 @@ const char *GetConfigValue(const char *blockName, const char *keyName, const cha
return def;
if(blockName && strcasecmp(blockName, "general") != 0)
snprintf(key, sizeof(key), "%s/%s", blockName, keyName);
{
if(devName)
snprintf(key, sizeof(key), "%s/%s/%s", blockName, devName, keyName);
else
snprintf(key, sizeof(key), "%s/%s", blockName, keyName);
}
else
{
strncpy(key, keyName, sizeof(key)-1);
key[sizeof(key)-1] = 0;
if(devName)
snprintf(key, sizeof(key), "%s/%s", devName, keyName);
else
{
strncpy(key, keyName, sizeof(key)-1);
key[sizeof(key)-1] = 0;
}
}
for(i = 0;i < cfgBlock.entryCount;i++)
{
if(strcasecmp(cfgBlock.entries[i].key, key) == 0)
if(strcmp(cfgBlock.entries[i].key, key) == 0)
{
TRACE("Found %s = \"%s\"\n", key, cfgBlock.entries[i].value);
if(cfgBlock.entries[i].value[0])
@@ -455,46 +578,50 @@ const char *GetConfigValue(const char *blockName, const char *keyName, const cha
}
}
TRACE("Key %s not found\n", key);
return def;
if(!devName)
{
TRACE("Key %s not found\n", key);
return def;
}
return GetConfigValue(NULL, blockName, keyName, def);
}
int ConfigValueExists(const char *blockName, const char *keyName)
int ConfigValueExists(const char *devName, const char *blockName, const char *keyName)
{
const char *val = GetConfigValue(blockName, keyName, "");
const char *val = GetConfigValue(devName, blockName, keyName, "");
return !!val[0];
}
int ConfigValueStr(const char *blockName, const char *keyName, const char **ret)
int ConfigValueStr(const char *devName, const char *blockName, const char *keyName, const char **ret)
{
const char *val = GetConfigValue(blockName, keyName, "");
const char *val = GetConfigValue(devName, blockName, keyName, "");
if(!val[0]) return 0;
*ret = val;
return 1;
}
int ConfigValueInt(const char *blockName, const char *keyName, int *ret)
int ConfigValueInt(const char *devName, const char *blockName, const char *keyName, int *ret)
{
const char *val = GetConfigValue(blockName, keyName, "");
const char *val = GetConfigValue(devName, blockName, keyName, "");
if(!val[0]) return 0;
*ret = strtol(val, NULL, 0);
return 1;
}
int ConfigValueUInt(const char *blockName, const char *keyName, unsigned int *ret)
int ConfigValueUInt(const char *devName, const char *blockName, const char *keyName, unsigned int *ret)
{
const char *val = GetConfigValue(blockName, keyName, "");
const char *val = GetConfigValue(devName, blockName, keyName, "");
if(!val[0]) return 0;
*ret = strtoul(val, NULL, 0);
return 1;
}
int ConfigValueFloat(const char *blockName, const char *keyName, float *ret)
int ConfigValueFloat(const char *devName, const char *blockName, const char *keyName, float *ret)
{
const char *val = GetConfigValue(blockName, keyName, "");
const char *val = GetConfigValue(devName, blockName, keyName, "");
if(!val[0]) return 0;
#ifdef HAVE_STRTOF
@@ -505,9 +632,19 @@ int ConfigValueFloat(const char *blockName, const char *keyName, float *ret)
return 1;
}
int GetConfigValueBool(const char *blockName, const char *keyName, int def)
int ConfigValueBool(const char *devName, const char *blockName, const char *keyName, int *ret)
{
const char *val = GetConfigValue(blockName, keyName, "");
const char *val = GetConfigValue(devName, blockName, keyName, "");
if(!val[0]) return 0;
*ret = (strcasecmp(val, "true") == 0 || strcasecmp(val, "yes") == 0 ||
strcasecmp(val, "on") == 0 || atoi(val) != 0);
return 1;
}
int GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, int def)
{
const char *val = GetConfigValue(devName, blockName, keyName, "");
if(!val[0]) return !!def;
return (strcasecmp(val, "true") == 0 || strcasecmp(val, "yes") == 0 ||
+262 -74
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -25,105 +25,293 @@
#include "alMain.h"
#include "threads.h"
#include "almalloc.h"
#include "compat.h"
struct RingBuffer {
ALubyte *mem;
/* 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 {
ATOMIC(size_t) write_ptr;
ATOMIC(size_t) read_ptr;
size_t size;
size_t size_mask;
size_t elem_size;
int mlocked;
ALsizei frame_size;
ALsizei length;
ALint read_pos;
ALint write_pos;
almtx_t mtx;
alignas(16) char buf[];
};
RingBuffer *CreateRingBuffer(ALsizei frame_size, ALsizei length)
/* Create a new ringbuffer to hold at least `sz' elements of `elem_sz' bytes.
* The number of elements is rounded up to the next power of two. */
ll_ringbuffer_t *ll_ringbuffer_create(size_t sz, size_t elem_sz)
{
RingBuffer *ring = calloc(1, sizeof(*ring) + ((length+1) * frame_size));
if(ring)
{
ring->mem = (ALubyte*)(ring+1);
ll_ringbuffer_t *rb;
ALuint power_of_two;
ring->frame_size = frame_size;
ring->length = length+1;
ring->read_pos = 0;
ring->write_pos = 0;
power_of_two = NextPowerOf2(sz);
if(power_of_two < sz)
return NULL;
almtx_init(&ring->mtx, almtx_plain);
}
return ring;
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->mlocked = 0;
return rb;
}
void DestroyRingBuffer(RingBuffer *ring)
/* Free all data associated with the ringbuffer `rb'. */
void ll_ringbuffer_free(ll_ringbuffer_t *rb)
{
if(ring)
if(rb)
{
almtx_destroy(&ring->mtx);
free(ring);
#ifdef USE_MLOCK
if(rb->mlocked)
munlock(rb, sizeof(*rb) + rb->size*rb->elem_size);
#endif /* USE_MLOCK */
al_free(rb);
}
}
ALsizei RingBufferSize(RingBuffer *ring)
/* Lock the data block of `rb' using the system call 'mlock'. */
int ll_ringbuffer_mlock(ll_ringbuffer_t *rb)
{
ALsizei s;
almtx_lock(&ring->mtx);
s = (ring->write_pos-ring->read_pos+ring->length) % ring->length;
almtx_unlock(&ring->mtx);
return s;
#ifdef USE_MLOCK
if(!rb->mlocked && mlock(rb, sizeof(*rb) + rb->size*rb->elem_size))
return -1;
#endif /* USE_MLOCK */
rb->mlocked = 1;
return 0;
}
void WriteRingBuffer(RingBuffer *ring, const ALubyte *data, ALsizei len)
/* Reset the read and write pointers to zero. This is not thread safe. */
void ll_ringbuffer_reset(ll_ringbuffer_t *rb)
{
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);
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);
}
void ReadRingBuffer(RingBuffer *ring, ALubyte *data, ALsizei len)
/* Return the number of elements available for reading. This is the number of
* elements in front of the read pointer and behind the write pointer. */
size_t ll_ringbuffer_read_space(const ll_ringbuffer_t *rb)
{
int remain;
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 = 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;
}
almtx_lock(&ring->mtx);
/* 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;
size_t n1, n2;
remain = ring->length - ring->read_pos;
if(remain < len)
free_cnt = ll_ringbuffer_read_space(rb);
if(free_cnt == 0) return 0;
to_read = (cnt > free_cnt) ? free_cnt : cnt;
read_ptr = ATOMIC_LOAD(&rb->read_ptr, almemory_order_relaxed) & rb->size_mask;
cnt2 = read_ptr + to_read;
if(cnt2 > rb->size)
{
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);
n1 = rb->size - read_ptr;
n2 = cnt2 & rb->size_mask;
}
else
memcpy(data, ring->mem+(ring->read_pos*ring->frame_size), len*ring->frame_size);
{
n1 = to_read;
n2 = 0;
}
ring->read_pos += len;
ring->read_pos %= ring->length;
almtx_unlock(&ring->mtx);
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[(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;
}
/* The copying data reader w/o read pointer advance. Copy at most `cnt'
* elements from `rb' to `dest'. Returns the actual number of elements copied.
*/
size_t ll_ringbuffer_peek(ll_ringbuffer_t *rb, char *dest, size_t cnt)
{
size_t free_cnt;
size_t cnt2;
size_t to_read;
size_t n1, n2;
size_t read_ptr;
free_cnt = ll_ringbuffer_read_space(rb);
if(free_cnt == 0) return 0;
to_read = (cnt > free_cnt) ? free_cnt : cnt;
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 - read_ptr;
n2 = cnt2 & rb->size_mask;
}
else
{
n1 = to_read;
n2 = 0;
}
memcpy(dest, &rb->buf[read_ptr*rb->elem_size], n1*rb->elem_size);
if(n2)
{
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;
}
/* The copying data writer. Copy at most `cnt' elements to `rb' from `src'.
* 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;
size_t n1, n2;
free_cnt = ll_ringbuffer_write_space(rb);
if(free_cnt == 0) return 0;
to_write = (cnt > free_cnt) ? free_cnt : cnt;
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 - write_ptr;
n2 = cnt2 & rb->size_mask;
}
else
{
n1 = to_write;
n2 = 0;
}
memcpy(&rb->buf[write_ptr*rb->elem_size], src, n1*rb->elem_size);
write_ptr += n1;
if(n2)
{
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)
{
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)
{
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
* at `vec' to hold the current readable data at `rb'. If the readable data is
* in one segment the second segment has zero length. */
void ll_ringbuffer_get_read_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t * vec)
{
size_t free_cnt;
size_t cnt2;
size_t w, r;
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].len = rb->size - r;
vec[1].buf = (char*)rb->buf;
vec[1].len = cnt2 & rb->size_mask;
}
else
{
/* Single part vector: just the rest of the buffer */
vec[0].buf = (char*)&rb->buf[r*rb->elem_size];
vec[0].len = free_cnt;
vec[1].buf = NULL;
vec[1].len = 0;
}
}
/* The non-copying data writer. `vec' is an array of two places. Set the values
* at `vec' to hold the current writeable data at `rb'. If the writeable data
* is in one segment the second segment has zero length. */
void ll_ringbuffer_get_write_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t *vec)
{
size_t free_cnt;
size_t cnt2;
size_t w, r;
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].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].len = free_cnt;
vec[1].buf = NULL;
vec[1].len = 0;
}
}
+19 -15
View File
@@ -8,38 +8,42 @@
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 ALsizei 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 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 */
+142 -92
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -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,15 +276,49 @@ 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("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));
ConfigValueStr("alsa", prefix_name(stream), &main_prefix);
ConfigValueStr(NULL, "alsa", prefix_name(stream), &main_prefix);
while(card >= 0)
{
const char *card_prefix = main_prefix;
@@ -304,7 +342,7 @@ static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList)
cardid = snd_ctl_card_info_get_id(info);
snprintf(name, sizeof(name), "%s-%s", prefix_name(stream), cardid);
ConfigValueStr("alsa", name, &card_prefix);
ConfigValueStr(NULL, "alsa", name, &card_prefix);
dev = -1;
while(1)
@@ -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;
@@ -330,18 +369,18 @@ static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList)
devname = snd_pcm_info_get_name(pcminfo);
snprintf(name, sizeof(name), "%s-%s-%d", prefix_name(stream), cardid, dev);
ConfigValueStr("alsa", name, &device_prefix);
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,17 +671,17 @@ 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
{
name = alsaDevice;
driver = GetConfigValue("alsa", "device", "default");
driver = GetConfigValue(NULL, "alsa", "device", "default");
}
TRACE("Opening device \"%s\"\n", driver);
@@ -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;
}
@@ -677,6 +718,7 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
unsigned int rate;
const char *funcerr;
int allowmmap;
int dir;
int err;
switch(device->FmtType)
@@ -704,7 +746,7 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
break;
}
allowmmap = GetConfigValueBool("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;
@@ -748,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,
@@ -761,17 +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(snd_pcm_hw_params_set_rate_resample(self->pcmHandle, hp, 0) < 0)
ERR("Failed to disable ALSA resampler\n");
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)
@@ -784,7 +833,9 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
/* retrieve configuration info */
CHECK(snd_pcm_hw_params_get_access(hp, &access));
CHECK(snd_pcm_hw_params_get_period_size(hp, &periodSizeInFrames, NULL));
CHECK(snd_pcm_hw_params_get_periods(hp, &periods, NULL));
CHECK(snd_pcm_hw_params_get_periods(hp, &periods, &dir));
if(dir != 0)
WARN("Inexact period count: %u (%d)\n", periods, dir);
snd_pcm_hw_params_free(hp);
hp = NULL;
@@ -834,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");
@@ -856,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;
}
@@ -879,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;
}
@@ -907,7 +965,7 @@ typedef struct ALCcaptureAlsa {
ALsizei size;
ALboolean doCapture;
RingBuffer *ring;
ll_ringbuffer_t *ring;
snd_pcm_sframes_t last_avail;
} ALCcaptureAlsa;
@@ -921,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)
@@ -955,17 +1013,17 @@ 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
{
name = alsaDevice;
driver = GetConfigValue("alsa", "capture", "default");
driver = GetConfigValue(NULL, "alsa", "capture", "default");
}
TRACE("Opening device \"%s\"\n", driver);
@@ -1017,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) */
@@ -1039,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;
@@ -1065,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);
@@ -1077,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;
}
@@ -1114,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;
}
@@ -1135,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;
}
@@ -1160,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;
}
@@ -1238,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));
@@ -1267,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);
@@ -1352,25 +1412,15 @@ static ALCbackend* ALCalsaBackendFactory_createBackend(ALCalsaBackendFactory* UN
if(type == ALCbackend_Playback)
{
ALCplaybackAlsa *backend;
backend = ALCplaybackAlsa_New(sizeof(*backend));
NEW_OBJ(backend, ALCplaybackAlsa)(device);
if(!backend) return NULL;
memset(backend, 0, sizeof(*backend));
ALCplaybackAlsa_Construct(backend, device);
return STATIC_CAST(ALCbackend, backend);
}
if(type == ALCbackend_Capture)
{
ALCcaptureAlsa *backend;
backend = ALCcaptureAlsa_New(sizeof(*backend));
NEW_OBJ(backend, ALCcaptureAlsa)(device);
if(!backend) return NULL;
memset(backend, 0, sizeof(*backend));
ALCcaptureAlsa_Construct(backend, device);
return STATIC_CAST(ALCbackend, backend);
}
+25 -176
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,174 +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 ALint64 PlaybackWrapper_getLatency(PlaybackWrapper *self);
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, lock)
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(PlaybackWrapper)
DEFINE_ALCBACKEND_VTABLE(PlaybackWrapper);
static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device, const BackendFuncs *funcs)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(PlaybackWrapper, ALCbackend, self);
self->Funcs = funcs;
}
static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
return self->Funcs->OpenPlayback(device, name);
}
static void PlaybackWrapper_close(PlaybackWrapper *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
self->Funcs->ClosePlayback(device);
}
static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
return self->Funcs->ResetPlayback(device);
}
static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
return self->Funcs->StartPlayback(device);
}
static void PlaybackWrapper_stop(PlaybackWrapper *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
self->Funcs->StopPlayback(device);
}
static ALint64 PlaybackWrapper_getLatency(PlaybackWrapper *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
return self->Funcs->GetLatency(device);
}
typedef struct CaptureWrapper {
DERIVE_FROM_TYPE(ALCbackend);
const BackendFuncs *Funcs;
} CaptureWrapper;
static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device, const BackendFuncs *funcs);
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, Destruct)
static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name);
static void CaptureWrapper_close(CaptureWrapper *self);
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ALCboolean, reset)
static ALCboolean CaptureWrapper_start(CaptureWrapper *self);
static void CaptureWrapper_stop(CaptureWrapper *self);
static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples);
static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self);
static ALint64 CaptureWrapper_getLatency(CaptureWrapper *self);
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, lock)
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(CaptureWrapper)
DEFINE_ALCBACKEND_VTABLE(CaptureWrapper);
static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device, const BackendFuncs *funcs)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(CaptureWrapper, ALCbackend, self);
self->Funcs = funcs;
}
static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
return self->Funcs->OpenCapture(device, name);
}
static void CaptureWrapper_close(CaptureWrapper *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
self->Funcs->CloseCapture(device);
}
static ALCboolean CaptureWrapper_start(CaptureWrapper *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
self->Funcs->StartCapture(device);
return ALC_TRUE;
}
static void CaptureWrapper_stop(CaptureWrapper *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
self->Funcs->StopCapture(device);
}
static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
return self->Funcs->CaptureSamples(device, buffer, samples);
}
static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
return self->Funcs->AvailableSamples(device);
}
static ALint64 CaptureWrapper_getLatency(CaptureWrapper *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
return self->Funcs->GetLatency(device);
}
ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
PlaybackWrapper *backend;
backend = PlaybackWrapper_New(sizeof(*backend));
if(!backend) return NULL;
PlaybackWrapper_Construct(backend, device, funcs);
return STATIC_CAST(ALCbackend, backend);
}
if(type == ALCbackend_Capture)
{
CaptureWrapper *backend;
backend = CaptureWrapper_New(sizeof(*backend));
if(!backend) return NULL;
CaptureWrapper_Construct(backend, device, funcs);
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
+28 -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,12 +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 */
+316 -195
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -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)
{
ComponentDescription desc;
Component comp;
ca_data *data;
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
AudioComponentDescription desc;
AudioComponent comp;
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 */
@@ -154,64 +162,54 @@ static ALCenum ca_open_playback(ALCdevice *device, const ALCchar *deviceName)
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
comp = FindNextComponent(NULL, &desc);
comp = AudioComponentFindNext(NULL, &desc);
if(comp == NULL)
{
ERR("FindNextComponent failed\n");
ERR("AudioComponentFindNext failed\n");
return ALC_INVALID_VALUE;
}
data = calloc(1, sizeof(*data));
err = OpenAComponent(comp, &data->audioUnit);
err = AudioComponentInstanceNew(comp, &self->audioUnit);
if(err != noErr)
{
ERR("OpenAComponent failed\n");
free(data);
ERR("AudioComponentInstanceNew failed\n");
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");
CloseComponent(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);
CloseComponent(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,31 +353,125 @@ 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
AURenderCallbackStruct input;
ComponentDescription desc;
AudioComponentDescription desc;
AudioDeviceID inputDevice;
UInt32 outputFrameCount;
UInt32 propertySize;
AudioObjectPropertyAddress propertyAddress;
UInt32 enableIO;
Component comp;
ca_data *data;
AudioComponent comp;
OSStatus err;
if(!name)
name = ca_device;
else if(strcmp(name, ca_device) != 0)
return ALC_INVALID_VALUE;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_HALOutput;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
@@ -390,27 +479,24 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
desc.componentFlagsMask = 0;
// Search for component with given description
comp = FindNextComponent(NULL, &desc);
comp = AudioComponentFindNext(NULL, &desc);
if(comp == NULL)
{
ERR("FindNextComponent failed\n");
ERR("AudioComponentFindNext failed\n");
return ALC_INVALID_VALUE;
}
data = calloc(1, sizeof(*data));
device->ExtraData = data;
// Open the component
err = OpenAComponent(comp, &data->audioUnit);
err = AudioComponentInstanceNew(comp, &self->audioUnit);
if(err != noErr)
{
ERR("OpenAComponent failed\n");
ERR("AudioComponentInstanceNew failed\n");
goto error;
}
// 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");
@@ -419,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");
@@ -427,11 +513,16 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
}
// Get the default input device
propertySize = sizeof(AudioDeviceID);
err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &propertySize, &inputDevice);
propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propertySize, &inputDevice);
if(err != noErr)
{
ERR("AudioHardwareGetProperty failed\n");
ERR("AudioObjectGetPropertyData failed\n");
goto error;
}
@@ -442,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");
@@ -450,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");
@@ -461,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");
@@ -470,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");
@@ -514,9 +605,10 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
case DevFmtQuad:
case DevFmtX51:
case DevFmtX51Side:
case DevFmtX51Rear:
case DevFmtX61:
case DevFmtX71:
case DevFmtAmbi3D:
ERR("%s not supported\n", DevFmtChannelsString(device->FmtChans));
goto error;
}
@@ -529,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
@@ -538,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");
@@ -550,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);
@@ -559,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);
@@ -567,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)
CloseComponent(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);
CloseComponent(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;
@@ -645,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);
@@ -661,39 +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,
ALCdevice_GetLatencyDefault
};
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)
{
@@ -705,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;
}
+150 -144
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -43,6 +43,9 @@
#ifndef DSSPEAKER_5POINT1
# define DSSPEAKER_5POINT1 0x00000006
#endif
#ifndef DSSPEAKER_5POINT1_BACK
# define DSSPEAKER_5POINT1_BACK 0x00000006
#endif
#ifndef DSSPEAKER_7POINT1
# define DSSPEAKER_7POINT1 0x00000007
#endif
@@ -57,6 +60,8 @@
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
#define DEVNAME_HEAD "OpenAL Soft on "
#ifdef HAVE_DYNLOAD
static void *ds_handle;
@@ -118,15 +123,14 @@ 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, 0);
#undef DEINIT_STR
VECTOR_RESIZE(*list, 0);
}
static BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR* UNUSED(drvname), void *data)
{
vector_DevMap *devices = data;
OLECHAR *guidstr = NULL;
DevMap *iter, *end;
DevMap entry;
HRESULT hr;
int count;
@@ -137,30 +141,31 @@ static BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHA
AL_STRING_INIT(entry.name);
count = 0;
do {
al_string_copy_wcstr(&entry.name, desc);
while(1)
{
const DevMap *iter;
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);
}
count++;
iter = VECTOR_ITER_BEGIN(*devices);
end = VECTOR_ITER_END(*devices);
for(;iter != end;++iter)
{
if(al_string_cmp(entry.name, iter->name) == 0)
break;
}
} while(iter != end);
#define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0)
VECTOR_FIND_IF(iter, const DevMap, *devices, MATCH_ENTRY);
if(iter == VECTOR_END(*devices)) break;
#undef MATCH_ENTRY
count++;
}
entry.guid = *guid;
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);
}
@@ -194,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)
@@ -239,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);
@@ -294,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);
@@ -336,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;
@@ -374,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;
}
@@ -441,28 +448,35 @@ static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self)
hr = IDirectSound_GetSpeakerConfig(self->DS, &speakers);
if(SUCCEEDED(hr))
{
speakers = DSSPEAKER_CONFIG(speakers);
if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
{
speakers = DSSPEAKER_CONFIG(speakers);
if(speakers == DSSPEAKER_MONO)
device->FmtChans = DevFmtMono;
else if(speakers == DSSPEAKER_STEREO || speakers == DSSPEAKER_HEADPHONE)
device->FmtChans = DevFmtStereo;
else if(speakers == DSSPEAKER_QUAD)
device->FmtChans = DevFmtQuad;
else if(speakers == DSSPEAKER_5POINT1 || speakers == DSSPEAKER_5POINT1_SURROUND)
else if(speakers == DSSPEAKER_5POINT1_SURROUND)
device->FmtChans = DevFmtX51;
else if(speakers == DSSPEAKER_5POINT1_BACK)
device->FmtChans = DevFmtX51Rear;
else if(speakers == DSSPEAKER_7POINT1 || speakers == DSSPEAKER_7POINT1_SURROUND)
device->FmtChans = DevFmtX71;
else
ERR("Unknown system speaker config: 0x%lx\n", speakers);
}
device->IsHeadphones = (device->FmtChans == DevFmtStereo &&
speakers == DSSPEAKER_HEADPHONE);
switch(device->FmtChans)
{
case DevFmtMono:
OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;
break;
case DevFmtAmbi3D:
device->FmtChans = DevFmtStereo;
/*fall-through*/
case DevFmtStereo:
OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
SPEAKER_FRONT_RIGHT;
@@ -478,16 +492,16 @@ static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self)
SPEAKER_FRONT_RIGHT |
SPEAKER_FRONT_CENTER |
SPEAKER_LOW_FREQUENCY |
SPEAKER_BACK_LEFT |
SPEAKER_BACK_RIGHT;
SPEAKER_SIDE_LEFT |
SPEAKER_SIDE_RIGHT;
break;
case DevFmtX51Side:
case DevFmtX51Rear:
OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
SPEAKER_FRONT_RIGHT |
SPEAKER_FRONT_CENTER |
SPEAKER_LOW_FREQUENCY |
SPEAKER_SIDE_LEFT |
SPEAKER_SIDE_RIGHT;
SPEAKER_BACK_LEFT |
SPEAKER_BACK_RIGHT;
break;
case DevFmtX61:
OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
@@ -513,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;
@@ -641,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);
@@ -653,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)
@@ -689,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;
}
@@ -719,97 +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_BACK_LEFT |
SPEAKER_BACK_RIGHT;
break;
case DevFmtX51Side:
InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
SPEAKER_FRONT_RIGHT |
SPEAKER_FRONT_CENTER |
SPEAKER_LOW_FREQUENCY |
SPEAKER_SIDE_LEFT |
SPEAKER_SIDE_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;
}
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;
}
@@ -818,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);
@@ -833,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)
@@ -883,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;
}
@@ -899,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;
@@ -915,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);
@@ -931,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);
@@ -1027,26 +1043,16 @@ static ALCbackend* ALCdsoundBackendFactory_createBackend(ALCdsoundBackendFactory
if(type == ALCbackend_Playback)
{
ALCdsoundPlayback *backend;
backend = ALCdsoundPlayback_New(sizeof(*backend));
NEW_OBJ(backend, ALCdsoundPlayback)(device);
if(!backend) return NULL;
memset(backend, 0, sizeof(*backend));
ALCdsoundPlayback_Construct(backend, device);
return STATIC_CAST(ALCbackend, backend);
}
if(type == ALCbackend_Capture)
{
ALCdsoundCapture *backend;
backend = ALCdsoundCapture_New(sizeof(*backend));
NEW_OBJ(backend, ALCdsoundCapture)(device);
if(!backend) return NULL;
memset(backend, 0, sizeof(*backend));
ALCdsoundCapture_Construct(backend, device);
return STATIC_CAST(ALCbackend, backend);
}
+632
View File
@@ -0,0 +1,632 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include "alMain.h"
#include "alu.h"
#include "threads.h"
#include "compat.h"
#include "backends/base.h"
#include <jack/jack.h>
#include <jack/ringbuffer.h>
static const ALCchar jackDevice[] = "JACK Default";
#ifdef HAVE_DYNLOAD
#define JACK_FUNCS(MAGIC) \
MAGIC(jack_client_open); \
MAGIC(jack_client_close); \
MAGIC(jack_client_name_size); \
MAGIC(jack_get_client_name); \
MAGIC(jack_connect); \
MAGIC(jack_activate); \
MAGIC(jack_deactivate); \
MAGIC(jack_port_register); \
MAGIC(jack_port_unregister); \
MAGIC(jack_port_get_buffer); \
MAGIC(jack_port_name); \
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); \
MAGIC(jack_get_buffer_size);
static void *jack_handle;
#define MAKE_FUNC(f) static __typeof(f) * p##f
JACK_FUNCS(MAKE_FUNC);
#undef MAKE_FUNC
#define jack_client_open pjack_client_open
#define jack_client_close pjack_client_close
#define jack_client_name_size pjack_client_name_size
#define jack_get_client_name pjack_get_client_name
#define jack_connect pjack_connect
#define jack_activate pjack_activate
#define jack_deactivate pjack_deactivate
#define jack_port_register pjack_port_register
#define jack_port_unregister pjack_port_unregister
#define jack_port_get_buffer pjack_port_get_buffer
#define jack_port_name pjack_port_name
#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
#endif
static jack_options_t ClientOptions = JackNullOption;
static ALCboolean jack_load(void)
{
ALCboolean error = ALC_FALSE;
#ifdef HAVE_DYNLOAD
if(!jack_handle)
{
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
if(error)
{
WARN("Missing expected functions:%s\n", alstr_get_cstr(missing_funcs));
CloseLib(jack_handle);
jack_handle = NULL;
}
alstr_reset(&missing_funcs);
}
#endif
return !error;
}
typedef struct ALCjackPlayback {
DERIVE_FROM_TYPE(ALCbackend);
jack_client_t *Client;
jack_port_t *Port[MAX_OUTPUT_CHANNELS];
ll_ringbuffer_t *Ring;
alcnd_t Cond;
volatile int killNow;
althrd_t thread;
} ALCjackPlayback;
static int ALCjackPlayback_bufferSizeNotify(jack_nframes_t numframes, void *arg);
static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg);
static int ALCjackPlayback_mixerProc(void *arg);
static void ALCjackPlayback_Construct(ALCjackPlayback *self, ALCdevice *device);
static void ALCjackPlayback_Destruct(ALCjackPlayback *self);
static ALCenum ALCjackPlayback_open(ALCjackPlayback *self, const ALCchar *name);
static void ALCjackPlayback_close(ALCjackPlayback *self);
static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self);
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 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);
static void ALCjackPlayback_Construct(ALCjackPlayback *self, ALCdevice *device)
{
ALuint i;
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCjackPlayback, ALCbackend, self);
alcnd_init(&self->Cond);
self->Client = NULL;
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
self->Port[i] = NULL;
self->Ring = NULL;
self->killNow = 1;
}
static void ALCjackPlayback_Destruct(ALCjackPlayback *self)
{
ALuint i;
if(self->Client)
{
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
{
if(self->Port[i])
jack_port_unregister(self->Client, self->Port[i]);
self->Port[i] = NULL;
}
jack_client_close(self->Client);
self->Client = NULL;
}
alcnd_destroy(&self->Cond);
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
static int ALCjackPlayback_bufferSizeNotify(jack_nframes_t numframes, void *arg)
{
ALCjackPlayback *self = arg;
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
ALuint bufsize;
ALCjackPlayback_lock(self);
device->UpdateSize = numframes;
device->NumUpdates = 2;
bufsize = device->UpdateSize;
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, device->AmbiOrder)
);
if(!self->Ring)
{
ERR("Failed to reallocate ringbuffer\n");
aluHandleDisconnect(device);
}
ALCjackPlayback_unlock(self);
return 0;
}
static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg)
{
ALCjackPlayback *self = arg;
jack_default_audio_sample_t *out[MAX_OUTPUT_CHANNELS];
ll_ringbuffer_data_t data[2];
jack_nframes_t total = 0;
jack_nframes_t todo;
ALsizei i, c, numchans;
ll_ringbuffer_get_read_vector(self->Ring, data);
for(c = 0;c < MAX_OUTPUT_CHANNELS && self->Port[c];c++)
out[c] = jack_port_get_buffer(self->Port[c], numframes);
numchans = c;
todo = minu(numframes, data[0].len);
for(c = 0;c < 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;
todo = minu(numframes-total, data[1].len);
if(todo > 0)
{
for(c = 0;c < 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;
}
ll_ringbuffer_read_advance(self->Ring, total);
alcnd_signal(&self->Cond);
if(numframes > total)
{
todo = numframes-total;
for(c = 0;c < numchans;c++)
{
for(i = 0;(jack_nframes_t)i < todo;i++)
out[c][i] = 0.0f;
}
}
return 0;
}
static int ALCjackPlayback_mixerProc(void *arg)
{
ALCjackPlayback *self = arg;
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
ll_ringbuffer_data_t data[2];
SetRTPriority();
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
ALCjackPlayback_lock(self);
while(!self->killNow && device->Connected)
{
ALuint todo, len1, len2;
/* NOTE: Unfortunately, there is an unavoidable race condition here.
* It's possible for the process() method to run, updating the read
* pointer and signaling the condition variable, in between the mixer
* loop checking the write size and waiting for the condition variable.
* This will cause the mixer loop to wait until the *next* process()
* invocation, most likely writing silence for it.
*
* However, this should only happen if the mixer is running behind
* anyway (as ideally we'll be asleep in alcnd_wait by the time the
* process() method is invoked), so this behavior is not unwarranted.
* It's unfortunate since it'll be wasting time sleeping that could be
* used to catch up, but there's no way around it without blocking in
* the process() method.
*/
if(ll_ringbuffer_write_space(self->Ring) < device->UpdateSize)
{
alcnd_wait(&self->Cond, &STATIC_CAST(ALCbackend,self)->mMutex);
continue;
}
ll_ringbuffer_get_write_vector(self->Ring, data);
todo = data[0].len + data[1].len;
todo -= todo%device->UpdateSize;
len1 = minu(data[0].len, todo);
len2 = minu(data[1].len, todo-len1);
aluMixData(device, data[0].buf, len1);
if(len2 > 0)
aluMixData(device, data[1].buf, len2);
ll_ringbuffer_write_advance(self->Ring, todo);
}
ALCjackPlayback_unlock(self);
return 0;
}
static ALCenum ALCjackPlayback_open(ALCjackPlayback *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
const char *client_name = "alsoft";
jack_status_t status;
if(!name)
name = jackDevice;
else if(strcmp(name, jackDevice) != 0)
return ALC_INVALID_VALUE;
self->Client = jack_client_open(client_name, ClientOptions, &status, NULL);
if(self->Client == NULL)
{
ERR("jack_client_open() failed, status = 0x%02x\n", status);
return ALC_INVALID_VALUE;
}
if((status&JackServerStarted))
TRACE("JACK server started\n");
if((status&JackNameNotUnique))
{
client_name = jack_get_client_name(self->Client);
TRACE("Client name not unique, got `%s' instead\n", client_name);
}
jack_set_process_callback(self->Client, ALCjackPlayback_process, self);
jack_set_buffer_size_callback(self->Client, ALCjackPlayback_bufferSizeNotify, self);
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
static void ALCjackPlayback_close(ALCjackPlayback *self)
{
ALuint i;
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
{
if(self->Port[i])
jack_port_unregister(self->Client, self->Port[i]);
self->Port[i] = NULL;
}
jack_client_close(self->Client);
self->Client = NULL;
}
static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
ALsizei numchans, i;
ALuint bufsize;
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
{
if(self->Port[i])
jack_port_unregister(self->Client, self->Port[i]);
self->Port[i] = NULL;
}
/* Ignore the requested buffer metrics and just keep one JACK-sized buffer
* ready for when requested. Note that one period's worth of audio in the
* ring buffer will always be left unfilled because one element of the ring
* buffer will not be writeable, and we only write in period-sized chunks.
*/
device->Frequency = jack_get_sample_rate(self->Client);
device->UpdateSize = jack_get_buffer_size(self->Client);
device->NumUpdates = 2;
bufsize = device->UpdateSize;
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, device->AmbiOrder);
for(i = 0;i < numchans;i++)
{
char name[64];
snprintf(name, sizeof(name), "channel_%d", i+1);
self->Port[i] = jack_port_register(self->Client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
if(self->Port[i] == NULL)
{
ERR("Not enough JACK ports available for %s output\n", DevFmtChannelsString(device->FmtChans));
if(i == 0) return ALC_FALSE;
break;
}
}
if(i < numchans)
{
if(i == 1)
device->FmtChans = DevFmtMono;
else
{
for(--i;i >= 2;i--)
{
jack_port_unregister(self->Client, self->Port[i]);
self->Port[i] = NULL;
}
device->FmtChans = DevFmtStereo;
}
}
ll_ringbuffer_free(self->Ring);
self->Ring = ll_ringbuffer_create(bufsize,
FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder)
);
if(!self->Ring)
{
ERR("Failed to allocate ringbuffer\n");
return ALC_FALSE;
}
SetDefaultChannelOrder(device);
return ALC_TRUE;
}
static ALCboolean ALCjackPlayback_start(ALCjackPlayback *self)
{
const char **ports;
ALsizei i;
if(jack_activate(self->Client))
{
ERR("Failed to activate client\n");
return ALC_FALSE;
}
ports = jack_get_ports(self->Client, NULL, NULL, JackPortIsPhysical|JackPortIsInput);
if(ports == NULL)
{
ERR("No physical playback ports found\n");
jack_deactivate(self->Client);
return ALC_FALSE;
}
for(i = 0;i < MAX_OUTPUT_CHANNELS && self->Port[i];i++)
{
if(!ports[i])
{
ERR("No physical playback port for \"%s\"\n", jack_port_name(self->Port[i]));
break;
}
if(jack_connect(self->Client, jack_port_name(self->Port[i]), ports[i]))
ERR("Failed to connect output port \"%s\" to \"%s\"\n", jack_port_name(self->Port[i]), ports[i]);
}
jack_free(ports);
self->killNow = 0;
if(althrd_create(&self->thread, ALCjackPlayback_mixerProc, self) != althrd_success)
{
jack_deactivate(self->Client);
return ALC_FALSE;
}
return ALC_TRUE;
}
static void ALCjackPlayback_stop(ALCjackPlayback *self)
{
int res;
if(self->killNow)
return;
self->killNow = 1;
/* Lock the backend to ensure we don't flag the mixer to die and signal the
* mixer to wake up in between it checking the flag and going to sleep and
* wait for a wakeup (potentially leading to it never waking back up to see
* the flag). */
ALCjackPlayback_lock(self);
ALCjackPlayback_unlock(self);
alcnd_signal(&self->Cond);
althrd_join(self->thread, &res);
jack_deactivate(self->Client);
}
static ClockLatency ALCjackPlayback_getClockLatency(ALCjackPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
ClockLatency ret;
ALCjackPlayback_lock(self);
ret.ClockTime = GetDeviceClockTime(device);
ret.Latency = ll_ringbuffer_read_space(self->Ring) * DEVICE_CLOCK_RES /
device->Frequency;
ALCjackPlayback_unlock(self);
return ret;
}
static void jack_msg_handler(const char *message)
{
WARN("%s\n", message);
}
typedef struct ALCjackBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCjackBackendFactory;
#define ALCJACKBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCjackBackendFactory, ALCbackendFactory) } }
static ALCboolean ALCjackBackendFactory_init(ALCjackBackendFactory* UNUSED(self))
{
jack_client_t *client;
jack_status_t status;
if(!jack_load())
return ALC_FALSE;
if(!GetConfigValueBool(NULL, "jack", "spawn-server", 0))
ClientOptions |= JackNoStartServer;
jack_set_error_function(jack_msg_handler);
client = jack_client_open("alsoft", ClientOptions, &status, NULL);
jack_set_error_function(NULL);
if(client == NULL)
{
WARN("jack_client_open() failed, 0x%02x\n", status);
if((status&JackServerFailed) && !(ClientOptions&JackNoStartServer))
ERR("Unable to connect to JACK server\n");
return ALC_FALSE;
}
jack_client_close(client);
return ALC_TRUE;
}
static void ALCjackBackendFactory_deinit(ALCjackBackendFactory* UNUSED(self))
{
#ifdef HAVE_DYNLOAD
if(jack_handle)
CloseLib(jack_handle);
jack_handle = NULL;
#endif
}
static ALCboolean ALCjackBackendFactory_querySupport(ALCjackBackendFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
return ALC_TRUE;
return ALC_FALSE;
}
static void ALCjackBackendFactory_probe(ALCjackBackendFactory* UNUSED(self), enum DevProbe type)
{
switch(type)
{
case ALL_DEVICE_PROBE:
AppendAllDevicesList(jackDevice);
break;
case CAPTURE_DEVICE_PROBE:
break;
}
}
static ALCbackend* ALCjackBackendFactory_createBackend(ALCjackBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCjackPlayback *backend;
NEW_OBJ(backend, ALCjackPlayback)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCjackBackendFactory);
ALCbackendFactory *ALCjackBackendFactory_getFactory(void)
{
static ALCjackBackendFactory factory = ALCJACKBACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
+5 -10
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -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;
}
@@ -124,13 +124,8 @@ static ALCbackend* ALCloopbackFactory_createBackend(ALCloopbackFactory* UNUSED(s
if(type == ALCbackend_Loopback)
{
ALCloopback *backend;
backend = ALCloopback_New(sizeof(*backend));
NEW_OBJ(backend, ALCloopback)(device);
if(!backend) return NULL;
memset(backend, 0, sizeof(*backend));
ALCloopback_Construct(backend, device);
return STATIC_CAST(ALCbackend, backend);
}
+968 -91
View File
File diff suppressed because it is too large Load Diff
+8 -11
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -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)
@@ -106,10 +106,12 @@ static int ALCnullBackend_mixerProc(void *ptr)
}
if(avail-done < device->UpdateSize)
al_nssleep(0, restTime);
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;
}
@@ -214,13 +216,8 @@ static ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory* UN
if(type == ALCbackend_Playback)
{
ALCnullBackend *backend;
backend = ALCnullBackend_New(sizeof(*backend));
NEW_OBJ(backend, ALCnullBackend)(device);
if(!backend) return NULL;
memset(backend, 0, sizeof(*backend));
ALCnullBackend_Construct(backend, device);
return STATIC_CAST(ALCbackend, backend);
}
+890 -179
View File
File diff suppressed because it is too large Load Diff
+375 -139
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -22,10 +22,12 @@
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <memory.h>
#include <unistd.h>
#include <errno.h>
@@ -51,11 +53,176 @@
#define SOUND_MIXER_WRITE MIXER_WRITE
#endif
#if defined(SOUND_VERSION) && (SOUND_VERSION < 0x040000)
#define ALC_OSS_COMPAT
#endif
#ifndef SNDCTL_AUDIOINFO
#define ALC_OSS_COMPAT
#endif
static const ALCchar oss_device[] = "OSS Default";
/*
* FreeBSD strongly discourages the use of specific devices,
* such as those returned in oss_audioinfo.devnode
*/
#ifdef __FreeBSD__
#define ALC_OSS_DEVNODE_TRUC
#endif
static const char *oss_driver = "/dev/dsp";
static const char *oss_capture = "/dev/dsp";
struct oss_device {
const ALCchar *handle;
const char *path;
struct oss_device *next;
};
static struct oss_device oss_playback = {
"OSS Default",
"/dev/dsp",
NULL
};
static struct oss_device oss_capture = {
"OSS Default",
"/dev/dsp",
NULL
};
#ifdef ALC_OSS_COMPAT
#define DSP_CAP_OUTPUT 0x00020000
#define DSP_CAP_INPUT 0x00010000
static void ALCossListPopulate(struct oss_device *UNUSED(devlist), int UNUSED(type_flag))
{
}
#else
#ifndef HAVE_STRNLEN
static size_t strnlen(const char *str, size_t maxlen)
{
const char *end = memchr(str, 0, maxlen);
if(!end) return maxlen;
return end - str;
}
#endif
static void ALCossListAppend(struct oss_device *list, const char *handle, size_t hlen, const char *path, size_t plen)
{
struct oss_device *next;
struct oss_device *last;
size_t i;
/* skip the first item "OSS Default" */
last = list;
next = list->next;
#ifdef ALC_OSS_DEVNODE_TRUC
for(i = 0;i < plen;i++)
{
if(path[i] == '.')
{
if(strncmp(path + i, handle + hlen + i - plen, plen - i) == 0)
hlen = hlen + i - plen;
plen = i;
}
}
#else
(void)i;
#endif
if(handle[0] == '\0')
{
handle = path;
hlen = plen;
}
while(next != NULL)
{
if(strncmp(next->path, path, plen) == 0)
return;
last = next;
next = next->next;
}
next = (struct oss_device*)malloc(sizeof(struct oss_device) + hlen + plen + 2);
next->handle = (char*)(next + 1);
next->path = next->handle + hlen + 1;
next->next = NULL;
last->next = next;
strncpy((char*)next->handle, handle, hlen);
((char*)next->handle)[hlen] = '\0';
strncpy((char*)next->path, path, plen);
((char*)next->path)[plen] = '\0';
TRACE("Got device \"%s\", \"%s\"\n", next->handle, next->path);
}
static void ALCossListPopulate(struct oss_device *devlist, int type_flag)
{
struct oss_sysinfo si;
struct oss_audioinfo ai;
int fd, i;
if((fd=open("/dev/mixer", O_RDONLY)) < 0)
{
TRACE("Could not open /dev/mixer: %s\n", strerror(errno));
return;
}
if(ioctl(fd, SNDCTL_SYSINFO, &si) == -1)
{
TRACE("SNDCTL_SYSINFO failed: %s\n", strerror(errno));
goto done;
}
for(i = 0;i < si.numaudios;i++)
{
const char *handle;
size_t len;
ai.dev = i;
if(ioctl(fd, SNDCTL_AUDIOINFO, &ai) == -1)
{
ERR("SNDCTL_AUDIOINFO (%d) failed: %s\n", i, strerror(errno));
continue;
}
if(ai.devnode[0] == '\0')
continue;
if(ai.handle[0] != '\0')
{
len = strnlen(ai.handle, sizeof(ai.handle));
handle = ai.handle;
}
else
{
len = strnlen(ai.name, sizeof(ai.name));
handle = ai.name;
}
if((ai.caps&type_flag))
ALCossListAppend(devlist, handle, len, ai.devnode,
strnlen(ai.devnode, sizeof(ai.devnode)));
}
done:
close(fd);
}
#endif
static void ALCossListFree(struct oss_device *list)
{
struct oss_device *cur;
if(list == NULL)
return;
/* skip the first item "OSS Default" */
cur = list->next;
list->next = NULL;
while(cur != NULL)
{
struct oss_device *next = cur->next;
free(cur);
cur = next;
}
}
static int log2i(ALCuint x)
{
@@ -68,7 +235,6 @@ static int log2i(ALCuint x)
return y;
}
typedef struct ALCplaybackOSS {
DERIVE_FROM_TYPE(ALCbackend);
@@ -77,7 +243,7 @@ typedef struct ALCplaybackOSS {
ALubyte *mix_data;
int data_size;
volatile int killNow;
ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCplaybackOSS;
@@ -92,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)
@@ -103,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(0, 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;
}
@@ -148,27 +336,45 @@ 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)
{
struct oss_device *dev = &oss_playback;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
if(!name)
name = oss_device;
else if(strcmp(name, oss_device) != 0)
return ALC_INVALID_VALUE;
if(!name || strcmp(name, dev->handle) == 0)
name = dev->handle;
else
{
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)
{
WARN("Could not find \"%s\" in device list\n", name);
return ALC_INVALID_VALUE;
}
}
self->killNow = 0;
self->fd = open(oss_driver, O_WRONLY);
self->fd = open(dev->path, O_WRONLY);
if(self->fd == -1)
{
ERR("Could not open %s: %s\n", oss_driver, strerror(errno));
ERR("Could not open %s: %s\n", dev->path, strerror(errno));
return ALC_INVALID_VALUE;
}
al_string_copy_cstr(&device->DeviceName, name);
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
@@ -212,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) { \
@@ -245,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;
@@ -261,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);
@@ -272,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);
@@ -290,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)
@@ -309,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;
@@ -330,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)
@@ -341,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(), "alsoft-record");
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(0, 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;
@@ -377,11 +595,14 @@ 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)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
struct oss_device *dev = &oss_capture;
int numFragmentsLogSize;
int log2FragmentSize;
unsigned int periods;
@@ -392,15 +613,32 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
int ossSpeed;
char *err;
if(!name)
name = oss_device;
else if(strcmp(name, oss_device) != 0)
return ALC_INVALID_VALUE;
if(!name || strcmp(name, dev->handle) == 0)
name = dev->handle;
else
{
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)
{
WARN("Could not find \"%s\" in device list\n", name);
return ALC_INVALID_VALUE;
}
}
self->fd = open(oss_capture, O_RDONLY);
self->fd = open(dev->path, O_RDONLY);
if(self->fd == -1)
{
ERR("Could not open %s: %s\n", oss_capture, strerror(errno));
ERR("Could not open %s: %s\n", dev->path, strerror(errno));
return ALC_INVALID_VALUE;
}
@@ -424,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 *
@@ -454,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);
@@ -472,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");
@@ -481,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);
}
@@ -546,7 +774,7 @@ typedef struct ALCossBackendFactory {
ALCbackendFactory *ALCossBackendFactory_getFactory(void);
static ALCboolean ALCossBackendFactory_init(ALCossBackendFactory *self);
static DECLARE_FORWARD(ALCossBackendFactory, ALCbackendFactory, void, deinit)
static void ALCossBackendFactory_deinit(ALCossBackendFactory *self);
static ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory *self, ALCbackend_Type type);
static void ALCossBackendFactory_probe(ALCossBackendFactory *self, enum DevProbe type);
static ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
@@ -562,12 +790,19 @@ ALCbackendFactory *ALCossBackendFactory_getFactory(void)
ALCboolean ALCossBackendFactory_init(ALCossBackendFactory* UNUSED(self))
{
ConfigValueStr("oss", "device", &oss_driver);
ConfigValueStr("oss", "capture", &oss_capture);
ConfigValueStr(NULL, "oss", "device", &oss_playback.path);
ConfigValueStr(NULL, "oss", "capture", &oss_capture.path);
return ALC_TRUE;
}
void ALCossBackendFactory_deinit(ALCossBackendFactory* UNUSED(self))
{
ALCossListFree(&oss_playback);
ALCossListFree(&oss_capture);
}
ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Playback || type == ALCbackend_Capture)
@@ -577,27 +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:
{
ALCossListFree(&oss_playback);
ALCossListPopulate(&oss_playback, DSP_CAP_OUTPUT);
cur = &oss_playback;
while(cur != NULL)
{
#ifdef HAVE_STAT
struct stat buf;
if(stat(oss_driver, &buf) == 0)
struct stat buf;
if(stat(cur->path, &buf) == 0)
#endif
AppendAllDevicesList(oss_device);
}
break;
AppendAllDevicesList(cur->handle);
cur = cur->next;
}
break;
case CAPTURE_DEVICE_PROBE:
{
ALCossListFree(&oss_capture);
ALCossListPopulate(&oss_capture, DSP_CAP_INPUT);
cur = &oss_capture;
while(cur != NULL)
{
#ifdef HAVE_STAT
struct stat buf;
if(stat(oss_capture, &buf) == 0)
struct stat buf;
if(stat(cur->path, &buf) == 0)
#endif
AppendCaptureDeviceList(oss_device);
}
break;
AppendCaptureDeviceList(cur->handle);
cur = cur->next;
}
break;
}
}
@@ -606,25 +852,15 @@ ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory* UNUSED(self
if(type == ALCbackend_Playback)
{
ALCplaybackOSS *backend;
backend = ALCplaybackOSS_New(sizeof(*backend));
NEW_OBJ(backend, ALCplaybackOSS)(device);
if(!backend) return NULL;
memset(backend, 0, sizeof(*backend));
ALCplaybackOSS_Construct(backend, device);
return STATIC_CAST(ALCbackend, backend);
}
if(type == ALCbackend_Capture)
{
ALCcaptureOSS *backend;
backend = ALCcaptureOSS_New(sizeof(*backend));
NEW_OBJ(backend, ALCcaptureOSS)(device);
if(!backend) return NULL;
memset(backend, 0, sizeof(*backend));
ALCcaptureOSS_Construct(backend, device);
return STATIC_CAST(ALCbackend, backend);
}
+261 -155
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -28,6 +28,8 @@
#include "alu.h"
#include "compat.h"
#include "backends/base.h"
#include <portaudio.h>
@@ -122,149 +124,173 @@ static ALCboolean pa_load(void)
}
typedef struct {
typedef struct ALCportPlayback {
DERIVE_FROM_TYPE(ALCbackend);
PaStream *stream;
PaStreamParameters params;
ALuint update_size;
} ALCportPlayback;
RingBuffer *ring;
} pa_data;
static int ALCportPlayback_WriteCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
const PaStreamCallbackFlags statusFlags, void *userData);
static void ALCportPlayback_Construct(ALCportPlayback *self, ALCdevice *device);
static void ALCportPlayback_Destruct(ALCportPlayback *self);
static ALCenum ALCportPlayback_open(ALCportPlayback *self, const ALCchar *name);
static void ALCportPlayback_close(ALCportPlayback *self);
static ALCboolean ALCportPlayback_reset(ALCportPlayback *self);
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, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCportPlayback, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCportPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCportPlayback)
DEFINE_ALCBACKEND_VTABLE(ALCportPlayback);
static int pa_callback(const void *UNUSED(inputBuffer), void *outputBuffer,
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
static void ALCportPlayback_Construct(ALCportPlayback *self, ALCdevice *device)
{
ALCdevice *device = (ALCdevice*)userData;
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCportPlayback, ALCbackend, self);
aluMixData(device, outputBuffer, framesPerBuffer);
return 0;
self->stream = NULL;
}
static int pa_capture_cb(const void *inputBuffer, void *UNUSED(outputBuffer),
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
static void ALCportPlayback_Destruct(ALCportPlayback *self)
{
ALCdevice *device = (ALCdevice*)userData;
pa_data *data = (pa_data*)device->ExtraData;
if(self->stream)
Pa_CloseStream(self->stream);
self->stream = NULL;
WriteRingBuffer(data->ring, inputBuffer, framesPerBuffer);
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
static int ALCportPlayback_WriteCallback(const void *UNUSED(inputBuffer), void *outputBuffer,
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
{
ALCportPlayback *self = userData;
ALCportPlayback_lock(self);
aluMixData(STATIC_CAST(ALCbackend, self)->mDevice, outputBuffer, framesPerBuffer);
ALCportPlayback_unlock(self);
return 0;
}
static ALCenum pa_open_playback(ALCdevice *device, const ALCchar *deviceName)
static ALCenum ALCportPlayback_open(ALCportPlayback *self, const ALCchar *name)
{
pa_data *data;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
PaError err;
if(!deviceName)
deviceName = pa_device;
else if(strcmp(deviceName, pa_device) != 0)
if(!name)
name = pa_device;
else if(strcmp(name, pa_device) != 0)
return ALC_INVALID_VALUE;
data = (pa_data*)calloc(1, sizeof(pa_data));
data->update_size = device->UpdateSize;
self->update_size = device->UpdateSize;
data->params.device = -1;
if(!ConfigValueInt("port", "device", &data->params.device) ||
data->params.device < 0)
data->params.device = Pa_GetDefaultOutputDevice();
data->params.suggestedLatency = (device->UpdateSize*device->NumUpdates) /
self->params.device = -1;
if(!ConfigValueInt(NULL, "port", "device", &self->params.device) ||
self->params.device < 0)
self->params.device = Pa_GetDefaultOutputDevice();
self->params.suggestedLatency = (device->UpdateSize*device->NumUpdates) /
(float)device->Frequency;
data->params.hostApiSpecificStreamInfo = NULL;
self->params.hostApiSpecificStreamInfo = NULL;
data->params.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2);
self->params.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2);
switch(device->FmtType)
{
case DevFmtByte:
data->params.sampleFormat = paInt8;
self->params.sampleFormat = paInt8;
break;
case DevFmtUByte:
data->params.sampleFormat = paUInt8;
self->params.sampleFormat = paUInt8;
break;
case DevFmtUShort:
/* fall-through */
case DevFmtShort:
data->params.sampleFormat = paInt16;
self->params.sampleFormat = paInt16;
break;
case DevFmtUInt:
/* fall-through */
case DevFmtInt:
data->params.sampleFormat = paInt32;
self->params.sampleFormat = paInt32;
break;
case DevFmtFloat:
data->params.sampleFormat = paFloat32;
self->params.sampleFormat = paFloat32;
break;
}
retry_open:
err = Pa_OpenStream(&data->stream, NULL, &data->params, device->Frequency,
device->UpdateSize, paNoFlag, pa_callback, device);
err = Pa_OpenStream(&self->stream, NULL, &self->params,
device->Frequency, device->UpdateSize, paNoFlag,
ALCportPlayback_WriteCallback, self
);
if(err != paNoError)
{
if(data->params.sampleFormat == paFloat32)
if(self->params.sampleFormat == paFloat32)
{
data->params.sampleFormat = paInt16;
self->params.sampleFormat = paInt16;
goto retry_open;
}
ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
free(data);
return ALC_INVALID_VALUE;
}
device->ExtraData = data;
al_string_copy_cstr(&device->DeviceName, deviceName);
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
static void pa_close_playback(ALCdevice *device)
static void ALCportPlayback_close(ALCportPlayback *self)
{
pa_data *data = (pa_data*)device->ExtraData;
PaError err;
err = Pa_CloseStream(data->stream);
PaError err = Pa_CloseStream(self->stream);
if(err != paNoError)
ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
free(data);
device->ExtraData = NULL;
self->stream = NULL;
}
static ALCboolean pa_reset_playback(ALCdevice *device)
static ALCboolean ALCportPlayback_reset(ALCportPlayback *self)
{
pa_data *data = (pa_data*)device->ExtraData;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
const PaStreamInfo *streamInfo;
streamInfo = Pa_GetStreamInfo(data->stream);
streamInfo = Pa_GetStreamInfo(self->stream);
device->Frequency = streamInfo->sampleRate;
device->UpdateSize = data->update_size;
device->UpdateSize = self->update_size;
if(data->params.sampleFormat == paInt8)
if(self->params.sampleFormat == paInt8)
device->FmtType = DevFmtByte;
else if(data->params.sampleFormat == paUInt8)
else if(self->params.sampleFormat == paUInt8)
device->FmtType = DevFmtUByte;
else if(data->params.sampleFormat == paInt16)
else if(self->params.sampleFormat == paInt16)
device->FmtType = DevFmtShort;
else if(data->params.sampleFormat == paInt32)
else if(self->params.sampleFormat == paInt32)
device->FmtType = DevFmtInt;
else if(data->params.sampleFormat == paFloat32)
else if(self->params.sampleFormat == paFloat32)
device->FmtType = DevFmtFloat;
else
{
ERR("Unexpected sample format: 0x%lx\n", data->params.sampleFormat);
ERR("Unexpected sample format: 0x%lx\n", self->params.sampleFormat);
return ALC_FALSE;
}
if(data->params.channelCount == 2)
if(self->params.channelCount == 2)
device->FmtChans = DevFmtStereo;
else if(data->params.channelCount == 1)
else if(self->params.channelCount == 1)
device->FmtChans = DevFmtMono;
else
{
ERR("Unexpected channel count: %u\n", data->params.channelCount);
ERR("Unexpected channel count: %u\n", self->params.channelCount);
return ALC_FALSE;
}
SetDefaultChannelOrder(device);
@@ -272,12 +298,11 @@ static ALCboolean pa_reset_playback(ALCdevice *device)
return ALC_TRUE;
}
static ALCboolean pa_start_playback(ALCdevice *device)
static ALCboolean ALCportPlayback_start(ALCportPlayback *self)
{
pa_data *data = (pa_data*)device->ExtraData;
PaError err;
err = Pa_StartStream(data->stream);
err = Pa_StartStream(self->stream);
if(err != paNoError)
{
ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err));
@@ -287,161 +312,209 @@ static ALCboolean pa_start_playback(ALCdevice *device)
return ALC_TRUE;
}
static void pa_stop_playback(ALCdevice *device)
static void ALCportPlayback_stop(ALCportPlayback *self)
{
pa_data *data = (pa_data*)device->ExtraData;
PaError err;
err = Pa_StopStream(data->stream);
PaError err = Pa_StopStream(self->stream);
if(err != paNoError)
ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
}
static ALCenum pa_open_capture(ALCdevice *device, const ALCchar *deviceName)
typedef struct ALCportCapture {
DERIVE_FROM_TYPE(ALCbackend);
PaStream *stream;
PaStreamParameters params;
ll_ringbuffer_t *ring;
} ALCportCapture;
static int ALCportCapture_ReadCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
const PaStreamCallbackFlags statusFlags, void *userData);
static void ALCportCapture_Construct(ALCportCapture *self, ALCdevice *device);
static void ALCportCapture_Destruct(ALCportCapture *self);
static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name);
static void ALCportCapture_close(ALCportCapture *self);
static DECLARE_FORWARD(ALCportCapture, ALCbackend, ALCboolean, reset)
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, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCportCapture, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCportCapture, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCportCapture)
DEFINE_ALCBACKEND_VTABLE(ALCportCapture);
static void ALCportCapture_Construct(ALCportCapture *self, ALCdevice *device)
{
ALuint frame_size;
pa_data *data;
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCportCapture, ALCbackend, self);
self->stream = NULL;
}
static void ALCportCapture_Destruct(ALCportCapture *self)
{
if(self->stream)
Pa_CloseStream(self->stream);
self->stream = NULL;
if(self->ring)
ll_ringbuffer_free(self->ring);
self->ring = NULL;
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
static int ALCportCapture_ReadCallback(const void *inputBuffer, void *UNUSED(outputBuffer),
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
{
ALCportCapture *self = userData;
size_t writable = ll_ringbuffer_write_space(self->ring);
if(framesPerBuffer > writable)
framesPerBuffer = writable;
ll_ringbuffer_write(self->ring, inputBuffer, framesPerBuffer);
return 0;
}
static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
ALuint samples, frame_size;
PaError err;
if(!deviceName)
deviceName = pa_device;
else if(strcmp(deviceName, pa_device) != 0)
if(!name)
name = pa_device;
else if(strcmp(name, pa_device) != 0)
return ALC_INVALID_VALUE;
data = (pa_data*)calloc(1, sizeof(pa_data));
if(data == NULL)
return ALC_OUT_OF_MEMORY;
samples = device->UpdateSize * device->NumUpdates;
samples = maxu(samples, 100 * device->Frequency / 1000);
frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
data->ring = CreateRingBuffer(frame_size, device->UpdateSize*device->NumUpdates);
if(data->ring == NULL)
goto error;
self->ring = ll_ringbuffer_create(samples, frame_size);
if(self->ring == NULL) return ALC_INVALID_VALUE;
data->params.device = -1;
if(!ConfigValueInt("port", "capture", &data->params.device) ||
data->params.device < 0)
data->params.device = Pa_GetDefaultInputDevice();
data->params.suggestedLatency = 0.0f;
data->params.hostApiSpecificStreamInfo = NULL;
self->params.device = -1;
if(!ConfigValueInt(NULL, "port", "capture", &self->params.device) ||
self->params.device < 0)
self->params.device = Pa_GetDefaultInputDevice();
self->params.suggestedLatency = 0.0f;
self->params.hostApiSpecificStreamInfo = NULL;
switch(device->FmtType)
{
case DevFmtByte:
data->params.sampleFormat = paInt8;
self->params.sampleFormat = paInt8;
break;
case DevFmtUByte:
data->params.sampleFormat = paUInt8;
self->params.sampleFormat = paUInt8;
break;
case DevFmtShort:
data->params.sampleFormat = paInt16;
self->params.sampleFormat = paInt16;
break;
case DevFmtInt:
data->params.sampleFormat = paInt32;
self->params.sampleFormat = paInt32;
break;
case DevFmtFloat:
data->params.sampleFormat = paFloat32;
self->params.sampleFormat = paFloat32;
break;
case DevFmtUInt:
case DevFmtUShort:
ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
goto error;
return ALC_INVALID_VALUE;
}
data->params.channelCount = ChannelsFromDevFmt(device->FmtChans);
self->params.channelCount = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
err = Pa_OpenStream(&data->stream, &data->params, NULL, device->Frequency,
paFramesPerBufferUnspecified, paNoFlag, pa_capture_cb, device);
err = Pa_OpenStream(&self->stream, &self->params, NULL,
device->Frequency, paFramesPerBufferUnspecified, paNoFlag,
ALCportCapture_ReadCallback, self
);
if(err != paNoError)
{
ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
goto error;
return ALC_INVALID_VALUE;
}
al_string_copy_cstr(&device->DeviceName, deviceName);
alstr_copy_cstr(&device->DeviceName, name);
device->ExtraData = data;
return ALC_NO_ERROR;
error:
DestroyRingBuffer(data->ring);
free(data);
return ALC_INVALID_VALUE;
}
static void pa_close_capture(ALCdevice *device)
static void ALCportCapture_close(ALCportCapture *self)
{
pa_data *data = (pa_data*)device->ExtraData;
PaError err;
err = Pa_CloseStream(data->stream);
PaError err = Pa_CloseStream(self->stream);
if(err != paNoError)
ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
self->stream = NULL;
DestroyRingBuffer(data->ring);
data->ring = NULL;
free(data);
device->ExtraData = NULL;
ll_ringbuffer_free(self->ring);
self->ring = NULL;
}
static void pa_start_capture(ALCdevice *device)
{
pa_data *data = device->ExtraData;
PaError err;
err = Pa_StartStream(data->stream);
static ALCboolean ALCportCapture_start(ALCportCapture *self)
{
PaError err = Pa_StartStream(self->stream);
if(err != paNoError)
{
ERR("Error starting stream: %s\n", Pa_GetErrorText(err));
return ALC_FALSE;
}
return ALC_TRUE;
}
static void pa_stop_capture(ALCdevice *device)
static void ALCportCapture_stop(ALCportCapture *self)
{
pa_data *data = (pa_data*)device->ExtraData;
PaError err;
err = Pa_StopStream(data->stream);
PaError err = Pa_StopStream(self->stream);
if(err != paNoError)
ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
}
static ALCenum pa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
static ALCuint ALCportCapture_availableSamples(ALCportCapture *self)
{
pa_data *data = device->ExtraData;
ReadRingBuffer(data->ring, buffer, samples);
return ll_ringbuffer_read_space(self->ring);
}
static ALCenum ALCportCapture_captureSamples(ALCportCapture *self, ALCvoid *buffer, ALCuint samples)
{
ll_ringbuffer_read(self->ring, buffer, samples);
return ALC_NO_ERROR;
}
static ALCuint pa_available_samples(ALCdevice *device)
{
pa_data *data = device->ExtraData;
return RingBufferSize(data->ring);
}
typedef struct ALCportBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCportBackendFactory;
#define ALCPORTBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCportBackendFactory, ALCbackendFactory) } }
static ALCboolean ALCportBackendFactory_init(ALCportBackendFactory *self);
static void ALCportBackendFactory_deinit(ALCportBackendFactory *self);
static ALCboolean ALCportBackendFactory_querySupport(ALCportBackendFactory *self, ALCbackend_Type type);
static void ALCportBackendFactory_probe(ALCportBackendFactory *self, enum DevProbe type);
static ALCbackend* ALCportBackendFactory_createBackend(ALCportBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCportBackendFactory);
static const BackendFuncs pa_funcs = {
pa_open_playback,
pa_close_playback,
pa_reset_playback,
pa_start_playback,
pa_stop_playback,
pa_open_capture,
pa_close_capture,
pa_start_capture,
pa_stop_capture,
pa_capture_samples,
pa_available_samples,
ALCdevice_GetLatencyDefault
};
ALCboolean alc_pa_init(BackendFuncs *func_list)
static ALCboolean ALCportBackendFactory_init(ALCportBackendFactory* UNUSED(self))
{
if(!pa_load())
return ALC_FALSE;
*func_list = pa_funcs;
return ALC_TRUE;
}
void alc_pa_deinit(void)
static void ALCportBackendFactory_deinit(ALCportBackendFactory* UNUSED(self))
{
#ifdef HAVE_DYNLOAD
if(pa_handle)
@@ -455,7 +528,14 @@ void alc_pa_deinit(void)
#endif
}
void alc_pa_probe(enum DevProbe type)
static ALCboolean ALCportBackendFactory_querySupport(ALCportBackendFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Playback || type == ALCbackend_Capture)
return ALC_TRUE;
return ALC_FALSE;
}
static void ALCportBackendFactory_probe(ALCportBackendFactory* UNUSED(self), enum DevProbe type)
{
switch(type)
{
@@ -467,3 +547,29 @@ void alc_pa_probe(enum DevProbe type)
break;
}
}
static ALCbackend* ALCportBackendFactory_createBackend(ALCportBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCportPlayback *backend;
NEW_OBJ(backend, ALCportPlayback)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
if(type == ALCbackend_Capture)
{
ALCportCapture *backend;
NEW_OBJ(backend, ALCportCapture)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
ALCbackendFactory *ALCportBackendFactory_getFactory(void)
{
static ALCportBackendFactory factory = ALCPORTBACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
+349 -209
View File
File diff suppressed because it is too large Load Diff
+356 -448
View File
File diff suppressed because it is too large Load Diff
+132 -81
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -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,78 +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,
ALCdevice_GetLatencyDefault
};
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)
{
@@ -293,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;
}
+180 -105
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -22,6 +22,7 @@
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
@@ -36,109 +37,171 @@
#include "threads.h"
#include "compat.h"
#include "backends/base.h"
#include <sys/audioio.h>
typedef struct ALCsolarisBackend {
DERIVE_FROM_TYPE(ALCbackend);
int fd;
ALubyte *mix_data;
int data_size;
ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCsolarisBackend;
static int ALCsolarisBackend_mixerProc(void *ptr);
static void ALCsolarisBackend_Construct(ALCsolarisBackend *self, ALCdevice *device);
static void ALCsolarisBackend_Destruct(ALCsolarisBackend *self);
static ALCenum ALCsolarisBackend_open(ALCsolarisBackend *self, const ALCchar *name);
static void ALCsolarisBackend_close(ALCsolarisBackend *self);
static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self);
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, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCsolarisBackend)
DEFINE_ALCBACKEND_VTABLE(ALCsolarisBackend);
static const ALCchar solaris_device[] = "Solaris Default";
static const char *solaris_driver = "/dev/audio";
typedef struct {
int fd;
ALubyte *mix_data;
int data_size;
volatile int killNow;
althrd_t thread;
} solaris_data;
static int SolarisProc(void *ptr)
static void ALCsolarisBackend_Construct(ALCsolarisBackend *self, ALCdevice *device)
{
ALCdevice *Device = (ALCdevice*)ptr;
solaris_data *data = (solaris_data*)Device->ExtraData;
ALint frameSize;
int wrote;
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCsolarisBackend, ALCbackend, self);
self->fd = -1;
ATOMIC_INIT(&self->killNow, AL_FALSE);
}
static void ALCsolarisBackend_Destruct(ALCsolarisBackend *self)
{
if(self->fd != -1)
close(self->fd);
self->fd = -1;
free(self->mix_data);
self->mix_data = NULL;
self->data_size = 0;
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
static int ALCsolarisBackend_mixerProc(void *ptr)
{
ALCsolarisBackend *self = ptr;
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(!data->killNow && Device->Connected)
ALCsolarisBackend_lock(self);
while(!ATOMIC_LOAD_SEQ(&self->killNow) && device->Connected)
{
ALint len = data->data_size;
ALubyte *WritePtr = data->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 && !data->killNow)
ALCsolarisBackend_unlock(self);
sret = select(self->fd+1, NULL, &wfds, NULL, &timeout);
ALCsolarisBackend_lock(self);
if(sret < 0)
{
wrote = write(data->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));
ALCdevice_Lock(Device);
aluHandleDisconnect(Device);
ALCdevice_Unlock(Device);
break;
}
al_nssleep(0, 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;
}
static ALCenum solaris_open_playback(ALCdevice *device, const ALCchar *deviceName)
static ALCenum ALCsolarisBackend_open(ALCsolarisBackend *self, const ALCchar *name)
{
solaris_data *data;
ALCdevice *device;
if(!deviceName)
deviceName = solaris_device;
else if(strcmp(deviceName, solaris_device) != 0)
if(!name)
name = solaris_device;
else if(strcmp(name, solaris_device) != 0)
return ALC_INVALID_VALUE;
data = (solaris_data*)calloc(1, sizeof(solaris_data));
data->killNow = 0;
data->fd = open(solaris_driver, O_WRONLY);
if(data->fd == -1)
self->fd = open(solaris_driver, O_WRONLY);
if(self->fd == -1)
{
free(data);
ERR("Could not open %s: %s\n", solaris_driver, strerror(errno));
return ALC_INVALID_VALUE;
}
al_string_copy_cstr(&device->DeviceName, deviceName);
device->ExtraData = data;
device = STATIC_CAST(ALCbackend,self)->mDevice;
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
static void solaris_close_playback(ALCdevice *device)
static void ALCsolarisBackend_close(ALCsolarisBackend *self)
{
solaris_data *data = (solaris_data*)device->ExtraData;
close(data->fd);
free(data);
device->ExtraData = NULL;
close(self->fd);
self->fd = -1;
}
static ALCboolean solaris_reset_playback(ALCdevice *device)
static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
{
solaris_data *data = (solaris_data*)device->ExtraData;
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
audio_info_t info;
ALuint frameSize;
int numChannels;
ALsizei frameSize;
ALsizei numChannels;
AUDIO_INITINFO(&info);
@@ -146,7 +209,7 @@ static ALCboolean solaris_reset_playback(ALCdevice *device)
if(device->FmtChans != DevFmtMono)
device->FmtChans = DevFmtStereo;
numChannels = ChannelsFromDevFmt(device->FmtChans);
numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
info.play.channels = numChannels;
switch(device->FmtType)
@@ -174,15 +237,15 @@ static ALCboolean solaris_reset_playback(ALCdevice *device)
frameSize = numChannels * BytesFromDevFmt(device->FmtType);
info.play.buffer_size = device->UpdateSize*device->NumUpdates * frameSize;
if(ioctl(data->fd, AUDIO_SETINFO, &info) < 0)
if(ioctl(self->fd, AUDIO_SETINFO, &info) < 0)
{
ERR("ioctl failed: %s\n", strerror(errno));
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;
}
@@ -201,74 +264,73 @@ static ALCboolean solaris_reset_playback(ALCdevice *device)
SetDefaultChannelOrder(device);
free(self->mix_data);
self->data_size = device->UpdateSize * FrameSizeFromDevFmt(
device->FmtChans, device->FmtType, device->AmbiOrder
);
self->mix_data = calloc(1, self->data_size);
return ALC_TRUE;
}
static ALCboolean solaris_start_playback(ALCdevice *device)
static ALCboolean ALCsolarisBackend_start(ALCsolarisBackend *self)
{
solaris_data *data = (solaris_data*)device->ExtraData;
data->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
data->mix_data = calloc(1, data->data_size);
data->killNow = 0;
if(althrd_create(&data->thread, SolarisProc, device) != althrd_success)
{
free(data->mix_data);
data->mix_data = NULL;
ATOMIC_STORE_SEQ(&self->killNow, AL_FALSE);
if(althrd_create(&self->thread, ALCsolarisBackend_mixerProc, self) != althrd_success)
return ALC_FALSE;
}
return ALC_TRUE;
}
static void solaris_stop_playback(ALCdevice *device)
static void ALCsolarisBackend_stop(ALCsolarisBackend *self)
{
solaris_data *data = (solaris_data*)device->ExtraData;
int res;
if(data->killNow)
if(ATOMIC_EXCHANGE_SEQ(&self->killNow, AL_TRUE))
return;
data->killNow = 1;
althrd_join(data->thread, &res);
althrd_join(self->thread, &res);
if(ioctl(data->fd, AUDIO_DRAIN) < 0)
if(ioctl(self->fd, AUDIO_DRAIN) < 0)
ERR("Error draining device: %s\n", strerror(errno));
free(data->mix_data);
data->mix_data = NULL;
}
static const BackendFuncs solaris_funcs = {
solaris_open_playback,
solaris_close_playback,
solaris_reset_playback,
solaris_start_playback,
solaris_stop_playback,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
ALCdevice_GetLatencyDefault
};
typedef struct ALCsolarisBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCsolarisBackendFactory;
#define ALCSOLARISBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCsolarisBackendFactory, ALCbackendFactory) } }
ALCboolean alc_solaris_init(BackendFuncs *func_list)
ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void);
static ALCboolean ALCsolarisBackendFactory_init(ALCsolarisBackendFactory *self);
static DECLARE_FORWARD(ALCsolarisBackendFactory, ALCbackendFactory, void, deinit)
static ALCboolean ALCsolarisBackendFactory_querySupport(ALCsolarisBackendFactory *self, ALCbackend_Type type);
static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory *self, enum DevProbe type);
static ALCbackend* ALCsolarisBackendFactory_createBackend(ALCsolarisBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsolarisBackendFactory);
ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void)
{
ConfigValueStr("solaris", "device", &solaris_driver);
static ALCsolarisBackendFactory factory = ALCSOLARISBACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
*func_list = solaris_funcs;
static ALCboolean ALCsolarisBackendFactory_init(ALCsolarisBackendFactory* UNUSED(self))
{
ConfigValueStr(NULL, "solaris", "device", &solaris_driver);
return ALC_TRUE;
}
void alc_solaris_deinit(void)
static ALCboolean ALCsolarisBackendFactory_querySupport(ALCsolarisBackendFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
return ALC_TRUE;
return ALC_FALSE;
}
void alc_solaris_probe(enum DevProbe type)
static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory* UNUSED(self), enum DevProbe type)
{
switch(type)
{
@@ -286,3 +348,16 @@ void alc_solaris_probe(enum DevProbe type)
break;
}
}
ALCbackend* ALCsolarisBackendFactory_createBackend(ALCsolarisBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCsolarisBackend *backend;
NEW_OBJ(backend, ALCsolarisBackend)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
+223 -147
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -24,26 +24,13 @@
#include <stdio.h>
#include <memory.h>
#include <errno.h>
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#include "alMain.h"
#include "alu.h"
#include "threads.h"
#include "compat.h"
typedef struct {
FILE *f;
long DataStart;
ALvoid *buffer;
ALuint size;
volatile int killNow;
althrd_t thread;
} wave_data;
#include "backends/base.h"
static const ALCchar waveDevice[] = "Wave File Writer";
@@ -57,38 +44,80 @@ static const ALubyte SUBTYPE_FLOAT[] = {
0x00, 0x38, 0x9b, 0x71
};
static const ALuint channel_masks[] = {
0, /* invalid */
0x4, /* Mono */
0x1 | 0x2, /* Stereo */
0, /* 3 channel */
0x1 | 0x2 | 0x10 | 0x20, /* Quad */
0, /* 5 channel */
0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20, /* 5.1 */
0x1 | 0x2 | 0x4 | 0x8 | 0x100 | 0x200 | 0x400, /* 6.1 */
0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20 | 0x200 | 0x400, /* 7.1 */
static const ALubyte SUBTYPE_BFORMAT_PCM[] = {
0x01, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
0xca, 0x00, 0x00, 0x00
};
static const ALubyte SUBTYPE_BFORMAT_FLOAT[] = {
0x03, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
0xca, 0x00, 0x00, 0x00
};
static void fwrite16le(ALushort val, FILE *f)
{
fputc(val&0xff, f);
fputc((val>>8)&0xff, f);
ALubyte data[2] = { val&0xff, (val>>8)&0xff };
fwrite(data, 1, 2, f);
}
static void fwrite32le(ALuint val, FILE *f)
{
fputc(val&0xff, f);
fputc((val>>8)&0xff, f);
fputc((val>>16)&0xff, f);
fputc((val>>24)&0xff, f);
ALubyte data[4] = { val&0xff, (val>>8)&0xff, (val>>16)&0xff, (val>>24)&0xff };
fwrite(data, 1, 4, f);
}
static int WaveProc(void *ptr)
typedef struct ALCwaveBackend {
DERIVE_FROM_TYPE(ALCbackend);
FILE *mFile;
long mDataStart;
ALvoid *mBuffer;
ALuint mSize;
volatile int killNow;
althrd_t thread;
} ALCwaveBackend;
static int ALCwaveBackend_mixerProc(void *ptr);
static void ALCwaveBackend_Construct(ALCwaveBackend *self, ALCdevice *device);
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, Destruct)
static ALCenum ALCwaveBackend_open(ALCwaveBackend *self, const ALCchar *name);
static void ALCwaveBackend_close(ALCwaveBackend *self);
static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self);
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, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCwaveBackend)
DEFINE_ALCBACKEND_VTABLE(ALCwaveBackend);
static void ALCwaveBackend_Construct(ALCwaveBackend *self, ALCdevice *device)
{
ALCdevice *device = (ALCdevice*)ptr;
wave_data *data = (wave_data*)device->ExtraData;
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCwaveBackend, ALCbackend, self);
self->mFile = NULL;
self->mDataStart = -1;
self->mBuffer = NULL;
self->mSize = 0;
self->killNow = 1;
}
static int ALCwaveBackend_mixerProc(void *ptr)
{
ALCwaveBackend *self = (ALCwaveBackend*)ptr;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
struct timespec now, start;
ALint64 avail, done;
ALuint frameSize;
@@ -98,7 +127,7 @@ static int WaveProc(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)
@@ -106,7 +135,7 @@ static int WaveProc(void *ptr)
ERR("Failed to get starting time\n");
return 1;
}
while(!data->killNow && device->Connected)
while(!self->killNow && device->Connected)
{
if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC)
{
@@ -125,41 +154,45 @@ static int WaveProc(void *ptr)
}
if(avail-done < device->UpdateSize)
al_nssleep(0, restTime);
al_nssleep(restTime);
else while(avail-done >= device->UpdateSize)
{
aluMixData(device, data->buffer, 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 = data->buffer;
ALuint i;
if(bytesize == 1)
if(bytesize == 2)
{
for(i = 0;i < data->size;i++)
fputc(bytes[i], data->f);
}
else if(bytesize == 2)
{
for(i = 0;i < data->size;i++)
fputc(bytes[i^1], data->f);
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 < data->size;i++)
fputc(bytes[i^3], data->f);
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(data->buffer, frameSize, device->UpdateSize,
data->f);
(void)fs;
}
if(ferror(data->f))
fs = fwrite(self->mBuffer, frameSize, device->UpdateSize, self->mFile);
(void)fs;
if(ferror(self->mFile))
{
ERR("Error writing to file\n");
ALCdevice_Lock(device);
@@ -173,52 +206,55 @@ static int WaveProc(void *ptr)
return 0;
}
static ALCenum wave_open_playback(ALCdevice *device, const ALCchar *deviceName)
static ALCenum ALCwaveBackend_open(ALCwaveBackend *self, const ALCchar *name)
{
wave_data *data;
ALCdevice *device;
const char *fname;
fname = GetConfigValue("wave", "file", "");
if(!fname[0])
fname = GetConfigValue(NULL, "wave", "file", "");
if(!fname[0]) return ALC_INVALID_VALUE;
if(!name)
name = waveDevice;
else if(strcmp(name, waveDevice) != 0)
return ALC_INVALID_VALUE;
if(!deviceName)
deviceName = waveDevice;
else if(strcmp(deviceName, waveDevice) != 0)
return ALC_INVALID_VALUE;
data = (wave_data*)calloc(1, sizeof(wave_data));
data->f = al_fopen(fname, "wb");
if(!data->f)
self->mFile = al_fopen(fname, "wb");
if(!self->mFile)
{
free(data);
ERR("Could not open file '%s': %s\n", fname, strerror(errno));
return ALC_INVALID_VALUE;
}
al_string_copy_cstr(&device->DeviceName, deviceName);
device->ExtraData = data;
device = STATIC_CAST(ALCbackend, self)->mDevice;
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
static void wave_close_playback(ALCdevice *device)
static void ALCwaveBackend_close(ALCwaveBackend *self)
{
wave_data *data = (wave_data*)device->ExtraData;
fclose(data->f);
free(data);
device->ExtraData = NULL;
if(self->mFile)
fclose(self->mFile);
self->mFile = NULL;
}
static ALCboolean wave_reset_playback(ALCdevice *device)
static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self)
{
wave_data *data = (wave_data*)device->ExtraData;
ALuint channels=0, bits=0;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
ALuint channels=0, bits=0, chanmask=0;
int isbformat = 0;
size_t val;
fseek(data->f, 0, SEEK_SET);
clearerr(data->f);
fseek(self->mFile, 0, SEEK_SET);
clearerr(self->mFile);
if(GetConfigValueBool(NULL, "wave", "bformat", 0))
{
device->FmtChans = DevFmtAmbi3D;
device->AmbiOrder = 1;
}
switch(device->FmtType)
{
@@ -237,135 +273,162 @@ static ALCboolean wave_reset_playback(ALCdevice *device)
case DevFmtFloat:
break;
}
switch(device->FmtChans)
{
case DevFmtMono: chanmask = 0x04; break;
case DevFmtStereo: chanmask = 0x01 | 0x02; break;
case DevFmtQuad: chanmask = 0x01 | 0x02 | 0x10 | 0x20; break;
case DevFmtX51: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x200 | 0x400; break;
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 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(data->f, "RIFF");
fwrite32le(0xFFFFFFFF, data->f); // 'RIFF' header len; filled in at close
fputs("RIFF", self->mFile);
fwrite32le(0xFFFFFFFF, self->mFile); // 'RIFF' header len; filled in at close
fprintf(data->f, "WAVE");
fputs("WAVE", self->mFile);
fprintf(data->f, "fmt ");
fwrite32le(40, data->f); // 'fmt ' header len; 40 bytes for EXTENSIBLE
fputs("fmt ", self->mFile);
fwrite32le(40, self->mFile); // 'fmt ' header len; 40 bytes for EXTENSIBLE
// 16-bit val, format type id (extensible: 0xFFFE)
fwrite16le(0xFFFE, data->f);
fwrite16le(0xFFFE, self->mFile);
// 16-bit val, channel count
fwrite16le(channels, data->f);
fwrite16le(channels, self->mFile);
// 32-bit val, frequency
fwrite32le(device->Frequency, data->f);
fwrite32le(device->Frequency, self->mFile);
// 32-bit val, bytes per second
fwrite32le(device->Frequency * channels * bits / 8, data->f);
fwrite32le(device->Frequency * channels * bits / 8, self->mFile);
// 16-bit val, frame size
fwrite16le(channels * bits / 8, data->f);
fwrite16le(channels * bits / 8, self->mFile);
// 16-bit val, bits per sample
fwrite16le(bits, data->f);
fwrite16le(bits, self->mFile);
// 16-bit val, extra byte count
fwrite16le(22, data->f);
fwrite16le(22, self->mFile);
// 16-bit val, valid bits per sample
fwrite16le(bits, data->f);
fwrite16le(bits, self->mFile);
// 32-bit val, channel mask
fwrite32le(channel_masks[channels], data->f);
fwrite32le(chanmask, self->mFile);
// 16 byte GUID, sub-type format
val = fwrite(((bits==32) ? SUBTYPE_FLOAT : SUBTYPE_PCM), 1, 16, data->f);
val = fwrite((device->FmtType == DevFmtFloat) ?
(isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) :
(isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM), 1, 16, self->mFile);
(void)val;
fprintf(data->f, "data");
fwrite32le(0xFFFFFFFF, data->f); // 'data' header len; filled in at close
fputs("data", self->mFile);
fwrite32le(0xFFFFFFFF, self->mFile); // 'data' header len; filled in at close
if(ferror(data->f))
if(ferror(self->mFile))
{
ERR("Error writing header: %s\n", strerror(errno));
return ALC_FALSE;
}
data->DataStart = ftell(data->f);
self->mDataStart = ftell(self->mFile);
SetDefaultWFXChannelOrder(device);
return ALC_TRUE;
}
static ALCboolean wave_start_playback(ALCdevice *device)
static ALCboolean ALCwaveBackend_start(ALCwaveBackend *self)
{
wave_data *data = (wave_data*)device->ExtraData;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
data->size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
data->buffer = malloc(data->size);
if(!data->buffer)
self->mSize = device->UpdateSize * FrameSizeFromDevFmt(
device->FmtChans, device->FmtType, device->AmbiOrder
);
self->mBuffer = malloc(self->mSize);
if(!self->mBuffer)
{
ERR("Buffer malloc failed\n");
return ALC_FALSE;
}
data->killNow = 0;
if(althrd_create(&data->thread, WaveProc, device) != althrd_success)
self->killNow = 0;
if(althrd_create(&self->thread, ALCwaveBackend_mixerProc, self) != althrd_success)
{
free(data->buffer);
data->buffer = NULL;
free(self->mBuffer);
self->mBuffer = NULL;
self->mSize = 0;
return ALC_FALSE;
}
return ALC_TRUE;
}
static void wave_stop_playback(ALCdevice *device)
static void ALCwaveBackend_stop(ALCwaveBackend *self)
{
wave_data *data = (wave_data*)device->ExtraData;
ALuint dataLen;
long size;
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);
free(data->buffer);
data->buffer = NULL;
free(self->mBuffer);
self->mBuffer = NULL;
size = ftell(data->f);
size = ftell(self->mFile);
if(size > 0)
{
dataLen = size - data->DataStart;
if(fseek(data->f, data->DataStart-4, SEEK_SET) == 0)
fwrite32le(dataLen, data->f); // 'data' header len
if(fseek(data->f, 4, SEEK_SET) == 0)
fwrite32le(size-8, data->f); // 'WAVE' header len
dataLen = size - self->mDataStart;
if(fseek(self->mFile, self->mDataStart-4, SEEK_SET) == 0)
fwrite32le(dataLen, self->mFile); // 'data' header len
if(fseek(self->mFile, 4, SEEK_SET) == 0)
fwrite32le(size-8, self->mFile); // 'WAVE' header len
}
}
static const BackendFuncs wave_funcs = {
wave_open_playback,
wave_close_playback,
wave_reset_playback,
wave_start_playback,
wave_stop_playback,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
ALCdevice_GetLatencyDefault
};
typedef struct ALCwaveBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCwaveBackendFactory;
#define ALCWAVEBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCwaveBackendFactory, ALCbackendFactory) } }
ALCboolean alc_wave_init(BackendFuncs *func_list)
ALCbackendFactory *ALCwaveBackendFactory_getFactory(void);
static ALCboolean ALCwaveBackendFactory_init(ALCwaveBackendFactory *self);
static DECLARE_FORWARD(ALCwaveBackendFactory, ALCbackendFactory, void, deinit)
static ALCboolean ALCwaveBackendFactory_querySupport(ALCwaveBackendFactory *self, ALCbackend_Type type);
static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory *self, enum DevProbe type);
static ALCbackend* ALCwaveBackendFactory_createBackend(ALCwaveBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwaveBackendFactory);
ALCbackendFactory *ALCwaveBackendFactory_getFactory(void)
{
static ALCwaveBackendFactory factory = ALCWAVEBACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
static ALCboolean ALCwaveBackendFactory_init(ALCwaveBackendFactory* UNUSED(self))
{
*func_list = wave_funcs;
return ALC_TRUE;
}
void alc_wave_deinit(void)
static ALCboolean ALCwaveBackendFactory_querySupport(ALCwaveBackendFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
return !!ConfigValueExists(NULL, "wave", "file");
return ALC_FALSE;
}
void alc_wave_probe(enum DevProbe type)
static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory* UNUSED(self), enum DevProbe type)
{
if(!ConfigValueExists("wave", "file"))
return;
switch(type)
{
case ALL_DEVICE_PROBE:
@@ -375,3 +438,16 @@ void alc_wave_probe(enum DevProbe type)
break;
}
}
static ALCbackend* ALCwaveBackendFactory_createBackend(ALCwaveBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCwaveBackend *backend;
NEW_OBJ(backend, ALCwaveBackend)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
+410 -323
View File
File diff suppressed because it is too large Load Diff
+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 */
+62 -18
View File
@@ -29,9 +29,6 @@
#include "bs2b.h"
#include "alu.h"
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
/* Set up all data. */
static void init(struct bs2b *bs2b)
@@ -40,8 +37,6 @@ static void init(struct bs2b *bs2b)
float G_lo, G_hi;
float x, g;
bs2b->srate = clampi(bs2b->srate, 2000, 192000);
switch(bs2b->level)
{
case BS2B_LOW_CLEVEL: /* Low crossfeed level */
@@ -105,31 +100,25 @@ static void init(struct bs2b *bs2b)
bs2b->a1_hi = -x * g;
} /* init */
/* Exported functions.
* See descriptions in "bs2b.h"
*/
void bs2b_set_level(struct bs2b *bs2b, int level)
void bs2b_set_params(struct bs2b *bs2b, int level, int srate)
{
if(level == bs2b->level)
return;
if(srate <= 0) srate = 1;
bs2b->level = level;
bs2b->srate = srate;
init(bs2b);
} /* bs2b_set_level */
} /* bs2b_set_params */
int bs2b_get_level(struct bs2b *bs2b)
{
return bs2b->level;
} /* bs2b_get_level */
void bs2b_set_srate(struct bs2b *bs2b, int srate)
{
if (srate == bs2b->srate)
return;
bs2b->srate = srate;
init(bs2b);
} /* bs2b_set_srate */
int bs2b_get_srate(struct bs2b *bs2b)
{
return bs2b->srate;
@@ -140,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 */
+5081
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 */
-272
View File
@@ -1,272 +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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include <stdlib.h>
#include "config.h"
#include "alu.h"
#include "alFilter.h"
#include "alError.h"
#include "alMain.h"
#include "alAuxEffectSlot.h"
/* Auto-wah is simply a low-pass filter with a cutoff frequency that shifts up
* or down depending on the input signal, and a resonant peak at the cutoff.
*
* Currently, we assume a cutoff frequency range of 500hz (no amplitude) to
* 3khz (peak gain). Peak gain is assumed to be in normalized scale.
*/
typedef struct ALautowahState {
DERIVE_FROM_TYPE(ALeffectState);
/* Effect gains for each channel */
ALfloat Gain[MaxChannels];
/* Effect parameters */
ALfloat AttackRate;
ALfloat ReleaseRate;
ALfloat Resonance;
ALfloat PeakGain;
ALfloat GainCtrl;
ALfloat Frequency;
/* Samples processing */
ALfilterState LowPass;
} ALautowahState;
static ALvoid ALautowahState_Destruct(ALautowahState *UNUSED(state))
{
}
static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *device)
{
state->Frequency = (ALfloat)device->Frequency;
return AL_TRUE;
}
static ALvoid ALautowahState_update(ALautowahState *state, ALCdevice *device, const ALeffectslot *slot)
{
ALfloat attackTime, releaseTime;
ALfloat gain;
attackTime = slot->EffectProps.Autowah.AttackTime * state->Frequency;
releaseTime = slot->EffectProps.Autowah.ReleaseTime * state->Frequency;
state->AttackRate = powf(1.0f/GAIN_SILENCE_THRESHOLD, 1.0f/attackTime);
state->ReleaseRate = powf(GAIN_SILENCE_THRESHOLD/1.0f, 1.0f/releaseTime);
state->PeakGain = slot->EffectProps.Autowah.PeakGain;
state->Resonance = slot->EffectProps.Autowah.Resonance;
gain = sqrtf(1.0f / device->NumChan) * slot->Gain;
SetGains(device, gain, state->Gain);
}
static ALvoid ALautowahState_process(ALautowahState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE])
{
ALuint it, kt;
ALuint base;
for(base = 0;base < SamplesToDo;)
{
ALfloat temps[64];
ALuint td = minu(SamplesToDo-base, 64);
ALfloat gain = state->GainCtrl;
for(it = 0;it < td;it++)
{
ALfloat smp = SamplesIn[it+base];
ALfloat alpha, w0;
ALfloat amplitude;
ALfloat cutoff;
/* Similar to compressor, we get the current amplitude of the
* incoming signal, and attack or release to reach it. */
amplitude = fabsf(smp);
if(amplitude > gain)
gain = minf(gain*state->AttackRate, amplitude);
else if(amplitude < gain)
gain = maxf(gain*state->ReleaseRate, amplitude);
gain = maxf(gain, GAIN_SILENCE_THRESHOLD);
/* FIXME: What range does the filter cover? */
cutoff = lerp(20.0f, 20000.0f, minf(gain/state->PeakGain, 1.0f));
/* The code below is like calling ALfilterState_setParams with
* ALfilterType_LowPass. However, instead of passing a bandwidth,
* we use the resonance property for Q. This also inlines the call.
*/
w0 = F_2PI * cutoff / state->Frequency;
/* FIXME: Resonance controls the resonant peak, or Q. How? Not sure
* that Q = resonance*0.1. */
alpha = sinf(w0) / (2.0f * state->Resonance*0.1f);
state->LowPass.b[0] = (1.0f - cosf(w0)) / 2.0f;
state->LowPass.b[1] = 1.0f - cosf(w0);
state->LowPass.b[2] = (1.0f - cosf(w0)) / 2.0f;
state->LowPass.a[0] = 1.0f + alpha;
state->LowPass.a[1] = -2.0f * cosf(w0);
state->LowPass.a[2] = 1.0f - alpha;
state->LowPass.b[2] /= state->LowPass.a[0];
state->LowPass.b[1] /= state->LowPass.a[0];
state->LowPass.b[0] /= state->LowPass.a[0];
state->LowPass.a[2] /= state->LowPass.a[0];
state->LowPass.a[1] /= state->LowPass.a[0];
state->LowPass.a[0] /= state->LowPass.a[0];
temps[it] = ALfilterState_processSingle(&state->LowPass, smp);
}
state->GainCtrl = gain;
for(kt = 0;kt < MaxChannels;kt++)
{
ALfloat gain = state->Gain[kt];
if(!(gain > GAIN_SILENCE_THRESHOLD))
continue;
for(it = 0;it < td;it++)
SamplesOut[kt][base+it] += gain * temps[it];
}
base += td;
}
}
DECLARE_DEFAULT_ALLOCATORS(ALautowahState)
DEFINE_ALEFFECTSTATE_VTABLE(ALautowahState);
typedef struct ALautowahStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALautowahStateFactory;
static ALeffectState *ALautowahStateFactory_create(ALautowahStateFactory *UNUSED(factory))
{
ALautowahState *state;
state = ALautowahState_New(sizeof(*state));
if(!state) return NULL;
SET_VTABLE2(ALautowahState, ALeffectState, state);
state->AttackRate = 1.0f;
state->ReleaseRate = 1.0f;
state->Resonance = 2.0f;
state->PeakGain = 1.0f;
state->GainCtrl = 1.0f;
ALfilterState_clear(&state->LowPass);
return STATIC_CAST(ALeffectState, state);
}
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALautowahStateFactory);
ALeffectStateFactory *ALautowahStateFactory_getFactory(void)
{
static ALautowahStateFactory AutowahFactory = { { GET_VTABLE2(ALautowahStateFactory, ALeffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &AutowahFactory);
}
void ALautowah_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALautowah_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALautowah_setParami(effect, context, param, vals[0]);
}
void ALautowah_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_AUTOWAH_ATTACK_TIME:
if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Autowah.AttackTime = val;
break;
case AL_AUTOWAH_RELEASE_TIME:
if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Autowah.ReleaseTime = val;
break;
case AL_AUTOWAH_RESONANCE:
if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Autowah.Resonance = val;
break;
case AL_AUTOWAH_PEAK_GAIN:
if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Autowah.PeakGain = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALautowah_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALautowah_setParamf(effect, context, param, vals[0]);
}
void ALautowah_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALautowah_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALautowah_getParami(effect, context, param, vals);
}
void ALautowah_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_AUTOWAH_ATTACK_TIME:
*val = props->Autowah.AttackTime;
break;
case AL_AUTOWAH_RELEASE_TIME:
*val = props->Autowah.ReleaseTime;
break;
case AL_AUTOWAH_RESONANCE:
*val = props->Autowah.Resonance;
break;
case AL_AUTOWAH_PEAK_GAIN:
*val = props->Autowah.PeakGain;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALautowah_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALautowah_getParamf(effect, context, param, vals);
}
DEFINE_ALEFFECT_VTABLE(ALautowah);
+122 -109
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -39,14 +39,14 @@ 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;
/* Gains for left and right sides */
ALfloat Gain[2][MaxChannels];
ALfloat Gain[2][MAX_OUTPUT_CHANNELS];
/* effect parameters */
enum ChorusWaveForm waveform;
@@ -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,13 +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)
{
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;
@@ -106,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 */
ComputeAngleGains(Device, atan2f(-1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[0]);
ComputeAngleGains(Device, atan2f(+1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[1]);
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;
@@ -125,127 +153,120 @@ 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:
state->lfo_scale = 4.0f / state->lfo_range;
break;
case CWF_Sinusoid:
state->lfo_scale = F_2PI / state->lfo_range;
state->lfo_scale = F_TAU / state->lfo_range;
break;
}
/* 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])
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;)
{
ALfloat temps[64][2];
ALuint td = minu(SamplesToDo-base, 64);
const ALsizei todo = mini(128, SamplesToDo-base);
ALfloat temps[128][2];
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 < MaxChannels;kt++)
for(i = 0;i < todo;i++)
{
ALfloat gain = state->Gain[0][kt];
if(gain > GAIN_SILENCE_THRESHOLD)
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];
if(gain > GAIN_SILENCE_THRESHOLD)
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);
@@ -255,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);
}
+79 -45
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -31,7 +31,7 @@ typedef struct ALcompressorState {
DERIVE_FROM_TYPE(ALeffectState);
/* Effect gains for each channel */
ALfloat Gain[MaxChannels];
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,88 +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)
{
ALfloat gain;
ALuint i;
state->Enabled = Slot->EffectProps.Compressor.OnOff;
state->Enabled = props->Compressor.OnOff;
gain = sqrtf(1.0f / Device->NumChan) * Slot->Gain;
SetGains(Device, gain, state->Gain);
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])
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[64];
ALuint td = minu(SamplesToDo-base, 64);
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 < MaxChannels;kt++)
/* Now mix to the output. */
for(j = 0;j < 4;j++)
{
ALfloat gain = state->Gain[kt];
if(!(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);
@@ -146,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);
}
+68 -29
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -32,12 +32,32 @@
typedef struct ALdedicatedState {
DERIVE_FROM_TYPE(ALeffectState);
ALfloat gains[MaxChannels];
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,41 +65,65 @@ 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;
ALsizei s;
ALuint i;
Gain = Slot->Gain * Slot->EffectProps.Dedicated.Gain;
if(Slot->EffectType == AL_EFFECT_DEDICATED_DIALOGUE)
ComputeAngleGains(device, atan2f(0.0f, 1.0f), 0.0f, Gain, state->gains);
else if(Slot->EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
state->gains[i] = 0.0f;
Gain = Slot->Params.Gain * props->Dedicated.Gain;
if(Slot->Params.EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
{
for(s = 0;s < MaxChannels;s++)
state->gains[s] = 0.0f;
state->gains[LFE] = Gain;
int idx;
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->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->RealOut, FrontCenter)) != -1)
{
STATIC_CAST(ALeffectState,state)->OutBuffer = device->RealOut.Buffer;
STATIC_CAST(ALeffectState,state)->OutChannels = device->RealOut.NumChannels;
state->gains[idx] = Gain;
}
else
{
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])
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;
for(c = 0;c < MaxChannels;c++)
SamplesIn = ASSUME_ALIGNED(SamplesIn, 16);
SamplesOut = ASSUME_ALIGNED(SamplesOut, 16);
for(c = 0;c < NumChannels;c++)
{
if(!(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);
@@ -88,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 < MaxChannels;s++)
state->gains[s] = 0.0f;
return STATIC_CAST(ALeffectState, state);
}
+90 -90
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -34,7 +34,7 @@ typedef struct ALdistortionState {
DERIVE_FROM_TYPE(ALeffectState);
/* Effect gains for each channel */
ALfloat Gain[MaxChannels];
ALfloat Gain[MAX_OUTPUT_CHANNELS];
/* Effect parameters */
ALfilterState lowpass;
@@ -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,128 +71,113 @@ 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;
ALfloat gain;
/* 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), bandwidth);
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), bandwidth);
cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
);
gain = sqrtf(1.0f / Device->NumChan) * Slot->Gain;
SetGains(Device, 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])
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;
float oversample_buffer[64][4];
ALuint base;
ALuint it;
ALuint ot;
ALuint kt;
ALsizei it, kt;
ALsizei base;
for(base = 0;base < SamplesToDo;)
{
ALfloat temps[64];
ALuint td = minu(SamplesToDo-base, 64);
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++)
{
for(ot = 0;ot < 4;ot++)
{
ALfloat smp;
smp = ALfilterState_processSingle(&state->lowpass, oversample_buffer[it][ot]);
/* 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);
/* Restore signal power by multiplying sample by amount of oversampling */
oversample_buffer[it][ot] = smp * 4.0f;
}
/* 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++)
{
ALfloat smp = buffer[1][it];
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++)
/* Third step, do bandpass filtering of distorted signal. */
ALfilterState_process(&state->bandpass, buffer[1], buffer[0], td*4);
for(kt = 0;kt < NumChannels;kt++)
{
/* Second step, do distortion using waveshaper function */
/* to emulate signal processing during tube overdriving. */
/* Three steps of waveshaping are intended to modify */
/* waveform without boost/clipping/attenuation process. */
for(ot = 0;ot < 4;ot++)
{
ALfloat smp = oversample_buffer[it][ot];
smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp)) * -1.0f;
smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
/* Third step, do bandpass filtering of distorted signal */
smp = ALfilterState_processSingle(&state->bandpass, smp);
oversample_buffer[it][ot] = smp;
}
/* Fourth step, final, do attenuation and perform decimation, */
/* store only one sample out of 4. */
temps[it] = oversample_buffer[it][0] * state->attenuation;
}
for(kt = 0;kt < MaxChannels;kt++)
{
ALfloat gain = state->Gain[kt];
if(!(gain > GAIN_SILENCE_THRESHOLD))
/* Fourth step, final, do attenuation and perform decimation,
* store only one sample out of 4.
*/
ALfloat gain = state->Gain[kt] * state->attenuation;
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
continue;
for(it = 0;it < td;it++)
SamplesOut[kt][base+it] += gain * temps[it];
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);
@@ -183,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);
}
+159 -126
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -34,148 +34,34 @@ 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][MaxChannels];
ALfloat Gain[2][MAX_OUTPUT_CHANNELS];
ALfloat FeedGain;
ALfilterState Filter;
} ALechoState;
static ALvoid ALechoState_Destruct(ALechoState *state)
{
free(state->SampleBuffer);
state->SampleBuffer = NULL;
}
static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device)
{
ALuint maxlen, i;
// Use the next power of 2 for the buffer length, so the tap offsets can be
// wrapped using a mask instead of a modulo
maxlen = fastf2u(AL_ECHO_MAX_DELAY * Device->Frequency) + 1;
maxlen += fastf2u(AL_ECHO_MAX_LRDELAY * Device->Frequency) + 1;
maxlen = NextPowerOf2(maxlen);
if(maxlen != state->BufferLength)
{
void *temp;
temp = realloc(state->SampleBuffer, maxlen * sizeof(ALfloat));
if(!temp) return AL_FALSE;
state->SampleBuffer = temp;
state->BufferLength = maxlen;
}
for(i = 0;i < state->BufferLength;i++)
state->SampleBuffer[i] = 0.0f;
return AL_TRUE;
}
static ALvoid ALechoState_update(ALechoState *state, ALCdevice *Device, const ALeffectslot *Slot)
{
ALuint frequency = Device->Frequency;
ALfloat lrpan, gain;
ALfloat dirGain;
state->Tap[0].delay = fastf2u(Slot->EffectProps.Echo.Delay * frequency) + 1;
state->Tap[1].delay = fastf2u(Slot->EffectProps.Echo.LRDelay * frequency);
state->Tap[1].delay += state->Tap[0].delay;
lrpan = Slot->EffectProps.Echo.Spread;
state->FeedGain = Slot->EffectProps.Echo.Feedback;
ALfilterState_setParams(&state->Filter, ALfilterType_HighShelf,
1.0f - Slot->EffectProps.Echo.Damping,
LOWPASSFREQREF/frequency, 0.0f);
gain = Slot->Gain;
dirGain = fabsf(lrpan);
/* First tap panning */
ComputeAngleGains(Device, atan2f(-lrpan, 0.0f), (1.0f-dirGain)*F_PI, gain, state->Gain[0]);
/* Second tap panning */
ComputeAngleGains(Device, atan2f(+lrpan, 0.0f), (1.0f-dirGain)*F_PI, gain, state->Gain[1]);
}
static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
{
const ALuint mask = state->BufferLength-1;
const ALuint tap1 = state->Tap[0].delay;
const ALuint tap2 = state->Tap[1].delay;
ALuint offset = state->Offset;
ALfloat smp;
ALuint base;
ALuint i, k;
for(base = 0;base < SamplesToDo;)
{
ALfloat temps[64][2];
ALuint td = minu(SamplesToDo-base, 64);
for(i = 0;i < td;i++)
{
/* First tap */
temps[i][0] = state->SampleBuffer[(offset-tap1) & mask];
/* Second tap */
temps[i][1] = state->SampleBuffer[(offset-tap2) & mask];
// Apply damping and feedback gain to the second tap, and mix in the
// new sample
smp = ALfilterState_processSingle(&state->Filter, temps[i][1]+SamplesIn[i+base]);
state->SampleBuffer[offset&mask] = smp * state->FeedGain;
offset++;
}
for(k = 0;k < MaxChannels;k++)
{
ALfloat gain = state->Gain[0][k];
if(gain > GAIN_SILENCE_THRESHOLD)
{
for(i = 0;i < td;i++)
SamplesOut[k][i+base] += temps[i][0] * gain;
}
gain = state->Gain[1][k];
if(gain > GAIN_SILENCE_THRESHOLD)
{
for(i = 0;i < td;i++)
SamplesOut[k][i+base] += temps[i][1] * gain;
}
}
base += td;
}
state->Offset = offset;
}
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);
typedef struct ALechoStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALechoStateFactory;
ALeffectState *ALechoStateFactory_create(ALechoStateFactory *UNUSED(factory))
static void ALechoState_Construct(ALechoState *state)
{
ALechoState *state;
state = ALechoState_New(sizeof(*state));
if(!state) return NULL;
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
SET_VTABLE2(ALechoState, ALeffectState, state);
state->BufferLength = 0;
@@ -186,6 +72,153 @@ ALeffectState *ALechoStateFactory_create(ALechoStateFactory *UNUSED(factory))
state->Offset = 0;
ALfilterState_clear(&state->Filter);
}
static ALvoid ALechoState_Destruct(ALechoState *state)
{
al_free(state->SampleBuffer);
state->SampleBuffer = NULL;
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device)
{
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 = 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 = al_calloc(16, maxlen * sizeof(ALfloat));
if(!temp) return AL_FALSE;
al_free(state->SampleBuffer);
state->SampleBuffer = temp;
state->BufferLength = maxlen;
}
for(i = 0;i < state->BufferLength;i++)
state->SampleBuffer[i] = 0.0f;
return AL_TRUE;
}
static ALvoid ALechoState_update(ALechoState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
{
ALuint frequency = Device->Frequency;
ALfloat coeffs[MAX_AMBI_COEFFS];
ALfloat gain, lrpan, spread;
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;
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 = props->Echo.Feedback;
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, 1.0f));
gain = Slot->Params.Gain;
/* First tap panning */
CalcAngleCoeffs(-F_PI_2*lrpan, 0.0f, spread, coeffs);
ComputePanningGains(Device->Dry, coeffs, gain, state->Gain[0]);
/* Second tap panning */
CalcAngleCoeffs( F_PI_2*lrpan, 0.0f, spread, coeffs);
ComputePanningGains(Device->Dry, coeffs, gain, state->Gain[1]);
}
static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
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];
ALsizei td = mini(128, SamplesToDo-base);
for(i = 0;i < td;i++)
{
/* First tap */
temps[i][0] = state->SampleBuffer[(offset-tap1) & mask];
/* Second tap */
temps[i][1] = state->SampleBuffer[(offset-tap2) & mask];
// Apply damping and feedback gain to the second tap, and mix in the
// new sample
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++;
}
for(k = 0;k < NumChannels;k++)
{
ALfloat gain = state->Gain[0][k];
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
{
for(i = 0;i < td;i++)
SamplesOut[k][i+base] += temps[i][0] * gain;
}
gain = state->Gain[1][k];
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
{
for(i = 0;i < td;i++)
SamplesOut[k][i+base] += temps[i][1] * gain;
}
}
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;
}
typedef struct ALechoStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALechoStateFactory;
ALeffectState *ALechoStateFactory_create(ALechoStateFactory *UNUSED(factory))
{
ALechoState *state;
NEW_OBJ0(state, ALechoState)();
if(!state) return NULL;
return STATIC_CAST(ALeffectState, state);
}
+106 -60
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -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[MaxChannels];
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,75 +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 = sqrtf(1.0f / device->NumChan) * slot->Gain;
ALfloat gain, freq_mult;
ALuint i;
SetGains(device, 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 */
ALfilterState_setParams(&state->filter[0], ALfilterType_LowShelf,
sqrtf(slot->EffectProps.Equalizer.LowGain),
slot->EffectProps.Equalizer.LowCutoff/frequency,
0.0f);
/* 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 = 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]);
ALfilterState_setParams(&state->filter[1], ALfilterType_Peaking,
sqrtf(slot->EffectProps.Equalizer.Mid1Gain),
slot->EffectProps.Equalizer.Mid1Center/frequency,
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]);
ALfilterState_setParams(&state->filter[2], ALfilterType_Peaking,
sqrtf(slot->EffectProps.Equalizer.Mid2Gain),
slot->EffectProps.Equalizer.Mid2Center/frequency,
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]);
ALfilterState_setParams(&state->filter[3], ALfilterType_HighShelf,
sqrtf(slot->EffectProps.Equalizer.HighGain),
slot->EffectProps.Equalizer.HighCutoff/frequency,
0.0f);
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])
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[64];
ALuint td = minu(SamplesToDo-base, 64);
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 < MaxChannels;kt++)
{
ALfloat gain = state->Gain[kt];
if(!(gain > GAIN_SILENCE_THRESHOLD))
continue;
for(it = 0;it < td;it++)
SamplesOut[kt][base+it] += gain * temps[it];
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);
@@ -167,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);
}
+122 -110
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -39,14 +39,14 @@ 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;
/* Gains for left and right sides */
ALfloat Gain[2][MaxChannels];
ALfloat Gain[2][MAX_OUTPUT_CHANNELS];
/* effect parameters */
enum FlangerWaveForm waveform;
@@ -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,13 +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)
{
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;
@@ -106,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 */
ComputeAngleGains(Device, atan2f(-1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[0]);
ComputeAngleGains(Device, atan2f(+1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[1]);
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;
@@ -125,127 +153,119 @@ 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:
state->lfo_scale = 4.0f / state->lfo_range;
break;
case FWF_Sinusoid:
state->lfo_scale = F_2PI / state->lfo_range;
state->lfo_scale = F_TAU / state->lfo_range;
break;
}
/* 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])
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;)
{
ALfloat temps[64][2];
ALuint td = minu(SamplesToDo-base, 64);
const ALsizei todo = mini(128, SamplesToDo-base);
ALfloat temps[128][2];
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 < MaxChannels;kt++)
for(i = 0;i < todo;i++)
{
ALfloat gain = state->Gain[0][kt];
if(gain > GAIN_SILENCE_THRESHOLD)
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];
if(gain > GAIN_SILENCE_THRESHOLD)
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);
@@ -255,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);
}
+103 -95
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -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[MaxChannels];
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_2PI/WAVEFORM_FRACONE) - F_PI)*0.5f + 0.5f;
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]) \
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[64]; \
ALuint td = minu(SamplesToDo-base, 64); \
ALuint i, k; \
\
for(i = 0;i < td;i++) \
{ \
ALfloat samp; \
samp = SamplesIn[base+i]; \
samp = ALfilterState_processSingle(&state->Filter, samp); \
\
index += step; \
index &= WAVEFORM_FRACMASK; \
temps[i] = samp * func(index); \
} \
\
for(k = 0;k < MaxChannels;k++) \
{ \
ALfloat gain = state->Gain[k]; \
if(!(gain > GAIN_SILENCE_THRESHOLD)) \
continue; \
\
for(i = 0;i < td;i++) \
SamplesOut[k][base+i] += gain * temps[i]; \
} \
\
base += td; \
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,57 +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 gain, cw, a;
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_2PI * 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.b[0] = a;
state->Filter.b[1] = -a;
state->Filter.b[2] = 0.0f;
state->Filter.a[0] = 1.0f;
state->Filter.a[1] = -a;
state->Filter.a[2] = 0.0f;
gain = sqrtf(1.0f/Device->NumChan) * Slot->Gain;
SetGains(Device, gain, state->Gain);
}
static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
{
switch(state->Waveform)
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
{
case SINUSOID:
ProcessSin(state, SamplesToDo, SamplesIn, SamplesOut);
break;
case SAWTOOTH:
ProcessSaw(state, SamplesToDo, SamplesIn, SamplesOut);
break;
case SQUARE:
ProcessSquare(state, SamplesToDo, SamplesIn, SamplesOut);
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 {
@@ -184,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 -18
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,32 +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), ALfloat (*restrict samplesOut)[BUFFERSIZE])
static ALvoid ALnullState_process(ALnullState* UNUSED(state), ALsizei UNUSED(samplesToDo), const ALfloatBUFFERSIZE*restrict UNUSED(samplesIn), ALfloatBUFFERSIZE*restrict UNUSED(samplesOut), ALsizei UNUSED(NumChannels))
{
/* NOTE: Couldn't use the UNUSED macro on samplesOut due to the way GCC's
* __attribute__ declaration interacts with the parenthesis. */
(void)samplesOut;
}
/* This allocates memory to store the object, before it gets constructed.
* DECLARE_DEFAULT_ALLOCATORS can be used to declate a default method.
* 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);
@@ -77,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);
}
@@ -91,7 +106,6 @@ DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALnullStateFactory);
ALeffectStateFactory *ALnullStateFactory_getFactory(void)
{
static ALnullStateFactory NullFactory = { { GET_VTABLE2(ALnullStateFactory, ALeffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &NullFactory);
}
+1631 -1003
View File
File diff suppressed because it is too large Load Diff
-31
View File
@@ -1,31 +0,0 @@
#ifndef AL_EVTQUEUE_H
#define AL_EVTQUEUE_H
#include "AL/al.h"
#include "alMain.h"
typedef struct MidiEvent {
ALuint64 time;
ALuint event;
union {
ALuint val[2];
struct {
ALvoid *data;
ALsizei size;
} sysex;
} param;
} MidiEvent;
typedef struct EvtQueue {
MidiEvent *events;
ALsizei pos;
ALsizei size;
ALsizei maxsize;
} EvtQueue;
void InitEvtQueue(EvtQueue *queue);
void ResetEvtQueue(EvtQueue *queue);
ALenum InsertEvtQueue(EvtQueue *queue, const MidiEvent *evt);
#endif /* AL_EVTQUEUE_H */
+651 -311
View File
File diff suppressed because it is too large Load Diff
+852 -518
View File
File diff suppressed because it is too large Load Diff
+38 -14
View File
@@ -4,25 +4,49 @@
#include "AL/al.h"
#include "AL/alc.h"
enum DevFmtChannels;
#include "alMain.h"
#include "alstring.h"
#include "atomic.h"
struct Hrtf;
#define HRIR_BITS (7)
#define HRIR_LENGTH (1<<HRIR_BITS)
#define HRIR_MASK (HRIR_LENGTH-1)
#define HRTFDELAY_BITS (20)
#define HRTFDELAY_FRACONE (1<<HRTFDELAY_BITS)
#define HRTFDELAY_MASK (HRTFDELAY_FRACONE-1)
/* The maximum number of virtual speakers used to generate HRTF coefficients
* for decoding B-Format.
*/
#define HRTF_AMBI_MAX_CHANNELS 16
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];
};
const struct Hrtf *GetHrtf(enum DevFmtChannels chans, ALCuint srate);
ALCboolean FindHrtfFormat(enum DevFmtChannels *chans, ALCuint *srate);
void FreeHrtfs(void);
ALuint GetHrtfIrSize(const struct Hrtf *Hrtf);
ALfloat CalcHrtfDelta(ALfloat oldGain, ALfloat newGain, const ALfloat olddir[3], const ALfloat newdir[3]);
void GetLerpedHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat (*coeffs)[2], ALuint *delays);
ALuint GetMovingHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat delta, ALint counter, ALfloat (*coeffs)[2], ALuint *delays, ALfloat (*coeffStep)[2], ALint *delayStep);
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);
void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat spread, ALfloat (*coeffs)[2], ALsizei *delays);
/**
* 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];
}
}
-244
View File
@@ -1,244 +0,0 @@
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "midi/base.h"
#include "alMidi.h"
#include "alMain.h"
#include "alError.h"
#include "alThunk.h"
#include "evtqueue.h"
#include "rwlock.h"
#include "alu.h"
extern inline ALboolean IsValidCtrlInput(int cc);
extern inline size_t Reader_read(Reader *self, void *buf, size_t len);
/* MIDI events */
#define SYSEX_EVENT (0xF0)
void InitEvtQueue(EvtQueue *queue)
{
queue->events = NULL;
queue->maxsize = 0;
queue->size = 0;
queue->pos = 0;
}
void ResetEvtQueue(EvtQueue *queue)
{
ALsizei i;
for(i = 0;i < queue->size;i++)
{
if(queue->events[i].event == SYSEX_EVENT)
{
free(queue->events[i].param.sysex.data);
queue->events[i].param.sysex.data = NULL;
}
}
free(queue->events);
queue->events = NULL;
queue->maxsize = 0;
queue->size = 0;
queue->pos = 0;
}
ALenum InsertEvtQueue(EvtQueue *queue, const MidiEvent *evt)
{
ALsizei pos;
if(queue->maxsize == queue->size)
{
if(queue->pos > 0)
{
/* Queue has some stale entries, remove them to make space for more
* events. */
for(pos = 0;pos < queue->pos;pos++)
{
if(queue->events[pos].event == SYSEX_EVENT)
{
free(queue->events[pos].param.sysex.data);
queue->events[pos].param.sysex.data = NULL;
}
}
memmove(&queue->events[0], &queue->events[queue->pos],
(queue->size-queue->pos)*sizeof(queue->events[0]));
queue->size -= queue->pos;
queue->pos = 0;
}
else
{
/* Queue is full, double the allocated space. */
void *temp = NULL;
ALsizei newsize;
newsize = (queue->maxsize ? (queue->maxsize<<1) : 16);
if(newsize > queue->maxsize)
temp = realloc(queue->events, newsize * sizeof(queue->events[0]));
if(!temp)
return AL_OUT_OF_MEMORY;
queue->events = temp;
queue->maxsize = newsize;
}
}
pos = queue->pos;
if(queue->size > 0)
{
ALsizei high = queue->size - 1;
while(pos < high)
{
ALsizei mid = pos + (high-pos)/2;
if(queue->events[mid].time < evt->time)
pos = mid + 1;
else
high = mid;
}
while(pos < queue->size && queue->events[pos].time <= evt->time)
pos++;
if(pos < queue->size)
memmove(&queue->events[pos+1], &queue->events[pos],
(queue->size-pos)*sizeof(queue->events[0]));
}
queue->events[pos] = *evt;
queue->size++;
return AL_NO_ERROR;
}
void MidiSynth_Construct(MidiSynth *self, ALCdevice *device)
{
InitEvtQueue(&self->EventQueue);
RWLockInit(&self->Lock);
self->Soundfonts = NULL;
self->NumSoundfonts = 0;
self->Gain = 1.0f;
self->State = AL_INITIAL;
self->ClockBase = 0;
self->SamplesDone = 0;
self->SampleRate = device->Frequency;
}
void MidiSynth_Destruct(MidiSynth *self)
{
ALsizei i;
for(i = 0;i < self->NumSoundfonts;i++)
DecrementRef(&self->Soundfonts[i]->ref);
free(self->Soundfonts);
self->Soundfonts = NULL;
self->NumSoundfonts = 0;
ResetEvtQueue(&self->EventQueue);
}
ALenum MidiSynth_selectSoundfonts(MidiSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids)
{
ALCdevice *device = context->Device;
ALsoundfont **sfonts;
ALsizei i;
if(self->State != AL_INITIAL && self->State != AL_STOPPED)
return AL_INVALID_OPERATION;
sfonts = calloc(1, count * sizeof(sfonts[0]));
if(!sfonts) return AL_OUT_OF_MEMORY;
for(i = 0;i < count;i++)
{
if(ids[i] == 0)
sfonts[i] = ALsoundfont_getDefSoundfont(context);
else if(!(sfonts[i]=LookupSfont(device, ids[i])))
{
free(sfonts);
return AL_INVALID_VALUE;
}
}
for(i = 0;i < count;i++)
IncrementRef(&sfonts[i]->ref);
sfonts = ExchangePtr((XchgPtr*)&self->Soundfonts, sfonts);
count = ExchangeInt(&self->NumSoundfonts, count);
for(i = 0;i < count;i++)
DecrementRef(&sfonts[i]->ref);
free(sfonts);
return AL_NO_ERROR;
}
extern inline void MidiSynth_setGain(MidiSynth *self, ALfloat gain);
extern inline ALfloat MidiSynth_getGain(const MidiSynth *self);
extern inline void MidiSynth_setState(MidiSynth *self, ALenum state);
extern inline ALenum MidiSynth_getState(const MidiSynth *self);
void MidiSynth_stop(MidiSynth *self)
{
ResetEvtQueue(&self->EventQueue);
self->ClockBase = 0;
self->SamplesDone = 0;
}
extern inline void MidiSynth_reset(MidiSynth *self);
extern inline ALuint64 MidiSynth_getTime(const MidiSynth *self);
extern inline ALuint64 MidiSynth_getNextEvtTime(const MidiSynth *self);
void MidiSynth_setSampleRate(MidiSynth *self, ALuint srate)
{
if(self->SampleRate != srate)
{
self->ClockBase += self->SamplesDone * MIDI_CLOCK_RES / self->SampleRate;
self->SamplesDone = 0;
self->SampleRate = srate;
}
}
extern inline void MidiSynth_update(MidiSynth *self, ALCdevice *device);
ALenum MidiSynth_insertEvent(MidiSynth *self, ALuint64 time, ALuint event, ALsizei param1, ALsizei param2)
{
MidiEvent entry;
entry.time = time;
entry.event = event;
entry.param.val[0] = param1;
entry.param.val[1] = param2;
return InsertEvtQueue(&self->EventQueue, &entry);
}
ALenum MidiSynth_insertSysExEvent(MidiSynth *self, ALuint64 time, const ALbyte *data, ALsizei size)
{
MidiEvent entry;
ALenum err;
entry.time = time;
entry.event = SYSEX_EVENT;
entry.param.sysex.size = size;
entry.param.sysex.data = malloc(size);
if(!entry.param.sysex.data)
return AL_OUT_OF_MEMORY;
memcpy(entry.param.sysex.data, data, size);
err = InsertEvtQueue(&self->EventQueue, &entry);
if(err != AL_NO_ERROR)
free(entry.param.sysex.data);
return err;
}
-133
View File
@@ -1,133 +0,0 @@
#ifndef AL_MIDI_BASE_H
#define AL_MIDI_BASE_H
#include "alMain.h"
#include "atomic.h"
#include "evtqueue.h"
#ifdef __cplusplus
extern "C" {
#endif
struct ALsoundfont;
typedef size_t (*ReaderCb)(void *ptr, size_t size, void *stream);
typedef struct Reader {
ReaderCb cb;
void *ptr;
int error;
} Reader;
inline size_t Reader_read(Reader *self, void *buf, size_t len)
{
size_t got = (!self->error) ? self->cb(buf, len, self->ptr) : 0;
if(got < len) self->error = 1;
return got;
}
#define READERR(x_) ((x_)->error)
ALboolean loadSf2(Reader *stream, struct ALsoundfont *sfont, ALCcontext *context);
#define MIDI_CLOCK_RES U64(1000000000)
struct MidiSynthVtable;
typedef struct MidiSynth {
EvtQueue EventQueue;
ALuint64 ClockBase;
ALuint SamplesDone;
ALuint SampleRate;
/* NOTE: This rwlock is for the state and soundfont. The EventQueue and
* related must instead use the device lock as they're used in the mixer
* thread.
*/
RWLock Lock;
struct ALsoundfont **Soundfonts;
ALsizei NumSoundfonts;
volatile ALfloat Gain;
volatile ALenum State;
const struct MidiSynthVtable *vtbl;
} MidiSynth;
void MidiSynth_Construct(MidiSynth *self, ALCdevice *device);
void MidiSynth_Destruct(MidiSynth *self);
ALenum MidiSynth_selectSoundfonts(MidiSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids);
inline void MidiSynth_setGain(MidiSynth *self, ALfloat gain) { self->Gain = gain; }
inline ALfloat MidiSynth_getGain(const MidiSynth *self) { return self->Gain; }
inline void MidiSynth_setState(MidiSynth *self, ALenum state) { ExchangeInt(&self->State, state); }
inline ALenum MidiSynth_getState(const MidiSynth *self) { return self->State; }
void MidiSynth_stop(MidiSynth *self);
inline void MidiSynth_reset(MidiSynth *self) { MidiSynth_stop(self); }
inline ALuint64 MidiSynth_getTime(const MidiSynth *self)
{ return self->ClockBase + (self->SamplesDone*MIDI_CLOCK_RES/self->SampleRate); }
inline ALuint64 MidiSynth_getNextEvtTime(const MidiSynth *self)
{
if(self->EventQueue.pos == self->EventQueue.size)
return UINT64_MAX;
return self->EventQueue.events[self->EventQueue.pos].time;
}
void MidiSynth_setSampleRate(MidiSynth *self, ALuint srate);
inline void MidiSynth_update(MidiSynth *self, ALCdevice *device)
{ MidiSynth_setSampleRate(self, device->Frequency); }
ALenum MidiSynth_insertEvent(MidiSynth *self, ALuint64 time, ALuint event, ALsizei param1, ALsizei param2);
ALenum MidiSynth_insertSysExEvent(MidiSynth *self, ALuint64 time, const ALbyte *data, ALsizei size);
struct MidiSynthVtable {
void (*const Destruct)(MidiSynth *self);
ALenum (*const selectSoundfonts)(MidiSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids);
void (*const setGain)(MidiSynth *self, ALfloat gain);
void (*const stop)(MidiSynth *self);
void (*const reset)(MidiSynth *self);
void (*const update)(MidiSynth *self, ALCdevice *device);
void (*const process)(MidiSynth *self, ALuint samples, ALfloat (*restrict DryBuffer)[BUFFERSIZE]);
void (*const Delete)(void *ptr);
};
#define DEFINE_MIDISYNTH_VTABLE(T) \
DECLARE_THUNK(T, MidiSynth, void, Destruct) \
DECLARE_THUNK3(T, MidiSynth, ALenum, selectSoundfonts, ALCcontext*, ALsizei, const ALuint*) \
DECLARE_THUNK1(T, MidiSynth, void, setGain, ALfloat) \
DECLARE_THUNK(T, MidiSynth, void, stop) \
DECLARE_THUNK(T, MidiSynth, void, reset) \
DECLARE_THUNK1(T, MidiSynth, void, update, ALCdevice*) \
DECLARE_THUNK2(T, MidiSynth, void, process, ALuint, ALfloatBUFFERSIZE*restrict) \
static void T##_MidiSynth_Delete(void *ptr) \
{ T##_Delete(STATIC_UPCAST(T, MidiSynth, (MidiSynth*)ptr)); } \
\
static const struct MidiSynthVtable T##_MidiSynth_vtable = { \
T##_MidiSynth_Destruct, \
\
T##_MidiSynth_selectSoundfonts, \
T##_MidiSynth_setGain, \
T##_MidiSynth_stop, \
T##_MidiSynth_reset, \
T##_MidiSynth_update, \
T##_MidiSynth_process, \
\
T##_MidiSynth_Delete, \
}
MidiSynth *SSynth_create(ALCdevice *device);
MidiSynth *FSynth_create(ALCdevice *device);
MidiSynth *DSynth_create(ALCdevice *device);
MidiSynth *SynthCreate(ALCdevice *device);
#ifdef __cplusplus
}
#endif
#endif /* AL_MIDI_BASE_H */
-76
View File
@@ -1,76 +0,0 @@
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "alMain.h"
#include "alError.h"
#include "evtqueue.h"
#include "rwlock.h"
#include "alu.h"
#include "midi/base.h"
typedef struct DSynth {
DERIVE_FROM_TYPE(MidiSynth);
} DSynth;
static void DSynth_Construct(DSynth *self, ALCdevice *device);
static DECLARE_FORWARD(DSynth, MidiSynth, void, Destruct)
static DECLARE_FORWARD3(DSynth, MidiSynth, ALenum, selectSoundfonts, ALCcontext*, ALsizei, const ALuint*)
static DECLARE_FORWARD1(DSynth, MidiSynth, void, setGain, ALfloat)
static DECLARE_FORWARD(DSynth, MidiSynth, void, stop)
static DECLARE_FORWARD(DSynth, MidiSynth, void, reset)
static DECLARE_FORWARD1(DSynth, MidiSynth, void, update, ALCdevice*)
static void DSynth_process(DSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]);
DECLARE_DEFAULT_ALLOCATORS(DSynth)
DEFINE_MIDISYNTH_VTABLE(DSynth);
static void DSynth_Construct(DSynth *self, ALCdevice *device)
{
MidiSynth_Construct(STATIC_CAST(MidiSynth, self), device);
SET_VTABLE2(DSynth, MidiSynth, self);
}
static void DSynth_processQueue(DSynth *self, ALuint64 time)
{
EvtQueue *queue = &STATIC_CAST(MidiSynth, self)->EventQueue;
while(queue->pos < queue->size && queue->events[queue->pos].time <= time)
queue->pos++;
}
static void DSynth_process(DSynth *self, ALuint SamplesToDo, ALfloatBUFFERSIZE*restrict UNUSED(DryBuffer))
{
MidiSynth *synth = STATIC_CAST(MidiSynth, self);
ALuint64 curtime;
if(synth->State != AL_PLAYING)
return;
synth->SamplesDone += SamplesToDo;
synth->ClockBase += (synth->SamplesDone/synth->SampleRate) * MIDI_CLOCK_RES;
synth->SamplesDone %= synth->SampleRate;
curtime = MidiSynth_getTime(synth);
DSynth_processQueue(self, maxi64(curtime-1, 0));
}
MidiSynth *DSynth_create(ALCdevice *device)
{
DSynth *synth = DSynth_New(sizeof(*synth));
if(!synth)
{
ERR("Failed to allocate DSynth\n");
return NULL;
}
memset(synth, 0, sizeof(*synth));
DSynth_Construct(synth, device);
return STATIC_CAST(MidiSynth, synth);
}
-930
View File
@@ -1,930 +0,0 @@
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "midi/base.h"
#include "alMain.h"
#include "alError.h"
#include "alMidi.h"
#include "alu.h"
#include "compat.h"
#include "evtqueue.h"
#include "rwlock.h"
#ifdef HAVE_FLUIDSYNTH
#include <fluidsynth.h>
#ifdef HAVE_DYNLOAD
#define FLUID_FUNCS(MAGIC) \
MAGIC(new_fluid_synth); \
MAGIC(delete_fluid_synth); \
MAGIC(new_fluid_settings); \
MAGIC(delete_fluid_settings); \
MAGIC(fluid_settings_setint); \
MAGIC(fluid_settings_setnum); \
MAGIC(fluid_synth_noteon); \
MAGIC(fluid_synth_noteoff); \
MAGIC(fluid_synth_program_change); \
MAGIC(fluid_synth_pitch_bend); \
MAGIC(fluid_synth_channel_pressure); \
MAGIC(fluid_synth_cc); \
MAGIC(fluid_synth_sysex); \
MAGIC(fluid_synth_bank_select); \
MAGIC(fluid_synth_set_channel_type); \
MAGIC(fluid_synth_all_sounds_off); \
MAGIC(fluid_synth_system_reset); \
MAGIC(fluid_synth_set_gain); \
MAGIC(fluid_synth_set_sample_rate); \
MAGIC(fluid_synth_write_float); \
MAGIC(fluid_synth_add_sfloader); \
MAGIC(fluid_synth_sfload); \
MAGIC(fluid_synth_sfunload); \
MAGIC(fluid_synth_alloc_voice); \
MAGIC(fluid_synth_start_voice); \
MAGIC(fluid_voice_gen_set); \
MAGIC(fluid_voice_add_mod); \
MAGIC(fluid_mod_set_source1); \
MAGIC(fluid_mod_set_source2); \
MAGIC(fluid_mod_set_amount); \
MAGIC(fluid_mod_set_dest);
void *fsynth_handle = NULL;
#define DECL_FUNC(x) __typeof(x) *p##x
FLUID_FUNCS(DECL_FUNC)
#undef DECL_FUNC
#define new_fluid_synth pnew_fluid_synth
#define delete_fluid_synth pdelete_fluid_synth
#define new_fluid_settings pnew_fluid_settings
#define delete_fluid_settings pdelete_fluid_settings
#define fluid_settings_setint pfluid_settings_setint
#define fluid_settings_setnum pfluid_settings_setnum
#define fluid_synth_noteon pfluid_synth_noteon
#define fluid_synth_noteoff pfluid_synth_noteoff
#define fluid_synth_program_change pfluid_synth_program_change
#define fluid_synth_pitch_bend pfluid_synth_pitch_bend
#define fluid_synth_channel_pressure pfluid_synth_channel_pressure
#define fluid_synth_cc pfluid_synth_cc
#define fluid_synth_sysex pfluid_synth_sysex
#define fluid_synth_bank_select pfluid_synth_bank_select
#define fluid_synth_set_channel_type pfluid_synth_set_channel_type
#define fluid_synth_all_sounds_off pfluid_synth_all_sounds_off
#define fluid_synth_system_reset pfluid_synth_system_reset
#define fluid_synth_set_gain pfluid_synth_set_gain
#define fluid_synth_set_sample_rate pfluid_synth_set_sample_rate
#define fluid_synth_write_float pfluid_synth_write_float
#define fluid_synth_add_sfloader pfluid_synth_add_sfloader
#define fluid_synth_sfload pfluid_synth_sfload
#define fluid_synth_sfunload pfluid_synth_sfunload
#define fluid_synth_alloc_voice pfluid_synth_alloc_voice
#define fluid_synth_start_voice pfluid_synth_start_voice
#define fluid_voice_gen_set pfluid_voice_gen_set
#define fluid_voice_add_mod pfluid_voice_add_mod
#define fluid_mod_set_source1 pfluid_mod_set_source1
#define fluid_mod_set_source2 pfluid_mod_set_source2
#define fluid_mod_set_amount pfluid_mod_set_amount
#define fluid_mod_set_dest pfluid_mod_set_dest
static ALboolean LoadFSynth(void)
{
ALboolean ret = AL_TRUE;
if(!fsynth_handle)
{
fsynth_handle = LoadLib("libfluidsynth.so.1");
if(!fsynth_handle) return AL_FALSE;
#define LOAD_FUNC(x) do { \
p##x = GetSymbol(fsynth_handle, #x); \
if(!p##x) ret = AL_FALSE; \
} while(0)
FLUID_FUNCS(LOAD_FUNC)
#undef LOAD_FUNC
if(ret == AL_FALSE)
{
CloseLib(fsynth_handle);
fsynth_handle = NULL;
}
}
return ret;
}
#else
static inline ALboolean LoadFSynth(void) { return AL_TRUE; }
#endif
/* MIDI events */
#define SYSEX_EVENT (0xF0)
/* MIDI controllers */
#define CTRL_BANKSELECT_MSB (0)
#define CTRL_BANKSELECT_LSB (32)
#define CTRL_ALLNOTESOFF (123)
static int getModInput(ALenum input)
{
switch(input)
{
case AL_ONE_SOFT: return FLUID_MOD_NONE;
case AL_NOTEON_VELOCITY_SOFT: return FLUID_MOD_VELOCITY;
case AL_NOTEON_KEY_SOFT: return FLUID_MOD_KEY;
case AL_KEYPRESSURE_SOFT: return FLUID_MOD_KEYPRESSURE;
case AL_CHANNELPRESSURE_SOFT: return FLUID_MOD_CHANNELPRESSURE;
case AL_PITCHBEND_SOFT: return FLUID_MOD_PITCHWHEEL;
case AL_PITCHBEND_SENSITIVITY_SOFT: return FLUID_MOD_PITCHWHEELSENS;
}
return input&0x7F;
}
static int getModFlags(ALenum input, ALenum type, ALenum form)
{
int ret = 0;
switch(type)
{
case AL_UNORM_SOFT: ret |= FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE; break;
case AL_UNORM_REV_SOFT: ret |= FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE; break;
case AL_SNORM_SOFT: ret |= FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE; break;
case AL_SNORM_REV_SOFT: ret |= FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE; break;
}
switch(form)
{
case AL_LINEAR_SOFT: ret |= FLUID_MOD_LINEAR; break;
case AL_CONCAVE_SOFT: ret |= FLUID_MOD_CONCAVE; break;
case AL_CONVEX_SOFT: ret |= FLUID_MOD_CONVEX; break;
case AL_SWITCH_SOFT: ret |= FLUID_MOD_SWITCH; break;
}
/* Source input values less than 128 correspond to a MIDI continuous
* controller. Otherwise, it's a general controller. */
if(input < 128) ret |= FLUID_MOD_CC;
else ret |= FLUID_MOD_GC;
return ret;
}
static enum fluid_gen_type getModDest(ALenum gen)
{
switch(gen)
{
case AL_MOD_LFO_TO_PITCH_SOFT: return GEN_MODLFOTOPITCH;
case AL_VIBRATO_LFO_TO_PITCH_SOFT: return GEN_VIBLFOTOPITCH;
case AL_MOD_ENV_TO_PITCH_SOFT: return GEN_MODENVTOPITCH;
case AL_FILTER_CUTOFF_SOFT: return GEN_FILTERFC;
case AL_FILTER_RESONANCE_SOFT: return GEN_FILTERQ;
case AL_MOD_LFO_TO_FILTER_CUTOFF_SOFT: return GEN_MODLFOTOFILTERFC;
case AL_MOD_ENV_TO_FILTER_CUTOFF_SOFT: return GEN_MODENVTOFILTERFC;
case AL_MOD_LFO_TO_VOLUME_SOFT: return GEN_MODLFOTOVOL;
case AL_CHORUS_SEND_SOFT: return GEN_CHORUSSEND;
case AL_REVERB_SEND_SOFT: return GEN_REVERBSEND;
case AL_PAN_SOFT: return GEN_PAN;
case AL_MOD_LFO_DELAY_SOFT: return GEN_MODLFODELAY;
case AL_MOD_LFO_FREQUENCY_SOFT: return GEN_MODLFOFREQ;
case AL_VIBRATO_LFO_DELAY_SOFT: return GEN_VIBLFODELAY;
case AL_VIBRATO_LFO_FREQUENCY_SOFT: return GEN_VIBLFOFREQ;
case AL_MOD_ENV_DELAYTIME_SOFT: return GEN_MODENVDELAY;
case AL_MOD_ENV_ATTACKTIME_SOFT: return GEN_MODENVATTACK;
case AL_MOD_ENV_HOLDTIME_SOFT: return GEN_MODENVHOLD;
case AL_MOD_ENV_DECAYTIME_SOFT: return GEN_MODENVDECAY;
case AL_MOD_ENV_SUSTAINVOLUME_SOFT: return GEN_MODENVSUSTAIN;
case AL_MOD_ENV_RELEASETIME_SOFT: return GEN_MODENVRELEASE;
case AL_MOD_ENV_KEY_TO_HOLDTIME_SOFT: return GEN_KEYTOMODENVHOLD;
case AL_MOD_ENV_KEY_TO_DECAYTIME_SOFT: return GEN_KEYTOMODENVDECAY;
case AL_VOLUME_ENV_DELAYTIME_SOFT: return GEN_VOLENVDELAY;
case AL_VOLUME_ENV_ATTACKTIME_SOFT: return GEN_VOLENVATTACK;
case AL_VOLUME_ENV_HOLDTIME_SOFT: return GEN_VOLENVHOLD;
case AL_VOLUME_ENV_DECAYTIME_SOFT: return GEN_VOLENVDECAY;
case AL_VOLUME_ENV_SUSTAINVOLUME_SOFT: return GEN_VOLENVSUSTAIN;
case AL_VOLUME_ENV_RELEASETIME_SOFT: return GEN_VOLENVRELEASE;
case AL_VOLUME_ENV_KEY_TO_HOLDTIME_SOFT: return GEN_KEYTOVOLENVHOLD;
case AL_VOLUME_ENV_KEY_TO_DECAYTIME_SOFT: return GEN_KEYTOVOLENVDECAY;
case AL_ATTENUATION_SOFT: return GEN_ATTENUATION;
case AL_TUNING_COARSE_SOFT: return GEN_COARSETUNE;
case AL_TUNING_FINE_SOFT: return GEN_FINETUNE;
case AL_TUNING_SCALE_SOFT: return GEN_SCALETUNE;
}
ERR("Unhandled generator: 0x%04x\n", gen);
return 0;
}
static int getSf2LoopMode(ALenum mode)
{
switch(mode)
{
case AL_NONE: return 0;
case AL_LOOP_CONTINUOUS_SOFT: return 1;
case AL_LOOP_UNTIL_RELEASE_SOFT: return 3;
}
return 0;
}
static int getSampleType(ALenum type)
{
switch(type)
{
case AL_MONO_SOFT: return FLUID_SAMPLETYPE_MONO;
case AL_RIGHT_SOFT: return FLUID_SAMPLETYPE_RIGHT;
case AL_LEFT_SOFT: return FLUID_SAMPLETYPE_LEFT;
}
return FLUID_SAMPLETYPE_MONO;
}
typedef struct FSample {
DERIVE_FROM_TYPE(fluid_sample_t);
ALfontsound *Sound;
fluid_mod_t *Mods;
ALsizei NumMods;
} FSample;
static void FSample_Construct(FSample *self, ALfontsound *sound)
{
fluid_sample_t *sample = STATIC_CAST(fluid_sample_t, self);
memset(sample->name, 0, sizeof(sample->name));
sample->start = sound->Start;
sample->end = sound->End;
sample->loopstart = sound->LoopStart;
sample->loopend = sound->LoopEnd;
sample->samplerate = sound->SampleRate;
sample->origpitch = sound->PitchKey;
sample->pitchadj = sound->PitchCorrection;
sample->sampletype = getSampleType(sound->SampleType);
sample->valid = !!sound->Buffer;
sample->data = sound->Buffer ? sound->Buffer->data : NULL;
sample->amplitude_that_reaches_noise_floor_is_valid = 0;
sample->amplitude_that_reaches_noise_floor = 0.0;
sample->refcount = 0;
sample->notify = NULL;
sample->userdata = self;
self->Sound = sound;
self->NumMods = 0;
self->Mods = calloc(sound->ModulatorMap.size*4, sizeof(fluid_mod_t[4]));
if(self->Mods)
{
ALsizei i, j, k;
for(i = j = 0;i < sound->ModulatorMap.size;i++)
{
ALsfmodulator *mod = sound->ModulatorMap.array[i].value;
for(k = 0;k < 4;k++,mod++)
{
if(mod->Dest == AL_NONE)
continue;
fluid_mod_set_source1(&self->Mods[j], getModInput(mod->Source[0].Input),
getModFlags(mod->Source[0].Input, mod->Source[0].Type,
mod->Source[0].Form));
fluid_mod_set_source2(&self->Mods[j], getModInput(mod->Source[1].Input),
getModFlags(mod->Source[1].Input, mod->Source[1].Type,
mod->Source[1].Form));
fluid_mod_set_amount(&self->Mods[j], mod->Amount);
fluid_mod_set_dest(&self->Mods[j], getModDest(mod->Dest));
self->Mods[j++].next = NULL;
}
}
self->NumMods = j;
}
}
static void FSample_Destruct(FSample *self)
{
free(self->Mods);
self->Mods = NULL;
self->NumMods = 0;
}
typedef struct FPreset {
DERIVE_FROM_TYPE(fluid_preset_t);
char Name[16];
int Preset;
int Bank;
FSample *Samples;
ALsizei NumSamples;
} FPreset;
static char* FPreset_getName(fluid_preset_t *preset);
static int FPreset_getPreset(fluid_preset_t *preset);
static int FPreset_getBank(fluid_preset_t *preset);
static int FPreset_noteOn(fluid_preset_t *preset, fluid_synth_t *synth, int channel, int key, int velocity);
static void FPreset_Construct(FPreset *self, ALsfpreset *preset, fluid_sfont_t *parent)
{
STATIC_CAST(fluid_preset_t, self)->data = self;
STATIC_CAST(fluid_preset_t, self)->sfont = parent;
STATIC_CAST(fluid_preset_t, self)->free = NULL;
STATIC_CAST(fluid_preset_t, self)->get_name = FPreset_getName;
STATIC_CAST(fluid_preset_t, self)->get_banknum = FPreset_getBank;
STATIC_CAST(fluid_preset_t, self)->get_num = FPreset_getPreset;
STATIC_CAST(fluid_preset_t, self)->noteon = FPreset_noteOn;
STATIC_CAST(fluid_preset_t, self)->notify = NULL;
memset(self->Name, 0, sizeof(self->Name));
self->Preset = preset->Preset;
self->Bank = preset->Bank;
self->NumSamples = 0;
self->Samples = calloc(1, preset->NumSounds * sizeof(self->Samples[0]));
if(self->Samples)
{
ALsizei i;
self->NumSamples = preset->NumSounds;
for(i = 0;i < self->NumSamples;i++)
FSample_Construct(&self->Samples[i], preset->Sounds[i]);
}
}
static void FPreset_Destruct(FPreset *self)
{
ALsizei i;
for(i = 0;i < self->NumSamples;i++)
FSample_Destruct(&self->Samples[i]);
free(self->Samples);
self->Samples = NULL;
self->NumSamples = 0;
}
static ALboolean FPreset_canDelete(FPreset *self)
{
ALsizei i;
for(i = 0;i < self->NumSamples;i++)
{
if(fluid_sample_refcount(STATIC_CAST(fluid_sample_t, &self->Samples[i])) != 0)
return AL_FALSE;
}
return AL_TRUE;
}
static char* FPreset_getName(fluid_preset_t *preset)
{
return ((FPreset*)preset->data)->Name;
}
static int FPreset_getPreset(fluid_preset_t *preset)
{
return ((FPreset*)preset->data)->Preset;
}
static int FPreset_getBank(fluid_preset_t *preset)
{
return ((FPreset*)preset->data)->Bank;
}
static int FPreset_noteOn(fluid_preset_t *preset, fluid_synth_t *synth, int channel, int key, int vel)
{
FPreset *self = ((FPreset*)preset->data);
ALsizei i;
for(i = 0;i < self->NumSamples;i++)
{
FSample *sample = &self->Samples[i];
ALfontsound *sound = sample->Sound;
fluid_voice_t *voice;
ALsizei m;
if(!(key >= sound->MinKey && key <= sound->MaxKey && vel >= sound->MinVelocity && vel <= sound->MaxVelocity))
continue;
voice = fluid_synth_alloc_voice(synth, STATIC_CAST(fluid_sample_t, sample), channel, key, vel);
if(voice == NULL) return FLUID_FAILED;
fluid_voice_gen_set(voice, GEN_MODLFOTOPITCH, sound->ModLfoToPitch);
fluid_voice_gen_set(voice, GEN_VIBLFOTOPITCH, sound->VibratoLfoToPitch);
fluid_voice_gen_set(voice, GEN_MODENVTOPITCH, sound->ModEnvToPitch);
fluid_voice_gen_set(voice, GEN_FILTERFC, sound->FilterCutoff);
fluid_voice_gen_set(voice, GEN_FILTERQ, sound->FilterQ);
fluid_voice_gen_set(voice, GEN_MODLFOTOFILTERFC, sound->ModLfoToFilterCutoff);
fluid_voice_gen_set(voice, GEN_MODENVTOFILTERFC, sound->ModEnvToFilterCutoff);
fluid_voice_gen_set(voice, GEN_MODLFOTOVOL, sound->ModLfoToVolume);
fluid_voice_gen_set(voice, GEN_CHORUSSEND, sound->ChorusSend);
fluid_voice_gen_set(voice, GEN_REVERBSEND, sound->ReverbSend);
fluid_voice_gen_set(voice, GEN_PAN, sound->Pan);
fluid_voice_gen_set(voice, GEN_MODLFODELAY, sound->ModLfo.Delay);
fluid_voice_gen_set(voice, GEN_MODLFOFREQ, sound->ModLfo.Frequency);
fluid_voice_gen_set(voice, GEN_VIBLFODELAY, sound->VibratoLfo.Delay);
fluid_voice_gen_set(voice, GEN_VIBLFOFREQ, sound->VibratoLfo.Frequency);
fluid_voice_gen_set(voice, GEN_MODENVDELAY, sound->ModEnv.DelayTime);
fluid_voice_gen_set(voice, GEN_MODENVATTACK, sound->ModEnv.AttackTime);
fluid_voice_gen_set(voice, GEN_MODENVHOLD, sound->ModEnv.HoldTime);
fluid_voice_gen_set(voice, GEN_MODENVDECAY, sound->ModEnv.DecayTime);
fluid_voice_gen_set(voice, GEN_MODENVSUSTAIN, sound->ModEnv.SustainAttn);
fluid_voice_gen_set(voice, GEN_MODENVRELEASE, sound->ModEnv.ReleaseTime);
fluid_voice_gen_set(voice, GEN_KEYTOMODENVHOLD, sound->ModEnv.KeyToHoldTime);
fluid_voice_gen_set(voice, GEN_KEYTOMODENVDECAY, sound->ModEnv.KeyToDecayTime);
fluid_voice_gen_set(voice, GEN_VOLENVDELAY, sound->VolEnv.DelayTime);
fluid_voice_gen_set(voice, GEN_VOLENVATTACK, sound->VolEnv.AttackTime);
fluid_voice_gen_set(voice, GEN_VOLENVHOLD, sound->VolEnv.HoldTime);
fluid_voice_gen_set(voice, GEN_VOLENVDECAY, sound->VolEnv.DecayTime);
fluid_voice_gen_set(voice, GEN_VOLENVSUSTAIN, sound->VolEnv.SustainAttn);
fluid_voice_gen_set(voice, GEN_VOLENVRELEASE, sound->VolEnv.ReleaseTime);
fluid_voice_gen_set(voice, GEN_KEYTOVOLENVHOLD, sound->VolEnv.KeyToHoldTime);
fluid_voice_gen_set(voice, GEN_KEYTOVOLENVDECAY, sound->VolEnv.KeyToDecayTime);
fluid_voice_gen_set(voice, GEN_ATTENUATION, sound->Attenuation);
fluid_voice_gen_set(voice, GEN_COARSETUNE, sound->CoarseTuning);
fluid_voice_gen_set(voice, GEN_FINETUNE, sound->FineTuning);
fluid_voice_gen_set(voice, GEN_SAMPLEMODE, getSf2LoopMode(sound->LoopMode));
fluid_voice_gen_set(voice, GEN_SCALETUNE, sound->TuningScale);
fluid_voice_gen_set(voice, GEN_EXCLUSIVECLASS, sound->ExclusiveClass);
for(m = 0;m < sample->NumMods;m++)
fluid_voice_add_mod(voice, &sample->Mods[m], FLUID_VOICE_OVERWRITE);
fluid_synth_start_voice(synth, voice);
}
return FLUID_OK;
}
typedef struct FSfont {
DERIVE_FROM_TYPE(fluid_sfont_t);
char Name[16];
FPreset *Presets;
ALsizei NumPresets;
ALsizei CurrentPos;
} FSfont;
static int FSfont_free(fluid_sfont_t *sfont);
static char* FSfont_getName(fluid_sfont_t *sfont);
static fluid_preset_t* FSfont_getPreset(fluid_sfont_t *sfont, unsigned int bank, unsigned int prenum);
static void FSfont_iterStart(fluid_sfont_t *sfont);
static int FSfont_iterNext(fluid_sfont_t *sfont, fluid_preset_t *preset);
static void FSfont_Construct(FSfont *self, ALsoundfont *sfont)
{
STATIC_CAST(fluid_sfont_t, self)->data = self;
STATIC_CAST(fluid_sfont_t, self)->id = FLUID_FAILED;
STATIC_CAST(fluid_sfont_t, self)->free = FSfont_free;
STATIC_CAST(fluid_sfont_t, self)->get_name = FSfont_getName;
STATIC_CAST(fluid_sfont_t, self)->get_preset = FSfont_getPreset;
STATIC_CAST(fluid_sfont_t, self)->iteration_start = FSfont_iterStart;
STATIC_CAST(fluid_sfont_t, self)->iteration_next = FSfont_iterNext;
memset(self->Name, 0, sizeof(self->Name));
self->CurrentPos = 0;
self->NumPresets = 0;
self->Presets = calloc(1, sfont->NumPresets * sizeof(self->Presets[0]));
if(self->Presets)
{
ALsizei i;
self->NumPresets = sfont->NumPresets;
for(i = 0;i < self->NumPresets;i++)
FPreset_Construct(&self->Presets[i], sfont->Presets[i], STATIC_CAST(fluid_sfont_t, self));
}
}
static void FSfont_Destruct(FSfont *self)
{
ALsizei i;
for(i = 0;i < self->NumPresets;i++)
FPreset_Destruct(&self->Presets[i]);
free(self->Presets);
self->Presets = NULL;
self->NumPresets = 0;
self->CurrentPos = 0;
}
static int FSfont_free(fluid_sfont_t *sfont)
{
FSfont *self = STATIC_UPCAST(FSfont, fluid_sfont_t, sfont);
ALsizei i;
for(i = 0;i < self->NumPresets;i++)
{
if(!FPreset_canDelete(&self->Presets[i]))
return 1;
}
FSfont_Destruct(self);
free(self);
return 0;
}
static char* FSfont_getName(fluid_sfont_t *sfont)
{
return STATIC_UPCAST(FSfont, fluid_sfont_t, sfont)->Name;
}
static fluid_preset_t *FSfont_getPreset(fluid_sfont_t *sfont, unsigned int bank, unsigned int prenum)
{
FSfont *self = STATIC_UPCAST(FSfont, fluid_sfont_t, sfont);
ALsizei i;
for(i = 0;i < self->NumPresets;i++)
{
FPreset *preset = &self->Presets[i];
if(preset->Bank == (int)bank && preset->Preset == (int)prenum)
return STATIC_CAST(fluid_preset_t, preset);
}
return NULL;
}
static void FSfont_iterStart(fluid_sfont_t *sfont)
{
STATIC_UPCAST(FSfont, fluid_sfont_t, sfont)->CurrentPos = 0;
}
static int FSfont_iterNext(fluid_sfont_t *sfont, fluid_preset_t *preset)
{
FSfont *self = STATIC_UPCAST(FSfont, fluid_sfont_t, sfont);
if(self->CurrentPos >= self->NumPresets)
return 0;
*preset = *STATIC_CAST(fluid_preset_t, &self->Presets[self->CurrentPos++]);
preset->free = NULL;
return 1;
}
typedef struct FSynth {
DERIVE_FROM_TYPE(MidiSynth);
DERIVE_FROM_TYPE(fluid_sfloader_t);
fluid_settings_t *Settings;
fluid_synth_t *Synth;
int *FontIDs;
ALsizei NumFontIDs;
ALboolean ForceGM2BankSelect;
ALfloat GainScale;
} FSynth;
static void FSynth_Construct(FSynth *self, ALCdevice *device);
static void FSynth_Destruct(FSynth *self);
static ALboolean FSynth_init(FSynth *self, ALCdevice *device);
static ALenum FSynth_selectSoundfonts(FSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids);
static void FSynth_setGain(FSynth *self, ALfloat gain);
static void FSynth_stop(FSynth *self);
static void FSynth_reset(FSynth *self);
static void FSynth_update(FSynth *self, ALCdevice *device);
static void FSynth_processQueue(FSynth *self, ALuint64 time);
static void FSynth_process(FSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]);
DECLARE_DEFAULT_ALLOCATORS(FSynth)
DEFINE_MIDISYNTH_VTABLE(FSynth);
static fluid_sfont_t *FSynth_loadSfont(fluid_sfloader_t *loader, const char *filename);
static void FSynth_Construct(FSynth *self, ALCdevice *device)
{
MidiSynth_Construct(STATIC_CAST(MidiSynth, self), device);
SET_VTABLE2(FSynth, MidiSynth, self);
STATIC_CAST(fluid_sfloader_t, self)->data = self;
STATIC_CAST(fluid_sfloader_t, self)->free = NULL;
STATIC_CAST(fluid_sfloader_t, self)->load = FSynth_loadSfont;
self->Settings = NULL;
self->Synth = NULL;
self->FontIDs = NULL;
self->NumFontIDs = 0;
self->ForceGM2BankSelect = AL_FALSE;
self->GainScale = 0.2f;
}
static void FSynth_Destruct(FSynth *self)
{
ALsizei i;
for(i = 0;i < self->NumFontIDs;i++)
fluid_synth_sfunload(self->Synth, self->FontIDs[i], 0);
free(self->FontIDs);
self->FontIDs = NULL;
self->NumFontIDs = 0;
if(self->Synth != NULL)
delete_fluid_synth(self->Synth);
self->Synth = NULL;
if(self->Settings != NULL)
delete_fluid_settings(self->Settings);
self->Settings = NULL;
MidiSynth_Destruct(STATIC_CAST(MidiSynth, self));
}
static ALboolean FSynth_init(FSynth *self, ALCdevice *device)
{
ALfloat vol;
if(ConfigValueFloat("midi", "volume", &vol))
{
if(!(vol <= 0.0f))
{
ERR("MIDI volume %f clamped to 0\n", vol);
vol = 0.0f;
}
self->GainScale = powf(10.0f, vol / 20.0f);
}
self->Settings = new_fluid_settings();
if(!self->Settings)
{
ERR("Failed to create FluidSettings\n");
return AL_FALSE;
}
fluid_settings_setint(self->Settings, "synth.polyphony", 256);
fluid_settings_setnum(self->Settings, "synth.gain", self->GainScale);
fluid_settings_setnum(self->Settings, "synth.sample-rate", device->Frequency);
self->Synth = new_fluid_synth(self->Settings);
if(!self->Synth)
{
ERR("Failed to create FluidSynth\n");
return AL_FALSE;
}
fluid_synth_add_sfloader(self->Synth, STATIC_CAST(fluid_sfloader_t, self));
return AL_TRUE;
}
static fluid_sfont_t *FSynth_loadSfont(fluid_sfloader_t *loader, const char *filename)
{
FSynth *self = STATIC_UPCAST(FSynth, fluid_sfloader_t, loader);
FSfont *sfont;
int idx;
if(!filename || sscanf(filename, "_al_internal %d", &idx) != 1)
return NULL;
if(idx < 0 || idx >= STATIC_CAST(MidiSynth, self)->NumSoundfonts)
{
ERR("Received invalid soundfont index %d (max: %d)\n", idx, STATIC_CAST(MidiSynth, self)->NumSoundfonts);
return NULL;
}
sfont = calloc(1, sizeof(sfont[0]));
if(!sfont) return NULL;
FSfont_Construct(sfont, STATIC_CAST(MidiSynth, self)->Soundfonts[idx]);
return STATIC_CAST(fluid_sfont_t, sfont);
}
static ALenum FSynth_selectSoundfonts(FSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids)
{
int *fontid;
ALenum ret;
ALsizei i;
ret = MidiSynth_selectSoundfonts(STATIC_CAST(MidiSynth, self), context, count, ids);
if(ret != AL_NO_ERROR) return ret;
ALCdevice_Lock(context->Device);
for(i = 0;i < 16;i++)
fluid_synth_all_sounds_off(self->Synth, i);
ALCdevice_Unlock(context->Device);
fontid = malloc(count * sizeof(fontid[0]));
if(fontid)
{
for(i = 0;i < STATIC_CAST(MidiSynth, self)->NumSoundfonts;i++)
{
char name[16];
snprintf(name, sizeof(name), "_al_internal %d", i);
fontid[i] = fluid_synth_sfload(self->Synth, name, 0);
if(fontid[i] == FLUID_FAILED)
ERR("Failed to load selected soundfont %d\n", i);
}
fontid = ExchangePtr((XchgPtr*)&self->FontIDs, fontid);
count = ExchangeInt(&self->NumFontIDs, count);
}
else
{
ERR("Failed to allocate space for %d font IDs!\n", count);
fontid = ExchangePtr((XchgPtr*)&self->FontIDs, NULL);
count = ExchangeInt(&self->NumFontIDs, 0);
}
for(i = 0;i < count;i++)
fluid_synth_sfunload(self->Synth, fontid[i], 0);
free(fontid);
return ret;
}
static void FSynth_setGain(FSynth *self, ALfloat gain)
{
fluid_settings_setnum(self->Settings, "synth.gain", self->GainScale * gain);
fluid_synth_set_gain(self->Synth, self->GainScale * gain);
MidiSynth_setGain(STATIC_CAST(MidiSynth, self), gain);
}
static void FSynth_stop(FSynth *self)
{
MidiSynth *synth = STATIC_CAST(MidiSynth, self);
ALuint64 curtime;
ALsizei chan;
/* Make sure all pending events are processed. */
curtime = MidiSynth_getTime(synth);
FSynth_processQueue(self, curtime);
/* All notes off */
for(chan = 0;chan < 16;chan++)
fluid_synth_cc(self->Synth, chan, CTRL_ALLNOTESOFF, 0);
MidiSynth_stop(STATIC_CAST(MidiSynth, self));
}
static void FSynth_reset(FSynth *self)
{
/* Reset to power-up status. */
fluid_synth_system_reset(self->Synth);
MidiSynth_reset(STATIC_CAST(MidiSynth, self));
}
static void FSynth_update(FSynth *self, ALCdevice *device)
{
fluid_settings_setnum(self->Settings, "synth.sample-rate", device->Frequency);
fluid_synth_set_sample_rate(self->Synth, device->Frequency);
MidiSynth_update(STATIC_CAST(MidiSynth, self), device);
}
static void FSynth_processQueue(FSynth *self, ALuint64 time)
{
EvtQueue *queue = &STATIC_CAST(MidiSynth, self)->EventQueue;
while(queue->pos < queue->size && queue->events[queue->pos].time <= time)
{
const MidiEvent *evt = &queue->events[queue->pos];
if(evt->event == SYSEX_EVENT)
{
static const ALbyte gm2_on[] = { 0x7E, 0x7F, 0x09, 0x03 };
static const ALbyte gm2_off[] = { 0x7E, 0x7F, 0x09, 0x02 };
int handled = 0;
fluid_synth_sysex(self->Synth, evt->param.sysex.data, evt->param.sysex.size, NULL, NULL, &handled, 0);
if(!handled && evt->param.sysex.size >= (ALsizei)sizeof(gm2_on))
{
if(memcmp(evt->param.sysex.data, gm2_on, sizeof(gm2_on)) == 0)
self->ForceGM2BankSelect = AL_TRUE;
else if(memcmp(evt->param.sysex.data, gm2_off, sizeof(gm2_off)) == 0)
self->ForceGM2BankSelect = AL_FALSE;
}
}
else switch((evt->event&0xF0))
{
case AL_NOTEOFF_SOFT:
fluid_synth_noteoff(self->Synth, (evt->event&0x0F), evt->param.val[0]);
break;
case AL_NOTEON_SOFT:
fluid_synth_noteon(self->Synth, (evt->event&0x0F), evt->param.val[0], evt->param.val[1]);
break;
case AL_KEYPRESSURE_SOFT:
break;
case AL_CONTROLLERCHANGE_SOFT:
if(self->ForceGM2BankSelect)
{
int chan = (evt->event&0x0F);
if(evt->param.val[0] == CTRL_BANKSELECT_MSB)
{
if(evt->param.val[1] == 120 && (chan == 9 || chan == 10))
fluid_synth_set_channel_type(self->Synth, chan, CHANNEL_TYPE_DRUM);
else if(evt->param.val[1] == 121)
fluid_synth_set_channel_type(self->Synth, chan, CHANNEL_TYPE_MELODIC);
break;
}
if(evt->param.val[0] == CTRL_BANKSELECT_LSB)
{
fluid_synth_bank_select(self->Synth, chan, evt->param.val[1]);
break;
}
}
fluid_synth_cc(self->Synth, (evt->event&0x0F), evt->param.val[0], evt->param.val[1]);
break;
case AL_PROGRAMCHANGE_SOFT:
fluid_synth_program_change(self->Synth, (evt->event&0x0F), evt->param.val[0]);
break;
case AL_CHANNELPRESSURE_SOFT:
fluid_synth_channel_pressure(self->Synth, (evt->event&0x0F), evt->param.val[0]);
break;
case AL_PITCHBEND_SOFT:
fluid_synth_pitch_bend(self->Synth, (evt->event&0x0F), (evt->param.val[0]&0x7F) |
((evt->param.val[1]&0x7F)<<7));
break;
}
queue->pos++;
}
}
static void FSynth_process(FSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE])
{
MidiSynth *synth = STATIC_CAST(MidiSynth, self);
ALenum state = synth->State;
ALuint64 curtime;
ALuint total = 0;
if(state == AL_INITIAL)
return;
if(state != AL_PLAYING)
{
fluid_synth_write_float(self->Synth, SamplesToDo, DryBuffer[FrontLeft], 0, 1,
DryBuffer[FrontRight], 0, 1);
return;
}
curtime = MidiSynth_getTime(synth);
while(total < SamplesToDo)
{
ALuint64 time, diff;
ALint tonext;
time = MidiSynth_getNextEvtTime(synth);
diff = maxu64(time, curtime) - curtime;
if(diff >= MIDI_CLOCK_RES || time == UINT64_MAX)
{
/* If there's no pending event, or if it's more than 1 second
* away, do as many samples as we can. */
tonext = INT_MAX;
}
else
{
/* Figure out how many samples until the next event. */
tonext = (ALint)((diff*synth->SampleRate + (MIDI_CLOCK_RES-1)) / MIDI_CLOCK_RES);
tonext -= total;
}
if(tonext > 0)
{
ALuint todo = minu(tonext, SamplesToDo-total);
fluid_synth_write_float(self->Synth, todo, DryBuffer[FrontLeft], total, 1,
DryBuffer[FrontRight], total, 1);
total += todo;
tonext -= todo;
}
if(total < SamplesToDo && tonext <= 0)
FSynth_processQueue(self, time);
}
synth->SamplesDone += SamplesToDo;
synth->ClockBase += (synth->SamplesDone/synth->SampleRate) * MIDI_CLOCK_RES;
synth->SamplesDone %= synth->SampleRate;
}
MidiSynth *FSynth_create(ALCdevice *device)
{
FSynth *synth;
if(!LoadFSynth())
return NULL;
synth = FSynth_New(sizeof(*synth));
if(!synth)
{
ERR("Failed to allocate FSynth\n");
return NULL;
}
memset(synth, 0, sizeof(*synth));
FSynth_Construct(synth, device);
if(FSynth_init(synth, device) == AL_FALSE)
{
DELETE_OBJ(STATIC_CAST(MidiSynth, synth));
return NULL;
}
return STATIC_CAST(MidiSynth, synth);
}
#else
MidiSynth *FSynth_create(ALCdevice* UNUSED(device))
{
return NULL;
}
#endif
-1377
View File
File diff suppressed because it is too large Load Diff
-140
View File
@@ -1,140 +0,0 @@
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "alMain.h"
#include "alError.h"
#include "evtqueue.h"
#include "alu.h"
#include "midi/base.h"
typedef struct SSynth {
DERIVE_FROM_TYPE(MidiSynth);
} SSynth;
static void SSynth_mixSamples(SSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]);
static void SSynth_Construct(SSynth *self, ALCdevice *device);
static void SSynth_Destruct(SSynth *self);
static DECLARE_FORWARD3(SSynth, MidiSynth, ALenum, selectSoundfonts, ALCcontext*, ALsizei, const ALuint*)
static DECLARE_FORWARD1(SSynth, MidiSynth, void, setGain, ALfloat)
static DECLARE_FORWARD(SSynth, MidiSynth, void, stop)
static DECLARE_FORWARD(SSynth, MidiSynth, void, reset)
static void SSynth_update(SSynth *self, ALCdevice *device);
static void SSynth_process(SSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]);
DECLARE_DEFAULT_ALLOCATORS(SSynth)
DEFINE_MIDISYNTH_VTABLE(SSynth);
static void SSynth_Construct(SSynth *self, ALCdevice *device)
{
MidiSynth_Construct(STATIC_CAST(MidiSynth, self), device);
SET_VTABLE2(SSynth, MidiSynth, self);
}
static void SSynth_Destruct(SSynth* UNUSED(self))
{
}
static void SSynth_update(SSynth* UNUSED(self), ALCdevice* UNUSED(device))
{
}
static void SSynth_mixSamples(SSynth* UNUSED(self), ALuint UNUSED(SamplesToDo), ALfloatBUFFERSIZE *restrict UNUSED(DryBuffer))
{
}
static void SSynth_processQueue(SSynth *self, ALuint64 time)
{
EvtQueue *queue = &STATIC_CAST(MidiSynth, self)->EventQueue;
while(queue->pos < queue->size && queue->events[queue->pos].time <= time)
queue->pos++;
}
static void SSynth_process(SSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE])
{
MidiSynth *synth = STATIC_CAST(MidiSynth, self);
ALenum state = synth->State;
ALuint64 curtime;
ALuint total = 0;
if(state == AL_INITIAL)
return;
if(state != AL_PLAYING)
{
SSynth_mixSamples(self, SamplesToDo, DryBuffer);
return;
}
curtime = MidiSynth_getTime(synth);
while(total < SamplesToDo)
{
ALuint64 time, diff;
ALint tonext;
time = MidiSynth_getNextEvtTime(synth);
diff = maxu64(time, curtime) - curtime;
if(diff >= MIDI_CLOCK_RES || time == UINT64_MAX)
{
/* If there's no pending event, or if it's more than 1 second
* away, do as many samples as we can. */
tonext = INT_MAX;
}
else
{
/* Figure out how many samples until the next event. */
tonext = (ALint)((diff*synth->SampleRate + (MIDI_CLOCK_RES-1)) / MIDI_CLOCK_RES);
tonext -= total;
/* For efficiency reasons, try to mix a multiple of 64 samples
* (~1ms @ 44.1khz) before processing the next event. */
tonext = (tonext+63) & ~63;
}
if(tonext > 0)
{
ALuint todo = mini(tonext, SamplesToDo-total);
SSynth_mixSamples(self, todo, DryBuffer);
total += todo;
tonext -= todo;
}
if(total < SamplesToDo && tonext <= 0)
SSynth_processQueue(self, time);
}
synth->SamplesDone += SamplesToDo;
synth->ClockBase += (synth->SamplesDone/synth->SampleRate) * MIDI_CLOCK_RES;
synth->SamplesDone %= synth->SampleRate;
}
MidiSynth *SSynth_create(ALCdevice *device)
{
SSynth *synth;
/* This option is temporary. Once this synth is in a more usable state, a
* more generic selector should be used. */
if(!GetConfigValueBool("midi", "internal-synth", 0))
{
TRACE("Not using internal MIDI synth\n");
return NULL;
}
synth = SSynth_New(sizeof(*synth));
if(!synth)
{
ERR("Failed to allocate SSynth\n");
return NULL;
}
SSynth_Construct(synth, device);
return STATIC_CAST(MidiSynth, synth);
}
+372 -211
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -38,46 +38,86 @@
#include "mixer_defs.h"
extern inline void InitiatePositionArrays(ALuint frac, ALuint increment, ALuint *frac_arr, ALuint *pos_arr, ALuint size);
static_assert((INT_MAX>>FRACTIONBITS)/MAX_PITCH > BUFFERSIZE,
"MAX_PITCH and/or BUFFERSIZE are too large for FRACTIONBITS!");
extern inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALint *restrict pos_arr, ALsizei size);
static inline HrtfMixerFunc SelectHrtfMixer(void)
/* 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!");
enum Resampler ResamplerDefault = LinearResampler;
static MixerFunc MixSamples = Mix_C;
static HrtfMixerFunc MixHrtfSamples = MixHrtf_C;
HrtfMixerBlendFunc MixHrtfBlendSamples = MixHrtfBlend_C;
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, ALuint increment)
RowMixerFunc SelectRowMixer(void)
{
if(increment == FRACTIONONE)
return Resample_copy32_C;
switch(Resampler)
#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;
@@ -87,30 +127,85 @@ static inline ResamplerFunc SelectResampler(enum Resampler Resampler, ALuint inc
return Resample_lerp32_SSE2;
#endif
return Resample_lerp32_C;
case CubicResampler:
return Resample_cubic32_C;
case ResamplerMax:
/* Shouldn't happen */
break;
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;
#endif
#ifdef HAVE_SSE3
if((CPUCapFlags&CPU_CAP_SSE3))
return Resample_fir4_32_SSE3;
#endif
return Resample_fir4_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;
#endif
return Resample_bsinc32_C;
}
return Resample_point32_C;
}
void aluInitMixer(void)
{
const char *str;
if(ConfigValueStr(NULL, NULL, "resampler", &str))
{
if(strcasecmp(str, "point") == 0 || strcasecmp(str, "none") == 0)
ResamplerDefault = PointResampler;
else if(strcasecmp(str, "linear") == 0)
ResamplerDefault = LinearResampler;
else if(strcasecmp(str, "sinc4") == 0)
ResamplerDefault = FIR4Resampler;
else if(strcasecmp(str, "bsinc") == 0)
ResamplerDefault = BSincResampler;
else if(strcasecmp(str, "cubic") == 0 || strcasecmp(str, "sinc8") == 0)
{
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))
ResamplerDefault = n;
else
WARN("Invalid resampler: %s\n", str);
}
}
MixHrtfBlendSamples = SelectHrtfBlendMixer();
MixHrtfSamples = SelectHrtfMixer();
MixSamples = SelectMixer();
}
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 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]); \
}
@@ -121,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)
{
@@ -137,9 +232,9 @@ static void LoadSamples(ALfloat *dst, const ALvoid *src, ALuint srcstep, enum Fm
}
}
static 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;
}
@@ -147,26 +242,30 @@ static 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:
ALfilterState_processPassthru(lpfilter, src, numsamples);
ALfilterState_processPassthru(hpfilter, src, numsamples);
break;
case AF_LowPass:
ALfilterState_process(lpfilter, dst, src, numsamples);
ALfilterState_processPassthru(hpfilter, dst, numsamples);
return dst;
case AF_HighPass:
ALfilterState_processPassthru(lpfilter, src, numsamples);
ALfilterState_process(hpfilter, dst, src, numsamples);
return dst;
case AF_BandPass:
for(i = 0;i < numsamples;)
{
ALfloat temp[64];
ALuint todo = minu(64, numsamples-i);
ALfloat temp[256];
ALsizei todo = mini(256, numsamples-i);
ALfilterState_process(lpfilter, temp, src+i, todo);
ALfilterState_process(hpfilter, dst+i, temp, todo);
@@ -178,62 +277,63 @@ static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter
}
ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo)
ALboolean MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALsizei SamplesToDo)
{
MixerFunc Mix;
HrtfMixerFunc HrtfMix;
ResamplerFunc Resample;
ALsource *Source = src->Source;
ALbufferlistitem *BufferListItem;
ALuint DataPosInt, DataPosFrac;
ALboolean Looping;
ALuint increment;
enum Resampler Resampler;
ALenum State;
ALuint OutPos;
ALuint NumChannels;
ALuint SampleSize;
ALbufferlistitem *BufferLoopItem;
ALsizei NumChannels, SampleSize;
ResamplerFunc Resample;
ALsizei DataPosInt;
ALsizei DataPosFrac;
ALint64 DataSize64;
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;
increment = src->Step;
Resampler = (increment==FRACTIONONE) ? PointResampler : Source->Resampler;
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;
Mix = SelectMixer();
HrtfMix = SelectHrtfMixer();
Resample = SelectResampler(Resampler, increment);
IrSize = (Device->HrtfHandle ? Device->HrtfHandle->irSize : 0);
Resample = ((increment == FRACTIONONE && DataPosFrac == 0) ?
Resample_copy32_C : voice->Resampler);
Counter = (voice->Flags&VOICE_IS_FADING) ? SamplesToDo : 0;
firstpass = true;
OutPos = 0;
do {
const ALuint BufferPrePadding = ResamplerPrePadding[Resampler];
const ALuint BufferPadding = ResamplerPadding[Resampler];
ALuint SrcBufferSize, DstBufferSize;
ALsizei SrcBufferSize, DstBufferSize;
/* Figure out how many buffer samples will be needed */
DataSize64 = SamplesToDo-OutPos;
DataSize64 *= increment;
DataSize64 += DataPosFrac+FRACTIONMASK;
DataSize64 >>= FRACTIONBITS;
DataSize64 += BufferPadding+BufferPrePadding;
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;
DataSize64 -= BufferPadding+BufferPrePadding;
DataSize64 -= MAX_POST_SAMPLES+MAX_PRE_SAMPLES;
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. */
@@ -244,38 +344,32 @@ ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo)
{
const ALfloat *ResampledData;
ALfloat *SrcData = Device->SourceData;
ALuint SrcDataSize = 0;
ALsizei SrcDataSize;
/* Load the previous samples into the source data first. */
memcpy(SrcData, voice->PrevSamples[chan], MAX_PRE_SAMPLES*sizeof(ALfloat));
SrcDataSize = MAX_PRE_SAMPLES;
if(Source->SourceType == AL_STATIC)
{
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;
if(DataPosInt >= BufferPrePadding)
pos = DataPosInt - BufferPrePadding;
else
{
DataSize = BufferPrePadding - DataPosInt;
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
/* Load what's left to play from the source buffer, and
* clear the rest of the temp buffer */
DataSize = minu(SrcBufferSize - SrcDataSize,
ALBuffer->SampleLen - DataPosInt);
SilenceSamples(&SrcData[SrcDataSize], DataSize);
SrcDataSize += DataSize;
pos = 0;
}
/* Copy what's left to play in the source buffer, and clear the
* rest of the temp buffer */
DataSize = minu(SrcBufferSize - SrcDataSize, ALBuffer->SampleLen - pos);
LoadSamples(&SrcData[SrcDataSize], &Data[(pos*NumChannels + chan)*SampleSize],
LoadSamples(&SrcData[SrcDataSize], &Data[DataPosInt * NumChannels*SampleSize],
NumChannels, ALBuffer->FmtType, DataSize);
SrcDataSize += DataSize;
@@ -284,45 +378,23 @@ ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo)
}
else
{
ALuint LoopStart = ALBuffer->LoopStart;
ALuint LoopEnd = ALBuffer->LoopEnd;
ALsizei LoopStart = ALBuffer->LoopStart;
ALsizei LoopEnd = ALBuffer->LoopEnd;
if(DataPosInt >= LoopStart)
{
pos = DataPosInt-LoopStart;
while(pos < BufferPrePadding)
pos += LoopEnd-LoopStart;
pos -= BufferPrePadding;
pos += LoopStart;
}
else if(DataPosInt >= BufferPrePadding)
pos = DataPosInt - BufferPrePadding;
else
{
DataSize = BufferPrePadding - DataPosInt;
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
/* Load what's left of this loop iteration, then load
* repeats of the loop section */
DataSize = minu(SrcBufferSize - SrcDataSize, LoopEnd - DataPosInt);
SilenceSamples(&SrcData[SrcDataSize], DataSize);
SrcDataSize += DataSize;
pos = 0;
}
/* Copy what's left of this loop iteration, then copy repeats
* of the loop section */
DataSize = LoopEnd - pos;
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
LoadSamples(&SrcData[SrcDataSize], &Data[(pos*NumChannels + chan)*SampleSize],
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 + chan)*SampleSize],
LoadSamples(&SrcData[SrcDataSize], &Data[LoopStart * NumChannels*SampleSize],
NumChannels, ALBuffer->FmtType, DataSize);
SrcDataSize += DataSize;
}
@@ -332,45 +404,7 @@ ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo)
{
/* Crawl the buffer queue to fill in the temp buffer */
ALbufferlistitem *tmpiter = BufferListItem;
ALuint pos;
if(DataPosInt >= BufferPrePadding)
pos = DataPosInt - BufferPrePadding;
else
{
pos = BufferPrePadding - DataPosInt;
while(pos > 0)
{
ALbufferlistitem *prev;
if((prev=tmpiter->prev) != NULL)
tmpiter = prev;
else if(Looping)
{
while(tmpiter->next)
tmpiter = tmpiter->next;
}
else
{
ALuint DataSize = minu(SrcBufferSize - SrcDataSize, pos);
SilenceSamples(&SrcData[SrcDataSize], DataSize);
SrcDataSize += DataSize;
pos = 0;
break;
}
if(tmpiter->buffer)
{
if((ALuint)tmpiter->buffer->SampleLen > pos)
{
pos = tmpiter->buffer->SampleLen - pos;
break;
}
pos -= tmpiter->buffer->SampleLen;
}
}
}
ALsizei pos = DataPosInt;
while(tmpiter && SrcBufferSize > SrcDataSize)
{
@@ -378,7 +412,7 @@ ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo)
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)
@@ -395,9 +429,9 @@ ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo)
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);
@@ -406,44 +440,171 @@ ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo)
}
}
/* Store the last source samples used for next time. */
memcpy(voice->PrevSamples[chan],
&SrcData[(increment*DstBufferSize + DataPosFrac)>>FRACTIONBITS],
MAX_PRE_SAMPLES*sizeof(ALfloat)
);
/* Now resample, then filter and mix to the appropriate outputs. */
ResampledData = Resample(
&SrcData[BufferPrePadding], DataPosFrac, increment,
ResampledData = Resample(&voice->ResampleState,
&SrcData[MAX_PRE_SAMPLES], DataPosFrac, increment,
Device->ResampledData, DstBufferSize
);
{
DirectParams *parms = &src->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(!src->IsHrtf)
Mix(samples, MaxChannels, parms->OutBuffer, parms->Mix.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
HrtfMix(parms->OutBuffer, samples, parms->Counter, src->Offset,
OutPos, parms->Mix.Hrtf.IrSize, &parms->Mix.Hrtf.Params[chan],
&parms->Mix.Hrtf.State[chan], 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 = &src->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
);
Mix(samples, 1, parms->OutBuffer, &parms->Gain,
parms->Counter, OutPos, DstBufferSize);
}
}
/* Update positions */
@@ -452,18 +613,17 @@ ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo)
DataPosFrac &= FRACTIONMASK;
OutPos += DstBufferSize;
src->Offset += DstBufferSize;
src->Direct.Counter = maxu(src->Direct.Counter, DstBufferSize) - DstBufferSize;
for(j = 0;j < Device->NumAuxSends;j++)
src->Send[j].Counter = maxu(src->Send[j].Counter, DstBufferSize) - DstBufferSize;
voice->Offset += 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)
{
@@ -474,7 +634,7 @@ ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo)
break;
}
if(Looping && Source->SourceType == AL_STATIC)
if(BufferLoopItem && Source->SourceType == AL_STATIC)
{
assert(LoopEnd > LoopStart);
DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart;
@@ -484,14 +644,13 @@ ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo)
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;
@@ -500,11 +659,13 @@ ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo)
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;
}
+130 -48
View File
@@ -8,17 +8,18 @@
#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 cubic32(const ALfloat *vals, ALuint frac)
{ return cubic(vals[-1], vals[0], vals[1], vals[2], frac * (1.0f/FRACTIONONE)); }
static inline ALfloat fir4_32(const ALfloat *restrict vals, ALsizei frac)
{ return resample_fir4(vals[-1], vals[0], vals[1], vals[2], frac); }
const ALfloat *Resample_copy32_C(const ALfloat *src, ALuint UNUSED(frac),
ALuint 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)
{
assert(increment==FRACTIONONE);
#if defined(HAVE_SSE) || defined(HAVE_NEON)
/* Avoid copying the source data if it's aligned like the destination. */
if((((intptr_t)src)&15) == (((intptr_t)dst)&15))
@@ -29,10 +30,11 @@ const ALfloat *Resample_copy32_C(const ALfloat *src, ALuint UNUSED(frac),
}
#define DECL_TEMPLATE(Sampler) \
const ALfloat *Resample_##Sampler##_C(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); \
@@ -46,81 +48,161 @@ const ALfloat *Resample_##Sampler##_C(const ALfloat *src, ALuint frac, \
DECL_TEMPLATE(point32)
DECL_TEMPLATE(lerp32)
DECL_TEMPLATE(cubic32)
DECL_TEMPLATE(fir4_32)
#undef DECL_TEMPLATE
void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples)
const ALfloat *Resample_bsinc32_C(const InterpState *state, const ALfloat *restrict src,
ALsizei frac, ALint increment, ALfloat *restrict dst,
ALsizei dstlen)
{
ALuint i;
for(i = 0;i < numsamples;i++)
*(dst++) = ALfilterState_processSingle(filter, *(src++));
const ALfloat *fil, *scd, *phd, *spd;
const ALfloat sf = state->bsinc.sf;
const ALsizei m = state->bsinc.m;
ALsizei j_f, pi, i;
ALfloat pf, r;
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.
r = 0.0f;
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;
src += frac>>FRACTIONBITS;
frac &= FRACTIONMASK;
}
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)
void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALsizei numsamples)
{
ALuint c;
for(c = 0;c < IrSize;c++)
ALsizei i;
if(numsamples > 1)
{
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];
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 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 SUFFIX C
#define MixHrtf MixHrtf_C
#define MixHrtfBlend MixHrtfBlend_C
#define MixDirectHrtf MixDirectHrtf_C
#include "mixer_inc.c"
#undef SUFFIX
#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 != 1.0f && Counter > 0)
ALsizei pos = 0;
gain = CurrentGains[c];
step = (TargetGains[c] - gain) * delta;
if(fabsf(step) > FLT_EPSILON)
{
for(;pos < BufferSize && pos < Counter;pos++)
ALsizei minsize = mini(BufferSize, Counter);
for(;pos < minsize;pos++)
{
OutBuffer[c][OutPos+pos] += data[pos]*gain;
gain *= step;
gain += step;
}
if(pos == Counter)
gain = Gains[c].Target;
Gains[c].Current = gain;
gain = TargetGains[c];
CurrentGains[c] = gain;
}
if(!(gain > GAIN_SILENCE_THRESHOLD))
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
continue;
for(;pos < BufferSize;pos++)
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;
}
}
+94 -30
View File
@@ -8,58 +8,122 @@
struct MixGains;
struct HrtfParams;
struct MixHrtfParams;
struct HrtfState;
/* C resamplers */
const ALfloat *Resample_copy32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
const ALfloat *Resample_point32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
const ALfloat *Resample_lerp32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
const ALfloat *Resample_cubic32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
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_lerp32_SSE2(const ALfloat *src, ALuint frac, ALuint increment,
ALfloat *restrict dst, ALuint numsamples);
const ALfloat *Resample_lerp32_SSE41(const ALfloat *src, ALuint frac, ALuint increment,
ALfloat *restrict dst, ALuint numsamples);
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_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_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 */
+88 -67
View File
@@ -6,88 +6,109 @@
#include "hrtf.h"
#include "mixer_defs.h"
#include "align.h"
#include "alu.h"
#define REAL_MERGE(a,b) a##b
#define MERGE(a,b) REAL_MERGE(a,b)
#define MixHrtf MERGE(MixHrtf_,SUFFIX)
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint irSize,
ALfloat (*restrict Coeffs)[2],
const ALfloat (*restrict CoeffStep)[2],
ALfloat left, ALfloat right);
static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint irSize,
ALfloat (*restrict Coeffs)[2],
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;
ALuint c;
ALsizei i;
for(c = 0;c < IrSize;c++)
LeftOut += OutPos;
RightOut += OutPos;
for(i = 0;i < BufferSize;i++)
{
Coeffs[c][0] = hrtfparams->Coeffs[c][0] - (hrtfparams->CoeffStep[c][0]*Counter);
Coeffs[c][1] = hrtfparams->Coeffs[c][1] - (hrtfparams->CoeffStep[c][1]*Counter);
}
Delay[0] = hrtfparams->Delay[0] - (hrtfparams->DelayStep[0]*Counter);
Delay[1] = hrtfparams->Delay[1] - (hrtfparams->DelayStep[1]*Counter);
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;
for(pos = 0;pos < BufferSize && pos < Counter;pos++)
{
hrtfstate->History[Offset&SRC_HISTORY_MASK] = data[pos];
left = lerp(hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS))&SRC_HISTORY_MASK],
hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS)-1)&SRC_HISTORY_MASK],
(Delay[0]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE));
right = lerp(hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS))&SRC_HISTORY_MASK],
hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS)-1)&SRC_HISTORY_MASK],
(Delay[1]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE));
Delay[0] += hrtfparams->DelayStep[0];
Delay[1] += hrtfparams->DelayStep[1];
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f;
Offset++;
ApplyCoeffsStep(Offset, hrtfstate->Values, IrSize, Coeffs, hrtfparams->CoeffStep, left, right);
OutBuffer[FrontLeft][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0];
OutBuffer[FrontRight][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1];
OutPos++;
}
Delay[0] >>= HRTFDELAY_BITS;
Delay[1] >>= HRTFDELAY_BITS;
for(;pos < BufferSize;pos++)
{
hrtfstate->History[Offset&SRC_HISTORY_MASK] = data[pos];
left = hrtfstate->History[(Offset-Delay[0])&SRC_HISTORY_MASK];
right = hrtfstate->History[(Offset-Delay[1])&SRC_HISTORY_MASK];
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f;
Offset++;
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[FrontLeft][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0];
OutBuffer[FrontRight][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1];
*(LeftOut++) += hrtfstate->Values[Offset&HRIR_MASK][0];
*(RightOut++) += hrtfstate->Values[Offset&HRIR_MASK][1];
OutPos++;
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;
#undef MixHrtf
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;
#undef MERGE
#undef REAL_MERGE
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];
}
}
+263 -50
View File
@@ -7,46 +7,195 @@
#include "alMain.h"
#include "alu.h"
#include "hrtf.h"
#include "mixer_defs.h"
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint IrSize,
ALfloat (*restrict Coeffs)[2],
const ALfloat (*restrict CoeffStep)[2],
ALfloat left, ALfloat right)
const 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 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 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;
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_);
for(i = 0;numsamples-i > 3;i += 4)
{
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);
}
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 = 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 ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint IrSize,
ALfloat (*restrict Coeffs)[2],
const ALfloat *Resample_fir4_32_Neon(const InterpState* UNUSED(state),
const ALfloat *restrict src, ALsizei frac, ALint increment,
ALfloat *restrict dst, ALsizei numsamples)
{
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;
InitiatePositionArrays(frac, increment, frac_, pos_, 4);
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;
}
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);
@@ -54,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]);
@@ -69,50 +220,112 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
}
}
#define SUFFIX Neon
#define MixHrtf MixHrtf_Neon
#define MixHrtfBlend MixHrtfBlend_Neon
#define MixDirectHrtf MixDirectHrtf_Neon
#include "mixer_inc.c"
#undef SUFFIX
#undef MixHrtf
void MixDirect_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 != 1.0f && Counter > 0)
ALsizei pos = 0;
gain = CurrentGains[c];
step = (TargetGains[c] - gain) * delta;
if(fabsf(step) > FLT_EPSILON)
{
for(;pos < BufferSize && pos < Counter;pos++)
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;
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. */
for(;pos < BufferSize && (pos&3) != 0;pos++)
minsize = mini(BufferSize, (pos+3)&~3);
for(;pos < minsize;pos++)
OutBuffer[c][OutPos+pos] += data[pos]*gain;
}
if(!(gain > GAIN_SILENCE_THRESHOLD))
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[pos]);
float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]);
dry4 = vaddq_f32(dry4, vmulq_f32(val4, gain4));
dry4 = vmlaq_f32(dry4, val4, gain4);
vst1q_f32(&OutBuffer[c][OutPos+pos], dry4);
}
for(;pos < BufferSize;pos++)
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;
}
}
+119 -91
View File
@@ -1,12 +1,5 @@
#include "config.h"
#ifdef IN_IDE_PARSER
/* KDevelop's parser won't recognize these defines that get added by the -msse
* switch used to compile this source. Without them, xmmintrin.h fails to
* declare anything. */
#define __MMX__
#define __SSE__
#endif
#include <xmmintrin.h>
#include "AL/al.h"
@@ -19,82 +12,79 @@
#include "mixer_defs.h"
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint IrSize,
ALfloat (*restrict Coeffs)[2],
const ALfloat (*restrict CoeffStep)[2],
ALfloat left, ALfloat right)
const ALfloat *Resample_bsinc32_SSE(const InterpState *state, const ALfloat *restrict src,
ALsizei frac, ALint increment, ALfloat *restrict dst,
ALsizei dstlen)
{
const __m128 lrlr = _mm_setr_ps(left, right, left, right);
__m128 coeffs, deltas, imp0, imp1;
__m128 vals = _mm_setzero_ps();
ALuint i;
const __m128 sf4 = _mm_set1_ps(state->bsinc.sf);
const ALsizei m = state->bsinc.m;
const ALfloat *fil, *scd, *phd, *spd;
ALsizei pi, i, j;
ALfloat pf;
__m128 r4;
if((Offset&1))
src += state->bsinc.l;
for(i = 0;i < dstlen;i++)
{
const ALuint o0 = Offset&HRIR_MASK;
const ALuint o1 = (Offset+IrSize-1)&HRIR_MASK;
// 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
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)
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 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;
const __m128 pf4 = _mm_set1_ps(pf);
#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)
{
/* 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]))
);
/* r += f*src */
r4 = MLA4(r4, f4, ULD4(&src[j]));
}
#undef MLA4
#undef ULD4
#undef LD4
}
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;
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));
dst[i] = _mm_cvtss_f32(r4);
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);
}
frac += increment;
src += frac>>FRACTIONBITS;
frac &= FRACTIONMASK;
}
return dst;
}
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]);
@@ -104,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]);
@@ -123,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]);
@@ -133,60 +123,73 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
}
}
#define SUFFIX SSE
#define MixHrtf MixHrtf_SSE
#define MixHrtfBlend MixHrtfBlend_SSE
#define MixDirectHrtf MixDirectHrtf_SSE
#include "mixer_inc.c"
#undef SUFFIX
#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;
__m128 gain4, step4;
ALuint c;
ALfloat gain, delta, step;
__m128 gain4;
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 != 1.0f && Counter > 0)
ALsizei pos = 0;
gain = CurrentGains[c];
step = (TargetGains[c] - gain) * delta;
if(fabsf(step) > FLT_EPSILON)
{
ALsizei minsize = mini(BufferSize, Counter);
/* Mix with applying gain steps in aligned multiples of 4. */
if(BufferSize-pos > 3 && Counter-pos > 3)
if(minsize-pos > 3)
{
__m128 step4;
gain4 = _mm_setr_ps(
gain,
gain * step,
gain * step * step,
gain * step * step * step
gain + step,
gain + step + step,
gain + step + step + step
);
step4 = _mm_set1_ps(step * step * step * step);
step4 = _mm_set1_ps(step + step + step + step);
do {
const __m128 val4 = _mm_load_ps(&data[pos]);
__m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]);
dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
gain4 = _mm_mul_ps(gain4, step4);
gain4 = _mm_add_ps(gain4, step4);
_mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
pos += 4;
} while(BufferSize-pos > 3 && Counter-pos > 3);
} 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 = _mm_cvtss_f32(gain4);
}
/* Mix with applying left over gain steps that aren't aligned multiples of 4. */
for(;pos < BufferSize && pos < Counter;pos++)
for(;pos < minsize;pos++)
{
OutBuffer[c][OutPos+pos] += data[pos]*gain;
gain *= step;
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. */
for(;pos < BufferSize && (pos&3) != 0;pos++)
minsize = mini(BufferSize, (pos+3)&~3);
for(;pos < minsize;pos++)
OutBuffer[c][OutPos+pos] += data[pos]*gain;
}
if(!(gain > GAIN_SILENCE_THRESHOLD))
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
continue;
gain4 = _mm_set1_ps(gain);
for(;BufferSize-pos > 3;pos += 4)
@@ -200,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;
}
}
+12 -8
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -27,17 +27,18 @@
#include "mixer_defs.h"
const ALfloat *Resample_lerp32_SSE2(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);
@@ -63,6 +64,9 @@ const ALfloat *Resample_lerp32_SSE2(const ALfloat *src, ALuint frac, ALuint incr
_mm_store_ps(pos_.f, _mm_castsi128_ps(pos4));
}
/* NOTE: These four elements represent the position *after* the last four
* samples, so the lowest element is the next position to resample.
*/
pos = pos_.i[0];
frac = _mm_cvtsi128_si32(frac4);
+97
View File
@@ -0,0 +1,97 @@
/**
* OpenAL cross platform audio library, SSE3 mixer functions
*
* Copyright (C) 2014 by Timothy Arceri <t_arceri@yahoo.com.au>.
* Copyright (C) 2015 by Chris Robinson <chris.kcat@gmail.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <xmmintrin.h>
#include <emmintrin.h>
#include <pmmintrin.h>
#include "alu.h"
#include "mixer_defs.h"
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);
union { alignas(16) ALint i[4]; float f[4]; } pos_;
union { alignas(16) ALsizei i[4]; float f[4]; } frac_;
__m128i frac4, pos4;
ALint pos;
ALsizei i;
InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
--src;
for(i = 0;numsamples-i > 3;i += 4)
{
const __m128 val0 = _mm_loadu_ps(&src[pos_.i[0]]);
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(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);
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 = _mm_hadd_ps(k0, k2);
_mm_store_ps(&dst[i], out);
frac4 = _mm_add_epi32(frac4, increment4);
pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
frac4 = _mm_and_si128(frac4, fracMask4);
_mm_store_ps(pos_.f, _mm_castsi128_ps(pos4));
_mm_store_ps(frac_.f, _mm_castsi128_ps(frac4));
}
/* NOTE: These four elements represent the position *after* the last four
* samples, so the lowest element is the next position to resample.
*/
pos = pos_.i[0];
frac = frac_.i[0];
for(;i < numsamples;i++)
{
dst[i] = resample_fir4(src[pos], src[pos+1], src[pos+2], src[pos+3], frac);
frac += increment;
pos += frac>>FRACTIONBITS;
frac &= FRACTIONMASK;
}
return dst;
}
+80 -8
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -28,17 +28,18 @@
#include "mixer_defs.h"
const ALfloat *Resample_lerp32_SSE41(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);
@@ -67,6 +68,9 @@ const ALfloat *Resample_lerp32_SSE41(const ALfloat *src, ALuint frac, ALuint inc
pos_.i[3] = _mm_extract_epi32(pos4, 3);
}
/* NOTE: These four elements represent the position *after* the last four
* samples, so the lowest element is the next position to resample.
*/
pos = pos_.i[0];
frac = _mm_cvtsi128_si32(frac4);
@@ -80,3 +84,71 @@ const ALfloat *Resample_lerp32_SSE41(const ALfloat *src, ALuint frac, ALuint inc
}
return dst;
}
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);
union { alignas(16) ALint i[4]; float f[4]; } pos_;
union { alignas(16) ALsizei i[4]; float f[4]; } frac_;
__m128i frac4, pos4;
ALint pos;
ALsizei i;
InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
--src;
for(i = 0;numsamples-i > 3;i += 4)
{
const __m128 val0 = _mm_loadu_ps(&src[pos_.i[0]]);
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(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);
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 = _mm_hadd_ps(k0, k2);
_mm_store_ps(&dst[i], out);
frac4 = _mm_add_epi32(frac4, increment4);
pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
frac4 = _mm_and_si128(frac4, fracMask4);
pos_.i[0] = _mm_extract_epi32(pos4, 0);
pos_.i[1] = _mm_extract_epi32(pos4, 1);
pos_.i[2] = _mm_extract_epi32(pos4, 2);
pos_.i[3] = _mm_extract_epi32(pos4, 3);
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_fir4(src[pos], src[pos+1], src[pos+2], src[pos+3], 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 */
+1191 -370
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 */
+45 -39
View File
@@ -5,60 +5,66 @@
#include <AL/al.h>
/* "Base" vector type, designed to alias with the actual vector types. */
typedef struct vector__s {
ALsizei Capacity;
ALsizei Size;
} *vector_;
#include "almalloc.h"
#define TYPEDEF_VECTOR(T, N) typedef struct { \
ALsizei Capacity; \
ALsizei Size; \
size_t Capacity; \
size_t Size; \
T Data[]; \
} _##N; \
typedef _##N* N; \
typedef const _##N* const_##N;
#define VECTOR(T) struct { \
ALsizei Capacity; \
ALsizei Size; \
size_t Capacity; \
size_t Size; \
T Data[]; \
}*
#define VECTOR_INIT(_x) do { (_x) = NULL; } while(0)
#define VECTOR_INIT_STATIC() NULL
#define VECTOR_DEINIT(_x) do { free((_x)); (_x) = NULL; } while(0)
#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, ALsizei obj_count, ALboolean exact);
#define VECTOR_RESERVE(_x, _c) (vector_reserve((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_c), AL_TRUE))
ALboolean vector_resize(char *ptr, size_t base_size, size_t obj_size, ALsizei obj_count);
#define VECTOR_RESIZE(_x, _c) (vector_resize((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_c)))
#define VECTOR_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,15 +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_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)) \
+614 -311
View File
File diff suppressed because it is too large Load Diff
+10 -13
View File
@@ -1,17 +1,15 @@
GNU LIBRARY GENERAL PUBLIC LICENSE
Version 2, June 1991
GNU LIBRARY GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the library GPL. It is
numbered 2 because it goes with version 2 of the ordinary GPL.]
Preamble
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
@@ -101,7 +99,7 @@ works together with the library.
Note that it is possible for a library to be covered by the ordinary
General Public License rather than by this special one.
GNU LIBRARY GENERAL PUBLIC LICENSE
GNU LIBRARY GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library which
@@ -413,7 +411,7 @@ decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
@@ -436,9 +434,9 @@ FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Libraries
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
@@ -465,8 +463,8 @@ convey the exclusion of warranty; and each file should have at least the
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., 675 Mass Ave, Cambridge, MA 02139, USA.
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
@@ -481,4 +479,3 @@ necessary. Here is a sample; alter the names:
Ty Coon, President of Vice
That's all there is to it!
+290
View File
@@ -0,0 +1,290 @@
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.
Fixed building on OSX.
Fixed building on non-Windows systems without POSIX-2008.
Fixed Dedicated Dialog and Dedicated LFE effect output.
Added a build option to override the share install dir.
Added a build option to static-link libgcc for MinGW.
openal-soft-1.17.1:
Fixed building with JACK and without PulseAudio.
Fixed building on FreeBSD.
Fixed the ALSA backend's allow-resampler option.
Fixed handling of inexact ALSA period counts.
Altered device naming scheme on Windows backends to better match other
drivers.
Updated the CoreAudio backend to use the AudioComponent API. This clears up
deprecation warnings for OSX 10.11, although requires OSX 10.6 or newer.
openal-soft-1.17.0:
Implemented a JACK playback backend.
Implemented the AL_EXT_BFORMAT and AL_EXT_MULAW_BFORMAT extensions.
Implemented the ALC_SOFT_HRTF extension.
Implemented C, SSE3, and SSE4.1 based 4- and 8-point Sinc resamplers.
Implemented a C and SSE based band-limited Sinc resampler. This does 12- to
24-point Sinc resampling, and performs anti-aliasing.
Implemented B-Format output support for the wave file writer. This creates
FuMa-style first-order Ambisonics wave files (AMB format).
Implemented a stereo-mode config option for treating stereo modes as either
speakers or headphones.
Implemented per-device configuration options.
Fixed handling of PulseAudio and MMDevAPI devices that have identical
descriptions.
Fixed a potential lockup when stopping playback of suspended PulseAudio devices.
Fixed logging of Unicode characters on Windows.
Fixed 5.1 surround sound channels. By default it will now use the side
channels for the surround output. A configuration using rear channels is
still available.
Fixed the QSA backend potentially altering the capture format.
Fixed detecting MMDevAPI's default device.
Fixed returning the default capture device name.
Fixed mixing property calculations when deferring context updates.
Altered the behavior of alcSuspendContext and alcProcessContext to better
match certain Windows drivers.
Altered the panning algorithm, utilizing Ambisonics for better side and
back positioning cues with surround sound output.
Improved support for certain older Windows apps.
Improved the alffplay example to support surround sound streams.
Improved support for building as a sub-project.
Added an HRTF playback example.
Added a tone generator output test.
Added a toolchain to help with cross-compiling to Android.
openal-soft-1.16.0:
Implemented EFX Chorus, Flanger, Distortion, Equalizer, and Compressor
effects.
Implemented high-pass and band-pass EFX filters.
Implemented the high-pass filter for the EAXReverb effect.
Implemented SSE2 and SSE4.1 linear resamplers.
Implemented Neon-enhanced non-HRTF mixers.
Implemented a QSA backend, for QNX.
Implemented the ALC_SOFT_pause_device, AL_SOFT_deferred_updates,
AL_SOFT_block_alignment, AL_SOFT_MSADPCM, and AL_SOFT_source_length
extensions.
Fixed resetting mmdevapi backend devices.
Fixed clamping when converting 32-bit float samples to integer.
Fixed modulation range in the Modulator effect.
Several fixes for the OpenSL playback backend.
Fixed device specifier names that have Unicode characters on Windows.
Added support for filenames and paths with Unicode (UTF-8) characters on
Windows.
Added support for alsoft.conf config files found in XDG Base Directory
Specification locations (XDG_CONFIG_DIRS and XDG_CONFIG_HOME, or their
defaults) on non-Windows systems.
Added a GUI configuration utility (requires Qt 4.8).
Added support for environment variable expansion in config options (not
keys or section names).
Added an example that uses SDL2 and ffmpeg.
Modified examples to use SDL_sound.
Modified CMake config option names for better sorting.
HRTF data sets specified in the hrtf_tables config option may now be
relative or absolute filenames.
Made the default HRTF data set an external file, and added a data set for
48khz playback in addition to 44.1khz.
Added support for C11 atomic methods.
Improved support for some non-GNU build systems.
openal-soft-1.15.1:
Fixed a regression with retrieving the source's AL_GAIN property.
openal-soft-1.15:
Fixed device enumeration with the OSS backend.
Reorganized internal mixing logic, so unneeded steps can potentially be
skipped for better performance.
Removed the lookup table for calculating the mixing pans. The panning is
now calculated directly for better precision.
Improved the panning of stereo source channels when using stereo output.
Improved source filter quality on send paths.
Added a config option to allow PulseAudio to move streams between devices.
The PulseAudio backend will now attempt to spawn a server by default.
Added a workaround for a DirectSound bug relating to float32 output.
Added SSE-based mixers, for HRTF and non-HRTF mixing.
Added support for the new AL_SOFT_source_latency extension.
Improved ALSA capture by avoiding an extra buffer when using sizes
supported by the underlying device.
Improved the makehrtf utility to support new options and input formats.
Modified the CFLAGS declared in the pkg-config file so the "AL/" portion of
the header includes can optionally be omitted.
Added a couple example code programs to show how to apply reverb, and
retrieve latency.
The configuration sample is now installed into the share/openal/ directory
instead of /etc/openal.
The configuration sample now gets installed by default.
+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]);
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_THUNK3(T, ALeffectState, void, process, ALuint, const ALfloat*restrict, ALfloatBUFFERSIZE*restrict) \
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);
+35 -23
View File
@@ -17,26 +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) */
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);
}
@@ -56,12 +56,14 @@ enum FmtChannels {
FmtX51 = UserFmtX51,
FmtX61 = UserFmtX61,
FmtX71 = UserFmtX71,
FmtBFormat2D = UserFmtBFormat2D,
FmtBFormat3D = UserFmtBFormat3D,
};
#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);
}
@@ -76,17 +78,18 @@ 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;
ALsizei UnpackAlign;
ALsizei PackAlign;
ATOMIC(ALsizei) UnpackAlign;
ATOMIC(ALsizei) PackAlign;
/* Number of times buffer was attached to a source (deletion can only occur when 0) */
RefCount ref;
@@ -102,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 -22
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;
@@ -132,7 +123,6 @@ typedef union ALeffectProps {
} Echo;
struct {
ALfloat Delay;
ALfloat LowCutoff;
ALfloat LowGain;
ALfloat Mid1Center;
@@ -177,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; }
+84 -32
View File
@@ -3,6 +3,8 @@
#include "alMain.h"
#include "math_defs.h"
#ifdef __cplusplus
extern "C" {
#endif
@@ -11,57 +13,98 @@ extern "C" {
#define HIGHPASSFREQREF (250.0f)
/* Filters implementation is based on the "Cookbook formulae for audio *
* EQ biquad filter coefficients" by Robert Bristow-Johnson *
* http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt */
/* Filters implementation is based on the "Cookbook formulae for audio
* EQ biquad filter coefficients" by Robert Bristow-Johnson
* http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
*/
/* Implementation note: For the shelf filters, the specified gain is for the
* reference frequency, which is the centerpoint of the transition band. This
* better matches EFX filter design. To set the gain for the shelf itself, use
* the square root of the desired linear gain (or halve the dB gain).
*/
typedef enum ALfilterType {
/** EFX-style low-pass filter, specifying a gain and reference frequency. */
ALfilterType_HighShelf,
/** EFX-style high-pass filter, specifying a gain and reference frequency. */
ALfilterType_LowShelf,
/** Peaking filter, specifying a gain, reference frequency, and bandwidth. */
/** Peaking filter, specifying a gain and reference frequency. */
ALfilterType_Peaking,
/** Low-pass cut-off filter, specifying a cut-off frequency and bandwidth. */
/** Low-pass cut-off filter, specifying a cut-off frequency. */
ALfilterType_LowPass,
/** High-pass cut-off filter, specifying a cut-off frequency and bandwidth. */
/** High-pass cut-off filter, specifying a cut-off frequency. */
ALfilterType_HighPass,
/** Band-pass filter, specifying a center frequency and bandwidth. */
/** Band-pass filter, specifying a center frequency. */
ALfilterType_BandPass,
} ALfilterType;
typedef struct ALfilterState {
ALfloat x[2]; /* History of two last input samples */
ALfloat y[2]; /* History of two last output samples */
ALfloat a[3]; /* Transfer function coefficients "a" */
ALfloat b[3]; /* Transfer function coefficients "b" */
void (*process)(struct ALfilterState *self, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples);
ALfloat b0, b1, b2; /* Transfer function coefficients "b" */
ALfloat a1, a2; /* Transfer function coefficients "a" (a0 is pre-applied) */
} 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
void ALfilterState_clear(ALfilterState *filter);
void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat gain, ALfloat freq_mult, ALfloat bandwidth);
inline ALfloat ALfilterState_processSingle(ALfilterState *filter, ALfloat sample)
/* Calculates the rcpQ (i.e. 1/Q) coefficient for shelving filters, using the
* reference gain and shelf slope parameter.
* 0 < gain
* 0 < slope <= 1
*/
inline ALfloat calc_rcpQ_from_slope(ALfloat gain, ALfloat slope)
{
ALfloat outsmp;
outsmp = filter->b[0] * sample +
filter->b[1] * filter->x[0] +
filter->b[2] * filter->x[1] -
filter->a[1] * filter->y[0] -
filter->a[2] * filter->y[1];
filter->x[1] = filter->x[0];
filter->x[0] = sample;
filter->y[1] = filter->y[0];
filter->y[0] = outsmp;
return outsmp;
return sqrtf((gain + 1.0f/gain)*(1.0f/slope - 1.0f) + 2.0f);
}
/* Calculates the rcpQ (i.e. 1/Q) coefficient for filters, using the frequency
* multiple (i.e. ref_freq / sampling_freq) and bandwidth.
* 0 < freq_mult < 0.5.
*/
inline ALfloat calc_rcpQ_from_bandwidth(ALfloat freq_mult, ALfloat bandwidth)
{
ALfloat w0 = F_TAU * freq_mult;
return 2.0f*sinhf(logf(2.0f)/2.0f*bandwidth*w0/sinf(w0));
}
void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples);
inline void ALfilterState_clear(ALfilterState *filter)
{
filter->x[0] = 0.0f;
filter->x[1] = 0.0f;
filter->y[0] = 0.0f;
filter->y[1] = 0.0f;
}
void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat gain, ALfloat freq_mult, ALfloat rcpQ);
inline void ALfilterState_copyParams(ALfilterState *restrict dst, const ALfilterState *restrict src)
{
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 *restrict src, ALsizei numsamples);
inline void ALfilterState_processPassthru(ALfilterState *filter, const ALfloat *restrict src, ALsizei numsamples)
{
if(numsamples >= 2)
{
filter->x[1] = src[numsamples-2];
filter->x[0] = src[numsamples-1];
filter->y[1] = src[numsamples-2];
filter->y[0] = src[numsamples-1];
}
else if(numsamples == 1)
{
filter->x[1] = filter->x[0];
filter->x[0] = src[0];
filter->y[1] = filter->y[0];
filter->y[0] = src[0];
}
}
typedef struct ALfilter {
@@ -98,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);
+46 -8
View File
@@ -2,25 +2,63 @@
#define _AL_LISTENER_H_
#include "alMain.h"
#include "alu.h"
#ifdef __cplusplus
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 {
volatile ALfloat Position[3];
volatile ALfloat Velocity[3];
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 {
ALfloat Matrix[4][4];
ALfloat Velocity[3];
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
+586 -399
View File
File diff suppressed because it is too large Load Diff
-172
View File
@@ -1,172 +0,0 @@
#ifndef ALMIDI_H
#define ALMIDI_H
#include "alMain.h"
#include "atomic.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct ALsfmodulator {
struct {
ALenum Input;
ALenum Type;
ALenum Form;
} Source[2];
ALint Amount;
ALenum TransformOp;
ALenum Dest;
} ALsfmodulator;
typedef struct ALenvelope {
ALint DelayTime;
ALint AttackTime;
ALint HoldTime;
ALint DecayTime;
ALint SustainAttn;
ALint ReleaseTime;
ALint KeyToHoldTime;
ALint KeyToDecayTime;
} ALenvelope;
typedef struct ALfontsound {
RefCount ref;
struct ALbuffer *Buffer;
ALint MinKey, MaxKey;
ALint MinVelocity, MaxVelocity;
ALint ModLfoToPitch;
ALint VibratoLfoToPitch;
ALint ModEnvToPitch;
ALint FilterCutoff;
ALint FilterQ;
ALint ModLfoToFilterCutoff;
ALint ModEnvToFilterCutoff;
ALint ModLfoToVolume;
ALint ChorusSend;
ALint ReverbSend;
ALint Pan;
struct {
ALint Delay;
ALint Frequency;
} ModLfo;
struct {
ALint Delay;
ALint Frequency;
} VibratoLfo;
ALenvelope ModEnv;
ALenvelope VolEnv;
ALint Attenuation;
ALint CoarseTuning;
ALint FineTuning;
ALenum LoopMode;
ALint TuningScale;
ALint ExclusiveClass;
ALuint Start;
ALuint End;
ALuint LoopStart;
ALuint LoopEnd;
ALuint SampleRate;
ALubyte PitchKey;
ALbyte PitchCorrection;
ALenum SampleType;
struct ALfontsound *Link;
/* NOTE: Each map entry contains *four* (4) ALsfmodulator objects. */
UIntMap ModulatorMap;
ALuint id;
} ALfontsound;
void ALfontsound_setPropi(ALfontsound *self, ALCcontext *context, ALenum param, ALint value);
void ALfontsound_setModStagei(ALfontsound *self, ALCcontext *context, ALsizei stage, ALenum param, ALint value);
ALfontsound *NewFontsound(ALCcontext *context);
void DeleteFontsound(ALCdevice *device, ALfontsound *sound);
inline struct ALfontsound *LookupFontsound(ALCdevice *device, ALuint id)
{ return (struct ALfontsound*)LookupUIntMapKey(&device->FontsoundMap, id); }
inline struct ALfontsound *RemoveFontsound(ALCdevice *device, ALuint id)
{ return (struct ALfontsound*)RemoveUIntMapKey(&device->FontsoundMap, id); }
void ReleaseALFontsounds(ALCdevice *device);
typedef struct ALsfpreset {
RefCount ref;
ALint Preset; /* a.k.a. MIDI program number */
ALint Bank; /* MIDI bank 0...127, or percussion (bank 128) */
ALfontsound **Sounds;
ALsizei NumSounds;
ALuint id;
} ALsfpreset;
ALsfpreset *NewPreset(ALCcontext *context);
void DeletePreset(ALCdevice *device, ALsfpreset *preset);
inline struct ALsfpreset *LookupPreset(ALCdevice *device, ALuint id)
{ return (struct ALsfpreset*)LookupUIntMapKey(&device->PresetMap, id); }
inline struct ALsfpreset *RemovePreset(ALCdevice *device, ALuint id)
{ return (struct ALsfpreset*)RemoveUIntMapKey(&device->PresetMap, id); }
void ReleaseALPresets(ALCdevice *device);
typedef struct ALsoundfont {
RefCount ref;
ALsfpreset **Presets;
ALsizei NumPresets;
RWLock Lock;
ALuint id;
} ALsoundfont;
ALsoundfont *ALsoundfont_getDefSoundfont(ALCcontext *context);
void ALsoundfont_deleteSoundfont(ALsoundfont *self, ALCdevice *device);
inline struct ALsoundfont *LookupSfont(ALCdevice *device, ALuint id)
{ return (struct ALsoundfont*)LookupUIntMapKey(&device->SfontMap, id); }
inline struct ALsoundfont *RemoveSfont(ALCdevice *device, ALuint id)
{ return (struct ALsoundfont*)RemoveUIntMapKey(&device->SfontMap, id); }
void ReleaseALSoundfonts(ALCdevice *device);
inline ALboolean IsValidCtrlInput(int cc)
{
/* These correspond to MIDI functions, not real controller values. */
if(cc == 0 || cc == 6 || cc == 32 || cc == 38 || (cc >= 98 && cc <= 101) || cc >= 120)
return AL_FALSE;
/* These are the LSB components of CC0...CC31, which are automatically used when
* reading the MSB controller value. */
if(cc >= 32 && cc <= 63)
return AL_FALSE;
/* All the rest are okay! */
return AL_TRUE;
}
#ifdef __cplusplus
}
#endif
#endif /* ALMIDI_H */
+73 -92
View File
@@ -1,110 +1,67 @@
#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" {
#endif
extern enum Resampler DefaultResampler;
extern const ALsizei ResamplerPadding[ResamplerMax];
extern const ALsizei ResamplerPrePadding[ResamplerMax];
struct ALbuffer;
struct ALsource;
typedef struct ALbufferlistitem {
struct ALbuffer *buffer;
struct ALbufferlistitem *volatile next;
struct ALbufferlistitem *volatile prev;
ATOMIC(struct ALbufferlistitem*) next;
} ALbufferlistitem;
typedef struct ALactivesource {
struct ALsource *Source;
/** Method to update mixing parameters. */
ALvoid (*Update)(struct ALactivesource *self, const ALCcontext *context);
/** Current target parameters used for mixing. */
ALint Step;
ALboolean IsHrtf;
ALuint Offset; /* Number of output samples mixed since starting. */
DirectParams Direct;
SendParams Send[MAX_SENDS];
} ALactivesource;
typedef struct ALsource {
/** Source properties. */
volatile ALfloat Pitch;
volatile ALfloat Gain;
volatile ALfloat OuterGain;
volatile ALfloat MinGain;
volatile ALfloat MaxGain;
volatile ALfloat InnerAngle;
volatile ALfloat OuterAngle;
volatile ALfloat RefDistance;
volatile ALfloat MaxDistance;
volatile ALfloat RollOffFactor;
volatile ALfloat Position[3];
volatile ALfloat Velocity[3];
volatile ALfloat Orientation[3];
volatile ALboolean HeadRelative;
volatile ALboolean Looping;
volatile enum DistanceModel DistanceModel;
volatile ALboolean DirectChannels;
volatile ALboolean DryGainHFAuto;
volatile ALboolean WetGainAuto;
volatile ALboolean WetGainHFAuto;
volatile ALfloat OuterGainHF;
volatile ALfloat AirAbsorptionFactor;
volatile ALfloat RoomRolloffFactor;
volatile ALfloat DopplerFactor;
volatile ALfloat Radius;
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;
/**
* Last user-specified offset, and the offset type (bytes, samples, or
* seconds).
ALboolean DryGainHFAuto;
ALboolean WetGainAuto;
ALboolean WetGainHFAuto;
ALfloat OuterGainHF;
ALfloat AirAbsorptionFactor;
ALfloat RoomRolloffFactor;
ALfloat DopplerFactor;
/* 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 {
@@ -121,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);
+378 -98
View File
@@ -13,34 +13,125 @@
#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"
#define F_PI (3.14159265358979323846f)
#define F_PI_2 (1.57079632679489661923f)
#define F_2PI (6.28318530717958647692f)
#define MAX_PITCH (255)
#ifndef FLT_EPSILON
#define FLT_EPSILON (1.19209290e-07f)
#endif
/* Maximum number of buffer samples before the current pos needed for resampling. */
#define MAX_PRE_SAMPLES 12
#define DEG2RAD(x) ((ALfloat)(x) * (F_PI/180.0f))
#define RAD2DEG(x) ((ALfloat)(x) * (180.0f/F_PI))
#define SRC_HISTORY_BITS (6)
#define SRC_HISTORY_LENGTH (1<<SRC_HISTORY_BITS)
#define SRC_HISTORY_MASK (SRC_HISTORY_LENGTH-1)
#define MAX_PITCH (10)
/* Maximum number of buffer samples after the current pos needed for resampling. */
#define MAX_POST_SAMPLES 12
#ifdef __cplusplus
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)
#define BSINC_PHASE_BITS 4
#define BSINC_PHASE_COUNT (1<<BSINC_PHASE_BITS)
/* Interpolator state. Kind of a misnomer since the interpolator itself is
* stateless. This just keeps it from having to recompute scale-related
* mappings for every sample.
*/
typedef struct BsincState {
ALfloat sf; /* Scale interpolation factor. */
ALuint m; /* Coefficient count. */
ALint l; /* Left coefficient offset. */
struct {
const ALfloat *filter; /* Filter coefficients. */
const ALfloat *scDelta; /* Scale deltas. */
const ALfloat *phDelta; /* Phase deltas. */
const ALfloat *spDelta; /* Scale-phase deltas. */
} 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];
} aluVector;
inline void aluVectorSet(aluVector *vector, ALfloat x, ALfloat y, ALfloat z, ALfloat w)
{
vector->v[0] = x;
vector->v[1] = y;
vector->v[2] = z;
vector->v[3] = w;
}
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)
{
matrix->m[row][0] = m0;
matrix->m[row][1] = m1;
matrix->m[row][2] = m2;
matrix->m[row][3] = m3;
}
inline void aluMatrixfSet(aluMatrixf *matrix, ALfloat m00, ALfloat m01, ALfloat m02, ALfloat m03,
ALfloat m10, ALfloat m11, ALfloat m12, ALfloat m13,
ALfloat m20, ALfloat m21, ALfloat m22, ALfloat m23,
ALfloat m30, ALfloat m31, ALfloat m32, ALfloat m33)
{
aluMatrixfSetRow(matrix, 0, m00, m01, m02, m03);
aluMatrixfSetRow(matrix, 1, m10, m11, m12, m13);
aluMatrixfSetRow(matrix, 2, m20, m21, m22, m23);
aluMatrixfSetRow(matrix, 3, m30, m31, m32, m33);
}
enum ActiveFilters {
AF_None = 0,
AF_LowPass = 1,
@@ -49,89 +140,201 @@ enum ActiveFilters {
};
typedef struct HrtfState {
alignas(16) ALfloat History[SRC_HISTORY_LENGTH];
alignas(16) ALfloat Values[HRIR_LENGTH][2];
} HrtfState;
typedef struct HrtfParams {
alignas(16) ALfloat Coeffs[HRIR_LENGTH][2];
alignas(16) ALfloat CoeffStep[HRIR_LENGTH][2];
ALuint Delay[2];
ALint DelayStep[2];
} HrtfParams;
typedef struct MixGains {
ALfloat Current;
ALfloat Step;
ALfloat Target;
} MixGains;
typedef struct MixHrtfParams {
const ALfloat (*Coeffs)[2];
ALsizei Delay[2];
ALfloat Gain;
ALfloat GainStep;
} MixHrtfParams;
typedef struct DirectParams {
ALfloat (*OutBuffer)[BUFFERSIZE];
ALfilterState LowPass;
ALfilterState HighPass;
/* If not 'moving', gain/coefficients are set directly without fading. */
ALboolean Moving;
/* Stepping counter for gain/coefficient fading. */
ALuint Counter;
NfcFilter NFCtrlFilter[MAX_AMBI_ORDER];
struct {
enum ActiveFilters ActiveType;
ALfilterState LowPass;
ALfilterState HighPass;
} Filters[MAX_INPUT_CHANNELS];
HrtfParams Old;
HrtfParams Target;
HrtfState State;
} Hrtf;
union {
struct {
HrtfParams Params[MAX_INPUT_CHANNELS];
HrtfState State[MAX_INPUT_CHANNELS];
ALuint IrSize;
ALfloat Gain;
ALfloat Dir[3];
} Hrtf;
MixGains Gains[MAX_INPUT_CHANNELS][MaxChannels];
} Mix;
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 all input channels to a single (mono)
* output buffer. */
MixGains Gain;
ALfloat Current[MAX_OUTPUT_CHANNELS];
ALfloat Target[MAX_OUTPUT_CHANNELS];
} Gains;
} SendParams;
typedef const ALfloat* (*ResamplerFunc)(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 */
#define FRACTIONBITS (14)
/* 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)
@@ -179,51 +382,129 @@ inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max)
{ return minu64(max, maxu64(min, val)); }
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 cubic(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALfloat mu)
inline ALfloat resample_fir4(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALsizei frac)
{
ALfloat mu2 = mu*mu;
ALfloat a0 = -0.5f*val0 + 1.5f*val1 + -1.5f*val2 + 0.5f*val3;
ALfloat a1 = val0 + -2.5f*val1 + 2.0f*val2 + -0.5f*val3;
ALfloat a2 = -0.5f*val0 + 0.5f*val2;
ALfloat a3 = val1;
return a0*mu*mu2 + a1*mu2 + a2*mu + a3;
return sinc4Tab[frac][0]*val0 + sinc4Tab[frac][1]*val1 +
sinc4Tab[frac][2]*val2 + sinc4Tab[frac][3]*val3;
}
ALvoid aluInitPanning(ALCdevice *Device);
enum HrtfRequestMode {
Hrtf_Default = 0,
Hrtf_Enable = 1,
Hrtf_Disable = 2,
};
void aluInitMixer(void);
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);
/**
* ComputeAngleGains
* CalcDirectionCoeffs
*
* Sets channel gains based on a given source's angle and its half-width. The
* angle and hwidth parameters are in radians.
* 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 ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat hwidth, ALfloat ingain, ALfloat gains[MaxChannels]);
void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
/**
* SetGains
* CalcAngleCoeffs
*
* Helper to set the appropriate channels to the specified gain.
* Calculates ambisonic coefficients based on azimuth and elevation. The
* azimuth and elevation parameters are in radians, going right and up
* respectively.
*/
inline void SetGains(const ALCdevice *device, ALfloat ingain, ALfloat gains[MaxChannels])
inline void CalcAngleCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS])
{
ComputeAngleGains(device, 0.0f, F_PI, ingain, gains);
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]);
ALvoid CalcSourceParams(struct ALactivesource *src, const ALCcontext *ALContext);
ALvoid CalcNonAttnSourceParams(struct ALactivesource *src, const ALCcontext *ALContext);
/**
* ComputeAmbientGains
*
* Computes channel gains for ambient, omni-directional sounds.
*/
#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]);
ALvoid MixSource(struct ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo);
/**
* ComputePanningGains
*
* Computes panning gains using the given channel decoder coefficients and the
* pre-calculated direction or angle coefficients.
*/
#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]);
ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size);
/**
* 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]);
ALboolean MixSource(struct ALvoice *voice, struct ALsource *Source, ALCdevice *Device, ALsizei SamplesToDo);
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;
@@ -233,4 +514,3 @@ extern ALfloat ZScale;
#endif
#endif
+9 -43
View File
@@ -63,63 +63,29 @@ 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 value.
/* Clear buffers and set new coefficients with new crossfeed level and sample
* rate values.
* level - crossfeed level of *LEVEL values.
* srate - sample rate by Hz.
*/
void bs2b_set_level(struct bs2b *bs2b, int level);
void bs2b_set_params(struct bs2b *bs2b, int level, int srate);
/* Return current crossfeed level value */
int bs2b_get_level(struct bs2b *bs2b);
/* Clear buffers and set new coefficients with new sample rate value.
* srate - sample rate by Hz.
*/
void bs2b_set_srate(struct bs2b *bs2b, int srate);
/* Return current sample rate value */
int bs2b_get_srate(struct bs2b *bs2b);
/* Clear buffer */
void bs2b_clear(struct bs2b *bs2b);
/* Crossfeeds one stereo sample that are pointed by sample.
* [0] - first channel, [1] - second channel.
* Returns crossfided sample by sample pointer.
*/
inline void bs2b_cross_feed(struct bs2b *bs2b, float *restrict sample)
{
/* Single pole IIR filter.
* O[n] = a0*I[n] + a1*I[n-1] + b1*O[n-1]
*/
/* Lowpass filter */
#define lo_filter(in, out_1) (bs2b->a0_lo*(in) + bs2b->b1_lo*(out_1))
/* Highboost filter */
#define hi_filter(in, in_1, out_1) (bs2b->a0_hi*(in) + bs2b->a1_hi*(in_1) + bs2b->b1_hi*(out_1))
/* Lowpass filter */
bs2b->last_sample.lo[0] = lo_filter(sample[0], bs2b->last_sample.lo[0]);
bs2b->last_sample.lo[1] = lo_filter(sample[1], bs2b->last_sample.lo[1]);
/* Highboost filter */
bs2b->last_sample.hi[0] = hi_filter(sample[0], bs2b->last_sample.asis[0], bs2b->last_sample.hi[0]);
bs2b->last_sample.hi[1] = hi_filter(sample[1], bs2b->last_sample.asis[1], bs2b->last_sample.hi[1]);
bs2b->last_sample.asis[0] = sample[0];
bs2b->last_sample.asis[1] = sample[1];
/* Crossfeed */
sample[0] = bs2b->last_sample.hi[0] + bs2b->last_sample.lo[1];
sample[1] = bs2b->last_sample.hi[1] + bs2b->last_sample.lo[0];
#undef hi_filter
#undef lo_filter
} /* bs2b_cross_feed */
void bs2b_cross_feed(struct bs2b *bs2b, float *restrict Left, float *restrict Right, int SamplesToDo);
#ifdef __cplusplus
} /* extern "C" */
+285 -108
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -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));
+134 -54
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -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 = 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)
@@ -189,6 +197,8 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi
case UserFmtX51: newformat = AL_FORMAT_51CHN32; break;
case UserFmtX61: newformat = AL_FORMAT_61CHN32; break;
case UserFmtX71: newformat = AL_FORMAT_71CHN32; break;
case UserFmtBFormat2D: newformat = AL_FORMAT_BFORMAT2D_FLOAT32; break;
case UserFmtBFormat3D: newformat = AL_FORMAT_BFORMAT3D_FLOAT32; break;
}
err = LoadData(albuf, freq, newformat, size/framesize*align,
srcchannels, srctype, data, align, AL_TRUE);
@@ -211,6 +221,8 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi
case UserFmtX51: newformat = AL_FORMAT_51CHN16; break;
case UserFmtX61: newformat = AL_FORMAT_61CHN16; break;
case UserFmtX71: newformat = AL_FORMAT_71CHN16; break;
case UserFmtBFormat2D: newformat = AL_FORMAT_BFORMAT2D_16; break;
case UserFmtBFormat3D: newformat = AL_FORMAT_BFORMAT3D_16; break;
}
err = LoadData(albuf, freq, newformat, size/framesize*align,
srcchannels, srctype, data, align, AL_TRUE);
@@ -233,6 +245,8 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi
case UserFmtX51: newformat = AL_FORMAT_51CHN16; break;
case UserFmtX61: newformat = AL_FORMAT_61CHN16; break;
case UserFmtX71: newformat = AL_FORMAT_71CHN16; break;
case UserFmtBFormat2D: newformat = AL_FORMAT_BFORMAT2D_16; break;
case UserFmtBFormat3D: newformat = AL_FORMAT_BFORMAT3D_16; break;
}
err = LoadData(albuf, freq, newformat, size/framesize*align,
srcchannels, srctype, data, align, AL_TRUE);
@@ -255,6 +269,8 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi
case UserFmtX51: newformat = AL_FORMAT_51CHN16; break;
case UserFmtX61: newformat = AL_FORMAT_61CHN16; break;
case UserFmtX71: newformat = AL_FORMAT_71CHN16; break;
case UserFmtBFormat2D: newformat = AL_FORMAT_BFORMAT2D_16; break;
case UserFmtBFormat3D: newformat = AL_FORMAT_BFORMAT3D_16; break;
}
err = LoadData(albuf, freq, newformat, size/framesize*align,
srcchannels, srctype, data, align, AL_TRUE);
@@ -264,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))
@@ -291,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 = albuf->UnpackAlign;
align = ATOMIC_LOAD_SEQ(&albuf->UnpackAlign);
if(SanitizeAlignment(srctype, &align) == AL_FALSE)
{
WriteUnlock(&albuf->lock);
@@ -343,6 +361,7 @@ AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer, ALenum format, cons
WriteUnlock(&albuf->lock);
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -361,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))
@@ -368,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 = 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)
@@ -380,6 +400,7 @@ AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer,
SET_ERROR_AND_GOTO(context, err, done);
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -396,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))
@@ -404,7 +426,7 @@ AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer,
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
WriteLock(&albuf->lock);
align = albuf->UnpackAlign;
align = ATOMIC_LOAD_SEQ(&albuf->UnpackAlign);
if(SanitizeAlignment(type, &align) == AL_FALSE)
{
WriteUnlock(&albuf->lock);
@@ -433,6 +455,7 @@ AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer,
WriteUnlock(&albuf->lock);
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -449,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))
@@ -457,7 +481,7 @@ AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer,
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
ReadLock(&albuf->lock);
align = albuf->PackAlign;
align = ATOMIC_LOAD_SEQ(&albuf->PackAlign);
if(SanitizeAlignment(type, &align) == AL_FALSE)
{
ReadUnlock(&albuf->lock);
@@ -486,6 +510,7 @@ AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer,
ReadUnlock(&albuf->lock);
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -516,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);
@@ -526,6 +552,7 @@ AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat UNUSED(va
}
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -539,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);
@@ -549,6 +577,7 @@ AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, ALfloat UNUSED(v
}
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -562,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);
@@ -574,6 +604,7 @@ AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *v
}
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -588,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);
@@ -596,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);
ExchangeInt(&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);
ExchangeInt(&albuf->PackAlign, value);
ATOMIC_STORE_SEQ(&albuf->PackAlign, value);
break;
default:
@@ -610,6 +642,7 @@ AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value)
}
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -658,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);
@@ -689,6 +723,7 @@ AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *val
}
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -703,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);
@@ -724,6 +760,7 @@ AL_API ALvoid AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *val
}
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -737,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);
@@ -749,6 +787,7 @@ AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *valu
}
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -769,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);
@@ -781,6 +821,7 @@ AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *valu
}
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -795,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);
@@ -834,11 +876,11 @@ AL_API ALvoid AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value
break;
case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
*value = albuf->UnpackAlign;
*value = ATOMIC_LOAD_SEQ(&albuf->UnpackAlign);
break;
case AL_PACK_BLOCK_ALIGNMENT_SOFT:
*value = albuf->PackAlign;
*value = ATOMIC_LOAD_SEQ(&albuf->PackAlign);
break;
default:
@@ -846,6 +888,7 @@ AL_API ALvoid AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value
}
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -859,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);
@@ -871,6 +915,7 @@ AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1
}
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -900,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);
@@ -919,6 +965,7 @@ AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values
}
done:
UnlockBuffersRead(device);
ALCcontext_DecRef(context);
}
@@ -932,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);
@@ -958,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);
@@ -1013,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)
{
@@ -1025,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 */
@@ -1034,7 +1091,7 @@ ALuint BytesFromUserFmt(enum UserFmtType type)
}
return 0;
}
ALuint ChannelsFromUserFmt(enum UserFmtChannels chans)
ALsizei ChannelsFromUserFmt(enum UserFmtChannels chans)
{
switch(chans)
{
@@ -1045,6 +1102,8 @@ ALuint ChannelsFromUserFmt(enum UserFmtChannels chans)
case UserFmtX51: return 6;
case UserFmtX61: return 7;
case UserFmtX71: return 8;
case UserFmtBFormat2D: return 3;
case UserFmtBFormat3D: return 4;
}
return 0;
}
@@ -1101,6 +1160,16 @@ static ALboolean DecomposeUserFormat(ALenum format, enum UserFmtChannels *chans,
{ AL_FORMAT_71CHN16, UserFmtX71, UserFmtShort },
{ AL_FORMAT_71CHN32, UserFmtX71, UserFmtFloat },
{ AL_FORMAT_71CHN_MULAW, UserFmtX71, UserFmtMulaw },
{ AL_FORMAT_BFORMAT2D_8, UserFmtBFormat2D, UserFmtUByte },
{ AL_FORMAT_BFORMAT2D_16, UserFmtBFormat2D, UserFmtShort },
{ AL_FORMAT_BFORMAT2D_FLOAT32, UserFmtBFormat2D, UserFmtFloat },
{ AL_FORMAT_BFORMAT2D_MULAW, UserFmtBFormat2D, UserFmtMulaw },
{ AL_FORMAT_BFORMAT3D_8, UserFmtBFormat3D, UserFmtUByte },
{ AL_FORMAT_BFORMAT3D_16, UserFmtBFormat3D, UserFmtShort },
{ AL_FORMAT_BFORMAT3D_FLOAT32, UserFmtBFormat3D, UserFmtFloat },
{ AL_FORMAT_BFORMAT3D_MULAW, UserFmtBFormat3D, UserFmtMulaw },
};
ALuint i;
@@ -1117,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)
{
@@ -1127,7 +1196,7 @@ ALuint BytesFromFmt(enum FmtType type)
}
return 0;
}
ALuint ChannelsFromFmt(enum FmtChannels chans)
ALsizei ChannelsFromFmt(enum FmtChannels chans)
{
switch(chans)
{
@@ -1138,6 +1207,8 @@ ALuint ChannelsFromFmt(enum FmtChannels chans)
case FmtX51: return 6;
case FmtX61: return 7;
case FmtX71: return 8;
case FmtBFormat2D: return 3;
case FmtBFormat3D: return 4;
}
return 0;
}
@@ -1178,6 +1249,14 @@ static ALboolean DecomposeFormat(ALenum format, enum FmtChannels *chans, enum Fm
{ AL_7POINT1_8_SOFT, FmtX71, FmtByte },
{ AL_7POINT1_16_SOFT, FmtX71, FmtShort },
{ AL_7POINT1_32F_SOFT, FmtX71, FmtFloat },
{ AL_BFORMAT2D_8_SOFT, FmtBFormat2D, FmtByte },
{ AL_BFORMAT2D_16_SOFT, FmtBFormat2D, FmtShort },
{ AL_BFORMAT2D_32F_SOFT, FmtBFormat2D, FmtFloat },
{ AL_BFORMAT3D_8_SOFT, FmtBFormat3D, FmtByte },
{ AL_BFORMAT3D_16_SOFT, FmtBFormat3D, FmtShort },
{ AL_BFORMAT3D_32F_SOFT, FmtBFormat3D, FmtFloat },
};
ALuint i;
@@ -1245,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;
@@ -1263,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;
@@ -1275,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);
@@ -1287,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);
}
@@ -1300,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);
}
@@ -1317,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);
}
}
+50 -20
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -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);
}
}
@@ -402,24 +425,31 @@ static void InitEffectParams(ALeffect *effect, ALenum type)
effect->Props.Reverb.Diffusion = AL_REVERB_DEFAULT_DIFFUSION;
effect->Props.Reverb.Gain = AL_REVERB_DEFAULT_GAIN;
effect->Props.Reverb.GainHF = AL_REVERB_DEFAULT_GAINHF;
effect->Props.Reverb.GainLF = 1.0f;
effect->Props.Reverb.DecayTime = AL_REVERB_DEFAULT_DECAY_TIME;
effect->Props.Reverb.DecayHFRatio = AL_REVERB_DEFAULT_DECAY_HFRATIO;
effect->Props.Reverb.DecayLFRatio = 1.0f;
effect->Props.Reverb.ReflectionsGain = AL_REVERB_DEFAULT_REFLECTIONS_GAIN;
effect->Props.Reverb.ReflectionsDelay = AL_REVERB_DEFAULT_REFLECTIONS_DELAY;
effect->Props.Reverb.ReflectionsPan[0] = 0.0f;
effect->Props.Reverb.ReflectionsPan[1] = 0.0f;
effect->Props.Reverb.ReflectionsPan[2] = 0.0f;
effect->Props.Reverb.LateReverbGain = AL_REVERB_DEFAULT_LATE_REVERB_GAIN;
effect->Props.Reverb.LateReverbDelay = AL_REVERB_DEFAULT_LATE_REVERB_DELAY;
effect->Props.Reverb.LateReverbPan[0] = 0.0f;
effect->Props.Reverb.LateReverbPan[1] = 0.0f;
effect->Props.Reverb.LateReverbPan[2] = 0.0f;
effect->Props.Reverb.EchoTime = 0.25f;
effect->Props.Reverb.EchoDepth = 0.0f;
effect->Props.Reverb.ModulationTime = 0.25f;
effect->Props.Reverb.ModulationDepth = 0.0f;
effect->Props.Reverb.AirAbsorptionGainHF = AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF;
effect->Props.Reverb.HFReference = 5000.0f;
effect->Props.Reverb.LFReference = 250.0f;
effect->Props.Reverb.RoomRolloffFactor = AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR;
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;
@@ -637,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);
+9 -4
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -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);
+13 -16
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -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 }
};
+92 -74
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -29,9 +29,17 @@
#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 ALfloat ALfilterState_processSingle(ALfilterState *filter, ALfloat sample);
extern inline void ALfilterState_clear(ALfilterState *filter);
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);
static void InitFilterParams(ALfilter *filter, ALenum type);
@@ -52,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);
@@ -67,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);
@@ -90,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)
@@ -106,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);
}
@@ -121,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);
@@ -139,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
@@ -157,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);
}
@@ -178,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
@@ -185,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);
}
@@ -199,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
@@ -206,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);
}
@@ -220,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
@@ -227,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);
}
@@ -241,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
@@ -253,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);
}
@@ -274,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
@@ -281,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);
}
@@ -295,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
@@ -302,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);
}
@@ -316,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
@@ -323,97 +349,89 @@ 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);
}
void ALfilterState_clear(ALfilterState *filter)
void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat gain, ALfloat freq_mult, ALfloat rcpQ)
{
filter->x[0] = 0.0f;
filter->x[1] = 0.0f;
filter->y[0] = 0.0f;
filter->y[1] = 0.0f;
}
void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat gain, ALfloat freq_mult, ALfloat bandwidth)
{
ALfloat alpha;
ALfloat w0;
ALfloat alpha, sqrtgain_alpha_2;
ALfloat w0, sin_w0, cos_w0;
ALfloat a[3] = { 1.0f, 0.0f, 0.0f };
ALfloat b[3] = { 1.0f, 0.0f, 0.0f };
// Limit gain to -100dB
gain = maxf(gain, 0.00001f);
assert(gain > 0.00001f);
w0 = F_2PI * freq_mult;
w0 = F_TAU * freq_mult;
sin_w0 = sinf(w0);
cos_w0 = cosf(w0);
alpha = sin_w0/2.0f * rcpQ;
/* Calculate filter coefficients depending on filter type */
switch(type)
{
case ALfilterType_HighShelf:
alpha = sinf(w0)/2.0f*sqrtf((gain + 1.0f/gain)*(1.0f/0.75f - 1.0f) + 2.0f);
filter->b[0] = gain*((gain+1.0f) + (gain-1.0f)*cosf(w0) + 2.0f*sqrtf(gain)*alpha);
filter->b[1] = -2.0f*gain*((gain-1.0f) + (gain+1.0f)*cosf(w0) );
filter->b[2] = gain*((gain+1.0f) + (gain-1.0f)*cosf(w0) - 2.0f*sqrtf(gain)*alpha);
filter->a[0] = (gain+1.0f) - (gain-1.0f)*cosf(w0) + 2.0f*sqrtf(gain)*alpha;
filter->a[1] = 2.0f* ((gain-1.0f) - (gain+1.0f)*cosf(w0) );
filter->a[2] = (gain+1.0f) - (gain-1.0f)*cosf(w0) - 2.0f*sqrtf(gain)*alpha;
sqrtgain_alpha_2 = 2.0f * sqrtf(gain) * alpha;
b[0] = gain*((gain+1.0f) + (gain-1.0f)*cos_w0 + sqrtgain_alpha_2);
b[1] = -2.0f*gain*((gain-1.0f) + (gain+1.0f)*cos_w0 );
b[2] = gain*((gain+1.0f) + (gain-1.0f)*cos_w0 - sqrtgain_alpha_2);
a[0] = (gain+1.0f) - (gain-1.0f)*cos_w0 + sqrtgain_alpha_2;
a[1] = 2.0f* ((gain-1.0f) - (gain+1.0f)*cos_w0 );
a[2] = (gain+1.0f) - (gain-1.0f)*cos_w0 - sqrtgain_alpha_2;
break;
case ALfilterType_LowShelf:
alpha = sinf(w0)/2.0f*sqrtf((gain + 1.0f/gain)*(1.0f/0.75f - 1.0f) + 2.0f);
filter->b[0] = gain*((gain+1.0f) - (gain-1.0f)*cosf(w0) + 2.0f*sqrtf(gain)*alpha);
filter->b[1] = 2.0f*gain*((gain-1.0f) - (gain+1.0f)*cosf(w0) );
filter->b[2] = gain*((gain+1.0f) - (gain-1.0f)*cosf(w0) - 2.0f*sqrtf(gain)*alpha);
filter->a[0] = (gain+1.0f) + (gain-1.0f)*cosf(w0) + 2.0f*sqrtf(gain)*alpha;
filter->a[1] = -2.0f* ((gain-1.0f) + (gain+1.0f)*cosf(w0) );
filter->a[2] = (gain+1.0f) + (gain-1.0f)*cosf(w0) - 2.0f*sqrtf(gain)*alpha;
sqrtgain_alpha_2 = 2.0f * sqrtf(gain) * alpha;
b[0] = gain*((gain+1.0f) - (gain-1.0f)*cos_w0 + sqrtgain_alpha_2);
b[1] = 2.0f*gain*((gain-1.0f) - (gain+1.0f)*cos_w0 );
b[2] = gain*((gain+1.0f) - (gain-1.0f)*cos_w0 - sqrtgain_alpha_2);
a[0] = (gain+1.0f) + (gain-1.0f)*cos_w0 + sqrtgain_alpha_2;
a[1] = -2.0f* ((gain-1.0f) + (gain+1.0f)*cos_w0 );
a[2] = (gain+1.0f) + (gain-1.0f)*cos_w0 - sqrtgain_alpha_2;
break;
case ALfilterType_Peaking:
alpha = sinf(w0) * sinhf(logf(2.0f) / 2.0f * bandwidth * w0 / sinf(w0));
filter->b[0] = 1.0f + alpha * gain;
filter->b[1] = -2.0f * cosf(w0);
filter->b[2] = 1.0f - alpha * gain;
filter->a[0] = 1.0f + alpha / gain;
filter->a[1] = -2.0f * cosf(w0);
filter->a[2] = 1.0f - alpha / gain;
gain = sqrtf(gain);
b[0] = 1.0f + alpha * gain;
b[1] = -2.0f * cos_w0;
b[2] = 1.0f - alpha * gain;
a[0] = 1.0f + alpha / gain;
a[1] = -2.0f * cos_w0;
a[2] = 1.0f - alpha / gain;
break;
case ALfilterType_LowPass:
alpha = sinf(w0) * sinhf(logf(2.0f) / 2.0f * bandwidth * w0 / sinf(w0));
filter->b[0] = (1.0f - cosf(w0)) / 2.0f;
filter->b[1] = 1.0f - cosf(w0);
filter->b[2] = (1.0f - cosf(w0)) / 2.0f;
filter->a[0] = 1.0f + alpha;
filter->a[1] = -2.0f * cosf(w0);
filter->a[2] = 1.0f - alpha;
b[0] = (1.0f - cos_w0) / 2.0f;
b[1] = 1.0f - cos_w0;
b[2] = (1.0f - cos_w0) / 2.0f;
a[0] = 1.0f + alpha;
a[1] = -2.0f * cos_w0;
a[2] = 1.0f - alpha;
break;
case ALfilterType_HighPass:
alpha = sinf(w0) * sinhf(logf(2.0f) / 2.0f * bandwidth * w0 / sinf(w0));
filter->b[0] = (1.0f + cosf(w0)) / 2.0f;
filter->b[1] = 1.0f + cosf(w0);
filter->b[2] = (1.0f + cosf(w0)) / 2.0f;
filter->a[0] = 1.0f + alpha;
filter->a[1] = -2.0f * cosf(w0);
filter->a[2] = 1.0f - alpha;
b[0] = (1.0f + cos_w0) / 2.0f;
b[1] = -(1.0f + cos_w0);
b[2] = (1.0f + cos_w0) / 2.0f;
a[0] = 1.0f + alpha;
a[1] = -2.0f * cos_w0;
a[2] = 1.0f - alpha;
break;
case ALfilterType_BandPass:
alpha = sinf(w0) * sinhf(logf(2.0f) / 2.0f * bandwidth * w0 / sinf(w0));
filter->b[0] = alpha;
filter->b[1] = 0;
filter->b[2] = -alpha;
filter->a[0] = 1.0f + alpha;
filter->a[1] = -2.0f * cosf(w0);
filter->a[2] = 1.0f - alpha;
b[0] = alpha;
b[1] = 0;
b[2] = -alpha;
a[0] = 1.0f + alpha;
a[1] = -2.0f * cos_w0;
a[2] = 1.0f - alpha;
break;
}
filter->b[2] /= filter->a[0];
filter->b[1] /= filter->a[0];
filter->b[0] /= filter->a[0];
filter->a[2] /= filter->a[0];
filter->a[1] /= filter->a[0];
filter->a[0] /= filter->a[0];
filter->process = ALfilterState_processC;
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];
}
@@ -616,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);
}
}
File diff suppressed because it is too large Load Diff
+94 -30
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -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,37 +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);
context->Listener->Position[0] = value1;
context->Listener->Position[1] = value2;
context->Listener->Position[2] = value3;
ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
UnlockContext(context);
break;
case AL_VELOCITY:
if(!(isfinite(value1) && isfinite(value2) && isfinite(value3)))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
LockContext(context);
context->Listener->Velocity[0] = value1;
context->Listener->Velocity[1] = value2;
context->Listener->Velocity[2] = value3;
ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
UnlockContext(context);
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
UpdateListenerProps(context);
done:
WriteUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
@@ -125,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)
@@ -133,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];
@@ -142,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);
}
@@ -162,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);
}
@@ -188,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);
}
@@ -228,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)
@@ -235,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);
}
@@ -248,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)
@@ -265,6 +274,7 @@ AL_API ALvoid AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value)
}
done:
ReadUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
@@ -276,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[0];
*value2 = context->Listener->Position[1];
*value3 = context->Listener->Position[2];
UnlockContext(context);
break;
case AL_VELOCITY:
LockContext(context);
*value1 = context->Listener->Velocity[0];
*value2 = context->Listener->Velocity[1];
*value3 = context->Listener->Velocity[2];
UnlockContext(context);
break;
default:
@@ -301,6 +308,7 @@ AL_API ALvoid AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat
}
done:
ReadUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
@@ -325,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];
@@ -338,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:
@@ -346,6 +353,7 @@ AL_API ALvoid AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values)
}
done:
ReadUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
@@ -357,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)
@@ -366,6 +375,7 @@ AL_API ALvoid AL_APIENTRY alGetListeneri(ALenum param, ALint *value)
}
done:
ReadUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
@@ -377,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[0];
*value2 = (ALint)context->Listener->Position[1];
*value3 = (ALint)context->Listener->Position[2];
UnlockContext(context);
break;
case AL_VELOCITY:
LockContext(context);
*value1 = (ALint)context->Listener->Velocity[0];
*value2 = (ALint)context->Listener->Velocity[1];
*value3 = (ALint)context->Listener->Velocity[2];
UnlockContext(context);
break;
default:
@@ -402,6 +409,7 @@ AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *valu
}
done:
ReadUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
@@ -421,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];
@@ -434,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:
@@ -442,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);
}
}
-217
View File
@@ -1,217 +0,0 @@
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "alMain.h"
#include "alMidi.h"
#include "alError.h"
#include "alThunk.h"
#include "evtqueue.h"
#include "rwlock.h"
#include "alu.h"
#include "midi/base.h"
MidiSynth *SynthCreate(ALCdevice *device)
{
MidiSynth *synth = NULL;
if(!synth) synth = SSynth_create(device);
if(!synth) synth = FSynth_create(device);
if(!synth) synth = DSynth_create(device);
return synth;
}
AL_API void AL_APIENTRY alMidiSoundfontSOFT(ALuint id)
{
alMidiSoundfontvSOFT(1, &id);
}
AL_API void AL_APIENTRY alMidiSoundfontvSOFT(ALsizei count, const ALuint *ids)
{
ALCdevice *device;
ALCcontext *context;
MidiSynth *synth;
ALenum err;
context = GetContextRef();
if(!context) return;
if(count < 0)
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
device = context->Device;
synth = device->Synth;
WriteLock(&synth->Lock);
if(synth->State == AL_PLAYING || synth->State == AL_PAUSED)
alSetError(context, AL_INVALID_OPERATION);
else
{
err = V(synth,selectSoundfonts)(context, count, ids);
if(err != AL_NO_ERROR)
alSetError(context, err);
}
WriteUnlock(&synth->Lock);
done:
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alMidiEventSOFT(ALuint64SOFT time, ALenum event, ALsizei channel, ALsizei param1, ALsizei param2)
{
ALCdevice *device;
ALCcontext *context;
ALenum err;
context = GetContextRef();
if(!context) return;
if(!(event == AL_NOTEOFF_SOFT || event == AL_NOTEON_SOFT ||
event == AL_KEYPRESSURE_SOFT || event == AL_CONTROLLERCHANGE_SOFT ||
event == AL_PROGRAMCHANGE_SOFT || event == AL_CHANNELPRESSURE_SOFT ||
event == AL_PITCHBEND_SOFT))
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
if(!(channel >= 0 && channel <= 15))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
if(!(param1 >= 0 && param1 <= 127))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
if(!(param2 >= 0 && param2 <= 127))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
device = context->Device;
ALCdevice_Lock(device);
err = MidiSynth_insertEvent(device->Synth, time, event|channel, param1, param2);
ALCdevice_Unlock(device);
if(err != AL_NO_ERROR)
alSetError(context, err);
done:
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alMidiSysExSOFT(ALuint64SOFT time, const ALbyte *data, ALsizei size)
{
ALCdevice *device;
ALCcontext *context;
ALenum err;
context = GetContextRef();
if(!context) return;
if(!data || size < 0)
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
device = context->Device;
ALCdevice_Lock(device);
err = MidiSynth_insertSysExEvent(device->Synth, time, data, size);
ALCdevice_Unlock(device);
if(err != AL_NO_ERROR)
alSetError(context, err);
done:
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alMidiPlaySOFT(void)
{
ALCcontext *context;
MidiSynth *synth;
context = GetContextRef();
if(!context) return;
synth = context->Device->Synth;
WriteLock(&synth->Lock);
MidiSynth_setState(synth, AL_PLAYING);
WriteUnlock(&synth->Lock);
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alMidiPauseSOFT(void)
{
ALCcontext *context;
MidiSynth *synth;
context = GetContextRef();
if(!context) return;
synth = context->Device->Synth;
WriteLock(&synth->Lock);
MidiSynth_setState(synth, AL_PAUSED);
WriteUnlock(&synth->Lock);
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alMidiStopSOFT(void)
{
ALCdevice *device;
ALCcontext *context;
MidiSynth *synth;
context = GetContextRef();
if(!context) return;
device = context->Device;
synth = device->Synth;
WriteLock(&synth->Lock);
MidiSynth_setState(synth, AL_STOPPED);
ALCdevice_Lock(device);
V0(synth,stop)();
ALCdevice_Unlock(device);
WriteUnlock(&synth->Lock);
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alMidiResetSOFT(void)
{
ALCdevice *device;
ALCcontext *context;
MidiSynth *synth;
context = GetContextRef();
if(!context) return;
device = context->Device;
synth = device->Synth;
WriteLock(&synth->Lock);
MidiSynth_setState(synth, AL_INITIAL);
ALCdevice_Lock(device);
V0(synth,reset)();
ALCdevice_Unlock(device);
WriteUnlock(&synth->Lock);
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alMidiGainSOFT(ALfloat value)
{
ALCdevice *device;
ALCcontext *context;
context = GetContextRef();
if(!context) return;
if(!(value >= 0.0f && isfinite(value)))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
device = context->Device;
V(device->Synth,setGain)(value);
done:
ALCcontext_DecRef(context);
}
-339
View File
@@ -1,339 +0,0 @@
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include "alMain.h"
#include "alMidi.h"
#include "alError.h"
#include "alThunk.h"
#include "midi/base.h"
extern inline struct ALsfpreset *LookupPreset(ALCdevice *device, ALuint id);
extern inline struct ALsfpreset *RemovePreset(ALCdevice *device, ALuint id);
static void ALsfpreset_Construct(ALsfpreset *self);
static void ALsfpreset_Destruct(ALsfpreset *self);
AL_API void AL_APIENTRY alGenPresetsSOFT(ALsizei n, ALuint *ids)
{
ALCcontext *context;
ALsizei cur = 0;
context = GetContextRef();
if(!context) return;
if(!(n >= 0))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
for(cur = 0;cur < n;cur++)
{
ALsfpreset *preset = NewPreset(context);
if(!preset)
{
alDeletePresetsSOFT(cur, ids);
break;
}
ids[cur] = preset->id;
}
done:
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alDeletePresetsSOFT(ALsizei n, const ALuint *ids)
{
ALCdevice *device;
ALCcontext *context;
ALsfpreset *preset;
ALsizei i;
context = GetContextRef();
if(!context) return;
if(!(n >= 0))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
device = context->Device;
for(i = 0;i < n;i++)
{
/* Check for valid ID */
if((preset=LookupPreset(device, ids[i])) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
if(ReadRef(&preset->ref) != 0)
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
}
for(i = 0;i < n;i++)
{
if((preset=LookupPreset(device, ids[i])) != NULL)
DeletePreset(device, preset);
}
done:
ALCcontext_DecRef(context);
}
AL_API ALboolean AL_APIENTRY alIsPresetSOFT(ALuint id)
{
ALCcontext *context;
ALboolean ret;
context = GetContextRef();
if(!context) return AL_FALSE;
ret = LookupPreset(context->Device, id) ? AL_TRUE : AL_FALSE;
ALCcontext_DecRef(context);
return ret;
}
AL_API void AL_APIENTRY alPresetiSOFT(ALuint id, ALenum param, ALint value)
{
ALCdevice *device;
ALCcontext *context;
ALsfpreset *preset;
context = GetContextRef();
if(!context) return;
device = context->Device;
if((preset=LookupPreset(device, id)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
if(ReadRef(&preset->ref) != 0)
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
switch(param)
{
case AL_MIDI_PRESET_SOFT:
if(!(value >= 0 && value <= 127))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
preset->Preset = value;
break;
case AL_MIDI_BANK_SOFT:
if(!(value >= 0 && value <= 128))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
preset->Bank = value;
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alPresetivSOFT(ALuint id, ALenum param, const ALint *values)
{
ALCdevice *device;
ALCcontext *context;
ALsfpreset *preset;
switch(param)
{
case AL_MIDI_PRESET_SOFT:
case AL_MIDI_BANK_SOFT:
alPresetiSOFT(id, param, values[0]);
return;
}
context = GetContextRef();
if(!context) return;
device = context->Device;
if((preset=LookupPreset(device, id)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
if(ReadRef(&preset->ref) != 0)
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
switch(param)
{
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alGetPresetivSOFT(ALuint id, ALenum param, ALint *values)
{
ALCdevice *device;
ALCcontext *context;
ALsfpreset *preset;
ALsizei i;
context = GetContextRef();
if(!context) return;
device = context->Device;
if((preset=LookupPreset(device, id)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
{
case AL_MIDI_PRESET_SOFT:
values[0] = preset->Preset;
break;
case AL_MIDI_BANK_SOFT:
values[0] = preset->Bank;
break;
case AL_FONTSOUNDS_SIZE_SOFT:
values[0] = preset->NumSounds;
break;
case AL_FONTSOUNDS_SOFT:
for(i = 0;i < preset->NumSounds;i++)
values[i] = preset->Sounds[i]->id;
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alPresetFontsoundsSOFT(ALuint id, ALsizei count, const ALuint *fsids)
{
ALCdevice *device;
ALCcontext *context;
ALsfpreset *preset;
ALfontsound **sounds;
ALsizei i;
context = GetContextRef();
if(!context) return;
device = context->Device;
if(!(preset=LookupPreset(device, id)))
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
if(count < 0)
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
if(ReadRef(&preset->ref) != 0)
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
if(count == 0)
sounds = NULL;
else
{
sounds = calloc(count, sizeof(sounds[0]));
if(!sounds)
SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done);
for(i = 0;i < count;i++)
{
if(!(sounds[i]=LookupFontsound(device, fsids[i])))
{
free(sounds);
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
}
}
}
for(i = 0;i < count;i++)
IncrementRef(&sounds[i]->ref);
sounds = ExchangePtr((XchgPtr*)&preset->Sounds, sounds);
count = ExchangeInt(&preset->NumSounds, count);
for(i = 0;i < count;i++)
DecrementRef(&sounds[i]->ref);
free(sounds);
done:
ALCcontext_DecRef(context);
}
ALsfpreset *NewPreset(ALCcontext *context)
{
ALCdevice *device = context->Device;
ALsfpreset *preset;
ALenum err;
preset = calloc(1, sizeof(*preset));
if(!preset)
SET_ERROR_AND_RETURN_VALUE(context, AL_OUT_OF_MEMORY, NULL);
ALsfpreset_Construct(preset);
err = NewThunkEntry(&preset->id);
if(err == AL_NO_ERROR)
err = InsertUIntMapEntry(&device->PresetMap, preset->id, preset);
if(err != AL_NO_ERROR)
{
ALsfpreset_Destruct(preset);
memset(preset, 0, sizeof(*preset));
free(preset);
SET_ERROR_AND_RETURN_VALUE(context, err, NULL);
}
return preset;
}
void DeletePreset(ALCdevice *device, ALsfpreset *preset)
{
RemovePreset(device, preset->id);
ALsfpreset_Destruct(preset);
memset(preset, 0, sizeof(*preset));
free(preset);
}
static void ALsfpreset_Construct(ALsfpreset *self)
{
InitRef(&self->ref, 0);
self->Preset = 0;
self->Bank = 0;
self->Sounds = NULL;
self->NumSounds = 0;
self->id = 0;
}
static void ALsfpreset_Destruct(ALsfpreset *self)
{
ALsizei i;
FreeThunkEntry(self->id);
self->id = 0;
for(i = 0;i < self->NumSounds;i++)
DecrementRef(&self->Sounds[i]->ref);
free(self->Sounds);
self->Sounds = NULL;
self->NumSounds = 0;
}
/* ReleaseALPresets
*
* Called to destroy any presets that still exist on the device
*/
void ReleaseALPresets(ALCdevice *device)
{
ALsizei i;
for(i = 0;i < device->PresetMap.size;i++)
{
ALsfpreset *temp = device->PresetMap.array[i].value;
device->PresetMap.array[i].value = NULL;
ALsfpreset_Destruct(temp);
memset(temp, 0, sizeof(*temp));
free(temp);
}
}
-454
View File
@@ -1,454 +0,0 @@
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "alMain.h"
#include "alMidi.h"
#include "alThunk.h"
#include "alError.h"
#include <alBuffer.h>
#include "midi/base.h"
extern inline struct ALsoundfont *LookupSfont(ALCdevice *device, ALuint id);
extern inline struct ALsoundfont *RemoveSfont(ALCdevice *device, ALuint id);
static void ALsoundfont_Construct(ALsoundfont *self);
static void ALsoundfont_Destruct(ALsoundfont *self);
void ALsoundfont_deleteSoundfont(ALsoundfont *self, ALCdevice *device);
ALsoundfont *ALsoundfont_getDefSoundfont(ALCcontext *context);
static size_t ALsoundfont_read(ALvoid *buf, size_t bytes, ALvoid *ptr);
AL_API void AL_APIENTRY alGenSoundfontsSOFT(ALsizei n, ALuint *ids)
{
ALCdevice *device;
ALCcontext *context;
ALsizei cur = 0;
ALenum err;
context = GetContextRef();
if(!context) return;
if(!(n >= 0))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
device = context->Device;
for(cur = 0;cur < n;cur++)
{
ALsoundfont *sfont = calloc(1, sizeof(ALsoundfont));
if(!sfont)
{
alDeleteSoundfontsSOFT(cur, ids);
SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done);
}
ALsoundfont_Construct(sfont);
err = NewThunkEntry(&sfont->id);
if(err == AL_NO_ERROR)
err = InsertUIntMapEntry(&device->SfontMap, sfont->id, sfont);
if(err != AL_NO_ERROR)
{
ALsoundfont_Destruct(sfont);
memset(sfont, 0, sizeof(ALsoundfont));
free(sfont);
alDeleteSoundfontsSOFT(cur, ids);
SET_ERROR_AND_GOTO(context, err, done);
}
ids[cur] = sfont->id;
}
done:
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alDeleteSoundfontsSOFT(ALsizei n, const ALuint *ids)
{
ALCdevice *device;
ALCcontext *context;
ALsoundfont *sfont;
ALsizei i;
context = GetContextRef();
if(!context) return;
if(!(n >= 0))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
device = context->Device;
for(i = 0;i < n;i++)
{
/* Check for valid soundfont ID */
if(ids[i] == 0)
{
if(!(sfont=device->DefaultSfont))
continue;
}
else if((sfont=LookupSfont(device, ids[i])) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
if(ReadRef(&sfont->ref) != 0)
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
}
for(i = 0;i < n;i++)
{
if(ids[i] == 0)
{
MidiSynth *synth = device->Synth;
WriteLock(&synth->Lock);
if(device->DefaultSfont != NULL)
ALsoundfont_deleteSoundfont(device->DefaultSfont, device);
device->DefaultSfont = NULL;
WriteUnlock(&synth->Lock);
continue;
}
else if((sfont=RemoveSfont(device, ids[i])) == NULL)
continue;
ALsoundfont_Destruct(sfont);
memset(sfont, 0, sizeof(*sfont));
free(sfont);
}
done:
ALCcontext_DecRef(context);
}
AL_API ALboolean AL_APIENTRY alIsSoundfontSOFT(ALuint id)
{
ALCcontext *context;
ALboolean ret;
context = GetContextRef();
if(!context) return AL_FALSE;
ret = ((!id || LookupSfont(context->Device, id)) ?
AL_TRUE : AL_FALSE);
ALCcontext_DecRef(context);
return ret;
}
AL_API void AL_APIENTRY alGetSoundfontivSOFT(ALuint id, ALenum param, ALint *values)
{
ALCdevice *device;
ALCcontext *context;
ALsoundfont *sfont;
ALsizei i;
context = GetContextRef();
if(!context) return;
device = context->Device;
if(id == 0)
sfont = ALsoundfont_getDefSoundfont(context);
else if(!(sfont=LookupSfont(device, id)))
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
{
case AL_PRESETS_SIZE_SOFT:
values[0] = sfont->NumPresets;
break;
case AL_PRESETS_SOFT:
for(i = 0;i < sfont->NumPresets;i++)
values[i] = sfont->Presets[i]->id;
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alSoundfontPresetsSOFT(ALuint id, ALsizei count, const ALuint *pids)
{
ALCdevice *device;
ALCcontext *context;
ALsoundfont *sfont;
ALsfpreset **presets;
ALsizei i;
context = GetContextRef();
if(!context) return;
device = context->Device;
if(id == 0)
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
if(!(sfont=LookupSfont(device, id)))
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
if(count < 0)
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
WriteLock(&sfont->Lock);
if(ReadRef(&sfont->ref) != 0)
{
WriteUnlock(&sfont->Lock);
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
}
if(count == 0)
presets = NULL;
else
{
presets = calloc(count, sizeof(presets[0]));
if(!presets)
{
WriteUnlock(&sfont->Lock);
SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done);
}
for(i = 0;i < count;i++)
{
if(!(presets[i]=LookupPreset(device, pids[i])))
{
free(presets);
WriteUnlock(&sfont->Lock);
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
}
}
}
for(i = 0;i < count;i++)
IncrementRef(&presets[i]->ref);
presets = ExchangePtr((XchgPtr*)&sfont->Presets, presets);
count = ExchangeInt(&sfont->NumPresets, count);
WriteUnlock(&sfont->Lock);
for(i = 0;i < count;i++)
DecrementRef(&presets[i]->ref);
free(presets);
done:
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alLoadSoundfontSOFT(ALuint id, size_t(*cb)(ALvoid*,size_t,ALvoid*), ALvoid *user)
{
ALCdevice *device;
ALCcontext *context;
ALsoundfont *sfont;
Reader reader;
context = GetContextRef();
if(!context) return;
device = context->Device;
if(id == 0)
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
if(!(sfont=LookupSfont(device, id)))
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
WriteLock(&sfont->Lock);
if(ReadRef(&sfont->ref) != 0)
{
WriteUnlock(&sfont->Lock);
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
}
if(sfont->NumPresets > 0)
{
WriteUnlock(&sfont->Lock);
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
}
reader.cb = cb;
reader.ptr = user;
reader.error = 0;
loadSf2(&reader, sfont, context);
WriteUnlock(&sfont->Lock);
done:
ALCcontext_DecRef(context);
}
static void ALsoundfont_Construct(ALsoundfont *self)
{
InitRef(&self->ref, 0);
self->Presets = NULL;
self->NumPresets = 0;
RWLockInit(&self->Lock);
self->id = 0;
}
static void ALsoundfont_Destruct(ALsoundfont *self)
{
ALsizei i;
FreeThunkEntry(self->id);
self->id = 0;
for(i = 0;i < self->NumPresets;i++)
{
DecrementRef(&self->Presets[i]->ref);
self->Presets[i] = NULL;
}
free(self->Presets);
self->Presets = NULL;
self->NumPresets = 0;
}
ALsoundfont *ALsoundfont_getDefSoundfont(ALCcontext *context)
{
ALCdevice *device = context->Device;
al_string fname = AL_STRING_INIT_STATIC();
const char *namelist;
if(device->DefaultSfont)
return device->DefaultSfont;
device->DefaultSfont = calloc(1, sizeof(device->DefaultSfont[0]));
ALsoundfont_Construct(device->DefaultSfont);
namelist = getenv("ALSOFT_SOUNDFONT");
if(!namelist || !namelist[0])
ConfigValueStr("midi", "soundfont", &namelist);
while(namelist && namelist[0])
{
const char *next, *end;
FILE *f;
while(*namelist && (isspace(*namelist) || *namelist == ','))
namelist++;
if(!*namelist)
break;
next = strchr(namelist, ',');
end = next ? next++ : (namelist+strlen(namelist));
while(--end != namelist && isspace(*end)) {
}
if(end == namelist)
continue;
al_string_append_range(&fname, namelist, end+1);
namelist = next;
f = OpenDataFile(al_string_get_cstr(fname), "openal/soundfonts");
if(f == NULL)
ERR("Failed to open %s\n", al_string_get_cstr(fname));
else
{
Reader reader;
reader.cb = ALsoundfont_read;
reader.ptr = f;
reader.error = 0;
TRACE("Loading %s\n", al_string_get_cstr(fname));
loadSf2(&reader, device->DefaultSfont, context);
fclose(f);
}
al_string_clear(&fname);
}
AL_STRING_DEINIT(fname);
return device->DefaultSfont;
}
void ALsoundfont_deleteSoundfont(ALsoundfont *self, ALCdevice *device)
{
ALsfpreset **presets;
ALsizei num_presets;
VECTOR(ALbuffer*) buffers;
ALsizei i;
VECTOR_INIT(buffers);
presets = ExchangePtr((XchgPtr*)&self->Presets, NULL);
num_presets = ExchangeInt(&self->NumPresets, 0);
for(i = 0;i < num_presets;i++)
{
ALsfpreset *preset = presets[i];
ALfontsound **sounds;
ALsizei num_sounds;
ALboolean deleting;
ALsizei j;
sounds = ExchangePtr((XchgPtr*)&preset->Sounds, NULL);
num_sounds = ExchangeInt(&preset->NumSounds, 0);
DeletePreset(device, preset);
preset = NULL;
for(j = 0;j < num_sounds;j++)
DecrementRef(&sounds[j]->ref);
/* Some fontsounds may not be immediately deletable because they're
* linked to another fontsound. When those fontsounds are deleted
* they should become deletable, so use a loop until all fontsounds
* are deleted. */
do {
deleting = AL_FALSE;
for(j = 0;j < num_sounds;j++)
{
if(sounds[j] && ReadRef(&sounds[j]->ref) == 0)
{
deleting = AL_TRUE;
if(sounds[j]->Buffer)
{
ALbuffer *buffer = sounds[j]->Buffer;
ALbuffer **iter;
#define MATCH_BUFFER(_i) (buffer == *(_i))
VECTOR_FIND_IF(iter, ALbuffer*, buffers, MATCH_BUFFER);
if(iter == VECTOR_ITER_END(buffers))
VECTOR_PUSH_BACK(buffers, buffer);
#undef MATCH_BUFFER
}
DeleteFontsound(device, sounds[j]);
sounds[j] = NULL;
}
}
} while(deleting);
free(sounds);
}
ALsoundfont_Destruct(self);
free(self);
#define DELETE_BUFFER(iter) do { \
assert(ReadRef(&(*(iter))->ref) == 0); \
DeleteBuffer(device, *(iter)); \
} while(0)
VECTOR_FOR_EACH(ALbuffer*, buffers, DELETE_BUFFER);
VECTOR_DEINIT(buffers);
#undef DELETE_BUFFER
}
static size_t ALsoundfont_read(ALvoid *buf, size_t bytes, ALvoid *ptr)
{
return fread(buf, 1, bytes, (FILE*)ptr);
}
/* ReleaseALSoundfonts
*
* Called to destroy any soundfonts that still exist on the device
*/
void ReleaseALSoundfonts(ALCdevice *device)
{
ALsizei i;
for(i = 0;i < device->SfontMap.size;i++)
{
ALsoundfont *temp = device->SfontMap.array[i].value;
device->SfontMap.array[i].value = NULL;
ALsoundfont_Destruct(temp);
memset(temp, 0, sizeof(*temp));
free(temp);
}
}
+1598 -983
View File
File diff suppressed because it is too large Load Diff
+142 -165
View File
@@ -13,24 +13,26 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* 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 "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"
#include "alMidi.h"
#include "midi/base.h"
#include "backends/base.h"
static const ALchar alVendor[] = "OpenAL Community";
@@ -45,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;
@@ -52,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);
}
@@ -74,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);
}
@@ -144,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:
@@ -159,7 +188,6 @@ done:
AL_API ALdouble AL_APIENTRY alGetDouble(ALenum pname)
{
ALCdevice *device;
ALCcontext *context;
ALdouble value = 0.0;
@@ -185,17 +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_MIDI_GAIN_SOFT:
device = context->Device;
value = (ALdouble)MidiSynth_getGain(device->Synth);
case AL_GAIN_LIMIT_SOFT:
value = (ALdouble)GAIN_MIX_MAX/context->GainBoost;
break;
case AL_MIDI_STATE_SOFT:
device = context->Device;
value = (ALdouble)MidiSynth_getState(device->Synth);
case AL_NUM_RESAMPLERS_SOFT:
value = (ALdouble)(ResamplerMax + 1);
break;
case AL_DEFAULT_RESAMPLER_SOFT:
value = (ALdouble)ResamplerDefault;
break;
default:
@@ -210,7 +241,6 @@ done:
AL_API ALfloat AL_APIENTRY alGetFloat(ALenum pname)
{
ALCdevice *device;
ALCcontext *context;
ALfloat value = 0.0f;
@@ -236,17 +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_MIDI_GAIN_SOFT:
device = context->Device;
value = MidiSynth_getGain(device->Synth);
case AL_GAIN_LIMIT_SOFT:
value = GAIN_MIX_MAX/context->GainBoost;
break;
case AL_MIDI_STATE_SOFT:
device = context->Device;
value = (ALfloat)MidiSynth_getState(device->Synth);
case AL_NUM_RESAMPLERS_SOFT:
value = (ALfloat)(ResamplerMax + 1);
break;
case AL_DEFAULT_RESAMPLER_SOFT:
value = (ALfloat)ResamplerDefault;
break;
default:
@@ -262,8 +295,6 @@ done:
AL_API ALint AL_APIENTRY alGetInteger(ALenum pname)
{
ALCcontext *context;
ALCdevice *device;
MidiSynth *synth;
ALint value = 0;
context = GetContextRef();
@@ -288,18 +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_SOUNDFONTS_SIZE_SOFT:
device = context->Device;
synth = device->Synth;
value = synth->NumSoundfonts;
case AL_GAIN_LIMIT_SOFT:
value = (ALint)(GAIN_MIX_MAX/context->GainBoost);
break;
case AL_MIDI_STATE_SOFT:
device = context->Device;
value = MidiSynth_getState(device->Synth);
case AL_NUM_RESAMPLERS_SOFT:
value = ResamplerMax + 1;
break;
case AL_DEFAULT_RESAMPLER_SOFT:
value = ResamplerDefault;
break;
default:
@@ -315,8 +348,6 @@ done:
AL_API ALint64SOFT AL_APIENTRY alGetInteger64SOFT(ALenum pname)
{
ALCcontext *context;
ALCdevice *device;
MidiSynth *synth;
ALint64SOFT value = 0;
context = GetContextRef();
@@ -341,25 +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_MIDI_CLOCK_SOFT:
device = context->Device;
ALCdevice_Lock(device);
value = MidiSynth_getTime(device->Synth);
ALCdevice_Unlock(device);
case AL_GAIN_LIMIT_SOFT:
value = (ALint64SOFT)(GAIN_MIX_MAX/context->GainBoost);
break;
case AL_SOUNDFONTS_SIZE_SOFT:
device = context->Device;
synth = device->Synth;
value = (ALint64SOFT)synth->NumSoundfonts;
case AL_NUM_RESAMPLERS_SOFT:
value = (ALint64SOFT)(ResamplerMax + 1);
break;
case AL_MIDI_STATE_SOFT:
device = context->Device;
value = (ALint64SOFT)MidiSynth_getState(device->Synth);
case AL_DEFAULT_RESAMPLER_SOFT:
value = (ALint64SOFT)ResamplerDefault;
break;
default:
@@ -385,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;
}
@@ -418,8 +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_MIDI_GAIN_SOFT:
case AL_MIDI_STATE_SOFT:
case AL_GAIN_LIMIT_SOFT:
case AL_NUM_RESAMPLERS_SOFT:
case AL_DEFAULT_RESAMPLER_SOFT:
values[0] = alGetDouble(pname);
return;
}
@@ -453,8 +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_MIDI_GAIN_SOFT:
case AL_MIDI_STATE_SOFT:
case AL_GAIN_LIMIT_SOFT:
case AL_NUM_RESAMPLERS_SOFT:
case AL_DEFAULT_RESAMPLER_SOFT:
values[0] = alGetFloat(pname);
return;
}
@@ -478,9 +509,6 @@ done:
AL_API ALvoid AL_APIENTRY alGetIntegerv(ALenum pname, ALint *values)
{
ALCcontext *context;
ALCdevice *device;
MidiSynth *synth;
ALsizei i;
if(values)
{
@@ -491,8 +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_SOUNDFONTS_SIZE_SOFT:
case AL_MIDI_STATE_SOFT:
case AL_GAIN_LIMIT_SOFT:
case AL_NUM_RESAMPLERS_SOFT:
case AL_DEFAULT_RESAMPLER_SOFT:
values[0] = alGetInteger(pname);
return;
}
@@ -503,18 +532,6 @@ AL_API ALvoid AL_APIENTRY alGetIntegerv(ALenum pname, ALint *values)
switch(pname)
{
case AL_SOUNDFONTS_SOFT:
device = context->Device;
synth = device->Synth;
if(synth->NumSoundfonts > 0)
{
if(!(values))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
for(i = 0;i < synth->NumSoundfonts;i++)
values[i] = synth->Soundfonts[i]->id;
}
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
@@ -526,9 +543,6 @@ done:
AL_API void AL_APIENTRY alGetInteger64vSOFT(ALenum pname, ALint64SOFT *values)
{
ALCcontext *context;
ALCdevice *device;
MidiSynth *synth;
ALsizei i;
if(values)
{
@@ -539,9 +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_MIDI_CLOCK_SOFT:
case AL_SOUNDFONTS_SIZE_SOFT:
case AL_MIDI_STATE_SOFT:
case AL_GAIN_LIMIT_SOFT:
case AL_NUM_RESAMPLERS_SOFT:
case AL_DEFAULT_RESAMPLER_SOFT:
values[0] = alGetInteger64SOFT(pname);
return;
}
@@ -552,18 +566,6 @@ AL_API void AL_APIENTRY alGetInteger64vSOFT(ALenum pname, ALint64SOFT *values)
switch(pname)
{
case AL_SOUNDFONTS_SOFT:
device = context->Device;
synth = device->Synth;
if(synth->NumSoundfonts > 0)
{
if(!(values))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
for(i = 0;i < synth->NumSoundfonts;i++)
values[i] = (ALint64SOFT)synth->Soundfonts[i]->id;
}
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
@@ -642,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);
@@ -659,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);
@@ -676,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);
@@ -696,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);
@@ -712,54 +728,7 @@ AL_API ALvoid AL_APIENTRY alDeferUpdatesSOFT(void)
context = GetContextRef();
if(!context) return;
if(!context->DeferUpdates)
{
ALboolean UpdateSources;
ALactivesource **src, **src_end;
ALeffectslot **slot, **slot_end;
FPUCtl oldMode;
SetMixerFPUMode(&oldMode);
LockContext(context);
context->DeferUpdates = AL_TRUE;
/* Make sure all pending updates are performed */
UpdateSources = ATOMIC_EXCHANGE(ALenum, &context->UpdateSources, AL_FALSE);
src = context->ActiveSources;
src_end = src + context->ActiveSourceCount;
while(src != src_end)
{
ALsource *source = (*src)->Source;
if(source->state != AL_PLAYING && source->state != AL_PAUSED)
{
ALactivesource *temp = *(--src_end);
*src_end = *src;
*src = temp;
--(context->ActiveSourceCount);
continue;
}
if(ATOMIC_EXCHANGE(ALenum, &source->NeedsUpdate, AL_FALSE) || UpdateSources)
(*src)->Update(*src, context);
src++;
}
slot = VECTOR_ITER_BEGIN(context->ActiveAuxSlots);
slot_end = VECTOR_ITER_END(context->ActiveAuxSlots);
while(slot != slot_end)
{
if(ATOMIC_EXCHANGE(ALenum, &(*slot)->NeedsUpdate, AL_FALSE))
V((*slot)->EffectState,update)(context->Device, *slot);
slot++;
}
UnlockContext(context);
RestoreFPUMode(&oldMode);
}
ALCcontext_DeferUpdates(context);
ALCcontext_DecRef(context);
}
@@ -771,32 +740,40 @@ AL_API ALvoid AL_APIENTRY alProcessUpdatesSOFT(void)
context = GetContextRef();
if(!context) return;
if(ExchangeInt(&context->DeferUpdates, AL_FALSE))
{
ALsizei pos;
LockContext(context);
LockUIntMapRead(&context->SourceMap);
for(pos = 0;pos < context->SourceMap.size;pos++)
{
ALsource *Source = context->SourceMap.array[pos].value;
ALenum new_state;
if((Source->state == AL_PLAYING || Source->state == AL_PAUSED) &&
Source->Offset >= 0.0)
{
ReadLock(&Source->queue_lock);
ApplyOffset(Source);
ReadUnlock(&Source->queue_lock);
}
new_state = ExchangeInt(&Source->new_state, AL_NONE);
if(new_state)
SetSourceState(Source, context, new_state);
}
UnlockUIntMapRead(&context->SourceMap);
UnlockContext(context);
}
ALCcontext_ProcessUpdates(context);
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;
}
+37 -18
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -25,34 +25,36 @@
#include "alMain.h"
#include "alThunk.h"
#include "almalloc.h"
static ALenum *ThunkArray;
static ALuint ThunkArraySize;
static RWLock ThunkLock;
static ATOMIC_FLAG *ThunkArray;
static ALsizei ThunkArraySize;
static RWLock ThunkLock;
void ThunkInit(void)
{
RWLockInit(&ThunkLock);
ThunkArraySize = 1;
ThunkArray = calloc(1, ThunkArraySize * sizeof(*ThunkArray));
ThunkArraySize = 1024;
ThunkArray = al_calloc(16, ThunkArraySize * sizeof(*ThunkArray));
}
void ThunkExit(void)
{
free(ThunkArray);
al_free(ThunkArray);
ThunkArray = NULL;
ThunkArraySize = 0;
}
ALenum NewThunkEntry(ALuint *index)
{
ALenum *NewList;
ALuint i;
void *NewList;
ALsizei i;
ReadLock(&ThunkLock);
for(i = 0;i < ThunkArraySize;i++)
{
if(ExchangeInt(&ThunkArray[i], AL_TRUE) == AL_FALSE)
if(!ATOMIC_FLAG_TEST_AND_SET(&ThunkArray[i], almemory_order_acq_rel))
{
ReadUnlock(&ThunkLock);
*index = i+1;
@@ -62,28 +64,45 @@ ALenum NewThunkEntry(ALuint *index)
ReadUnlock(&ThunkLock);
WriteLock(&ThunkLock);
NewList = realloc(ThunkArray, ThunkArraySize*2 * sizeof(*ThunkArray));
/* Double-check that there's still no free entries, in case another
* invocation just came through and increased the size of the array.
*/
for(;i < ThunkArraySize;i++)
{
if(!ATOMIC_FLAG_TEST_AND_SET(&ThunkArray[i], almemory_order_acq_rel))
{
WriteUnlock(&ThunkLock);
*index = i+1;
return AL_NO_ERROR;
}
}
NewList = al_calloc(16, ThunkArraySize*2 * sizeof(*ThunkArray));
if(!NewList)
{
WriteUnlock(&ThunkLock);
ERR("Realloc failed to increase to %u entries!\n", ThunkArraySize*2);
return AL_OUT_OF_MEMORY;
}
memset(&NewList[ThunkArraySize], 0, ThunkArraySize*sizeof(*ThunkArray));
ThunkArraySize *= 2;
memcpy(NewList, ThunkArray, ThunkArraySize*sizeof(*ThunkArray));
al_free(ThunkArray);
ThunkArray = NewList;
ThunkArraySize *= 2;
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)
ExchangeInt(&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;
}
}
+6 -4
View File
@@ -45,9 +45,11 @@ Special thanks go to:
Creative Labs for the original source code this is based off of.
Christopher Fitzgerald for the current reverb effect implementation, and
helping with the low-pass filter.
helping with the low-pass and HRTF filters.
Christian Borss for the 3D panning code the current implementation is heavilly
based on.
Christian Borss for the 3D panning code previous versions used as a base.
Ben Davis for the idea behind the current click-removal code.
Ben Davis for the idea behind a previous version of the click-removal code.
Richard Furse for helping with my understanding of Ambisonics that is used by
the various parts of the library.
+39
View File
@@ -0,0 +1,39 @@
# Cross-compiling requires CMake 2.6 or newer. Example:
# cmake .. -DCMAKE_TOOLCHAIN_FILE=../XCompile-Android.txt -DHOST=arm-linux-androideabi
# Where 'arm-linux-androideabi' is the host prefix for the cross-compiler. If
# you already have a toolchain file setup, you may use that instead of this
# file. Make sure to set CMAKE_FIND_ROOT_PATH to where the NDK toolchain was
# installed (e.g. "$ENV{HOME}/toolchains/arm-linux-androideabi-r10c-21").
# the name of the target operating system
SET(CMAKE_SYSTEM_NAME Linux)
# which compilers to use for C and C++
SET(CMAKE_C_COMPILER "${HOST}-gcc")
SET(CMAKE_CXX_COMPILER "${HOST}-g++")
SET(CMAKE_RC_COMPILER "${HOST}-windres")
# here is the target environment located
SET(CMAKE_FIND_ROOT_PATH "SET THIS TO THE NDK TOOLCHAIN'S INSTALL PATH")
# here is where stuff gets installed to
SET(CMAKE_INSTALL_PREFIX "${CMAKE_FIND_ROOT_PATH}" CACHE STRING "Install path prefix, prepended onto install directories." FORCE)
# adjust the default behaviour of the FIND_XXX() commands:
# search headers and libraries in the target environment, search
# programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
# set env vars so that pkg-config will look in the appropriate directory for
# .pc files (as there seems to be no way to force using ${HOST}-pkg-config)
set(ENV{PKG_CONFIG_LIBDIR} "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig")
set(ENV{PKG_CONFIG_PATH} "")
# Qt4 tools
SET(QT_QMAKE_EXECUTABLE ${HOST}-qmake)
SET(QT_MOC_EXECUTABLE ${HOST}-moc)
SET(QT_RCC_EXECUTABLE ${HOST}-rcc)
SET(QT_UIC_EXECUTABLE ${HOST}-uic)
SET(QT_LRELEASE_EXECUTABLE ${HOST}-lrelease)

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