Compare commits

...

2067 Commits

Author SHA1 Message Date
Dima353 e5e43fed09 Fix compile on latest vitaSDK 2026-07-23 21:41:44 +03:00
Ivan Epifanov c851a68f09 Bump cmake version 2026-01-28 19:44:52 +03:00
Ivan Epifanov 5ac19a1aa1 Bump cmake version 2026-01-28 15:43:02 +03:00
Ivan Epifanov a3c6391bb4 Limit thread priority 2021-05-23 13:55:25 +03:00
Ivan Epifanov dfeead1931 Allow to change thread priority/affinity 2021-05-23 13:36:46 +03:00
Ivan Epifanov 5724481a72 Use native threads for vita playback 2021-02-28 15:28:00 +03:00
Ivan Epifanov b88e5de1ae Use native lightweight mutexes for locks 2021-02-28 14:39:45 +03:00
Ivan Epifanov bb59b318de Shut up warning 2021-02-28 14:39:23 +03:00
Ivan Epifanov 32f350c1b5 Typo 2021-02-28 14:03:42 +03:00
Ivan Epifanov 84143100e5 Align sample size 2021-02-26 14:55:25 +03:00
Ivan Epifanov 452a1bd4de Properly reset device 2021-01-28 15:18:17 +03:00
Ivan Epifanov 419cb0e847 Reset device to initial params 2021-01-27 19:32:41 +03:00
Ivan Epifanov 6a0314bfbc Enable vita backend and add vita-specific flags 2021-01-27 18:47:36 +03:00
Ivan Epifanov 6b630fd787 Add vita playback/capture backends, courtesy of @dwhinham with some fixes 2021-01-27 18:47:36 +03:00
Ivan Epifanov 26b92d58ec GCC10 fixes, courtesy of @fgsfdsfgs 2021-01-27 18:08:35 +03:00
Ivan Epifanov ddde29b2e9 Enable NEON, fix paths and mmap 2021-01-27 17:47:36 +03:00
Ivan Epifanov 5feca399c2 Nanosleep replacement 2021-01-27 17:46:52 +03:00
Ivan Epifanov e4a60406da Load config from vita-specific paths 2021-01-27 17:46:26 +03:00
Chris Robinson 6761218e51 Release 1.19.1 2018-10-11 15:05:31 -07:00
Chris Robinson 31b9c50721 Use the common init/close functions for alffplay 2018-10-10 16:16:00 -07:00
Chris Robinson f589244fb6 Allow building alffplay without experimental extensions 2018-10-08 15:03:49 -07:00
Chris Robinson 7a79f09a31 Add a comment about waiting to kill the event thread 2018-10-07 17:18:50 -07:00
Chris Robinson db65113c5f Use a 24-bit dither depth limit 2018-10-03 13:51:21 -07:00
Chris Robinson f3f94e478b Don't limit output for ALC_DONT_CARE_SOFT and float samples 2018-10-03 13:48:04 -07:00
Chris Robinson c39eeb9638 Don't try to get the JNIEnv on Android
It's currently not used. More stuff is needed anyway which may need a different
approach.
2018-10-02 12:40:26 -07:00
Chris Robinson 493c8bbc83 Add back an inadvertently removed static 2018-10-02 12:25:26 -07:00
Chris Robinson 052fdd67cd Use < instead of != for some loop checks 2018-10-01 15:59:05 -07:00
Chris Robinson ed8f44d102 Don't scale the reverb fade counter so much 2018-09-30 16:34:00 -07:00
Chris Robinson 1d1acc0c34 Ensure BUFFERSIZE is a power of 2 2018-09-30 12:12:27 -07:00
Chris Robinson 1860b2ec8a Make the Compressor struct opaque 2018-09-30 12:08:01 -07:00
Chris Robinson 7ad4f753a4 Fix some length ranges 2018-09-29 19:39:49 -07:00
Chris Robinson d5a78f0843 Improve a couple loops
Avoid masking the index with each iteration, and instead do up to when the mask
would apply. This allows for better optimizations, in particular fewer
instructions and better chances for vectorization.
2018-09-29 18:48:28 -07:00
Chris Robinson 32494e72a5 Don't use a ringbuffer design for the limiter's side chain
Rather than continuously wrapping when used, each update uses it from the front
and copies the tail to the front at the end. This allows for more effficient
accesses in loops.
2018-09-29 11:54:45 -07:00
Chris Robinson 0396c0ecd2 Add some assumes for the limiter 2018-09-29 09:47:05 -07:00
Chris Robinson 51a447852f Simplify a lower-bound clamp 2018-09-28 18:34:21 -07:00
Chris Robinson 1684b2cc5f Constify a couple more variables 2018-09-28 16:42:22 -07:00
Chris Robinson 1dee902a5f Update changelog 2018-09-26 08:23:48 -07:00
Chris Robinson 79314c4461 Include the limiter's lookAhead delay in the device latency 2018-09-25 23:05:27 -07:00
Chris Robinson 2d6309d6fc Don't hardcode the limiter threshold
It's now calculated from the output sample type and dither depth.
2018-09-25 10:33:20 -07:00
Chris Robinson c69338bc0d Update the output limiter/compressor
This provides better characteristics for an amplitude limiter. In particular,
it utilizes the peak amplitude instead of the RMS, and the used parameters
basically guarantee no output samples exceed the given threshold... almost, due
to floating-point errors as the threshold is converted from dB to log-e for the
envelope, then is negated and converted to linear amplitude to apply to the
signal. It's quite possible for some rounding errors to creep in and not
perfectly saturate the result.
2018-09-25 10:04:14 -07:00
Chris Robinson 39c3314d00 Only compare the reverb params that induce a need for fading
The offsets and coefficients are controlled by a relatively small set of input
parameters, just with different base constants or different calculations. This
lead to numerous redundant checks since if one value didn't change, others that
use the same inputs wouldn't have either.
2018-09-23 22:59:16 -07:00
Chris Robinson db6905bf57 Clear reverb gain coefficients when doing a device update 2018-09-23 02:22:23 -07:00
Chris Robinson 800326d37a Rename ALreverbState to ReverbState 2018-09-23 01:11:58 -07:00
Chris Robinson c708f871ec Update changelog 2018-09-22 20:13:31 -07:00
Chris Robinson 77a53594ba Improve the gain stepping difference check
Given the more stable stepping now in use, check that the total difference is
enough for perceptible transition, instead of the step itself.
2018-09-22 08:26:52 -07:00
Chris Robinson 36a6b9d42a Adjust comment spacing 2018-09-21 06:06:29 -07:00
Chris Robinson b6d0ff02c2 Use an internal event to more timely release old effect states 2018-09-21 04:14:15 -07:00
Chris Robinson f21e2df4cf Remove an unnecessary mutex 2018-09-20 22:53:16 -07:00
Chris Robinson 84a90b7cc3 Stop the event thread when releasing the context
To ensure no user callback gets called after alcDestroyContext.
2018-09-20 22:44:58 -07:00
Chris Robinson ebbbeb0d66 Put user events in a union structure 2018-09-20 21:59:38 -07:00
Chris Robinson cb8545346d Always start the event thread with the context 2018-09-20 19:58:01 -07:00
Chris Robinson 5c6b8eda4f Remove another duplicate function 2018-09-19 22:18:46 -07:00
Chris Robinson ea95a8adef Combine nearly-duplicate structures 2018-09-19 21:31:46 -07:00
Chris Robinson 6eb980d1b2 Remove a couple duplicate functions 2018-09-19 21:09:19 -07:00
Chris Robinson 48b7745a49 Add macros for commonly used square roots 2018-09-19 19:53:25 -07:00
Chris Robinson 9ef4dd4247 Use ALsizei for the source resample position 2018-09-18 19:08:13 -07:00
Chris Robinson 952ff84b99 Properly queue buffers for OpenSL capture 2018-09-17 23:43:23 -07:00
Chris Robinson b77e6096b8 Fix some potential race conditions with OpenSL
For playback, increment the ring buffer's write pointer before queueing audio,
to handle cases where the callback is invoked, advancing the read pointer,
before the write pointer is advanced.

For capture, limit the number of re-queued chunks to the number of fully read
chunks.
2018-09-17 22:49:52 -07:00
Chris Robinson 7f4441ffbe Handle the bsinc C resampler like the others 2018-09-17 04:07:56 -07:00
Chris Robinson 8bacb5dfb8 Fix buffer queue mixing logic
In particular, the source sample position was reduced by the size of the
next buffer list item when one is completed, rather than the size of the
one it just completed.
2018-09-16 17:38:55 -07:00
Chris Robinson a6734c7a91 Check the effect slot list size only when there's no free entries
The list can contain (reuable) NULL entries, so the max - current_size doesn't
indicate how many can be allocated.
2018-09-14 14:53:35 -07:00
Chris Robinson db452a19da The last reverb loop update doesn't need an aligned count. 2018-09-11 19:05:49 -07:00
Chris Robinson 99737469e2 Ensure the max reverb update size is a multiple of 4
It's not an issue for the final mix, but if one loop has an unaligned count,
the next loop will have unaligned input and output buffer targets which can
crash the SSE mixers.
2018-09-11 18:39:50 -07:00
Chris Robinson b13396cce2 Separate the delay line feeding from reading
Since it feeds a different line than it reads, the feeding could overwrite
what's subsequently read.
2018-09-10 13:30:20 -07:00
Chris Robinson d1f8b78dd4 Avoid a couple line count assumptions 2018-09-10 03:19:41 -07:00
Chris Robinson 4bdab0051f Combine identical loops into a separate function 2018-09-10 03:17:14 -07:00
Chris Robinson 634b13a630 Handle the early reflection delay separate from late refeed 2018-09-10 02:10:35 -07:00
Chris Robinson 275658b6db Some suggested changes for iOS 2018-09-07 23:01:17 -07:00
Chris Robinson 9054f41434 PATH_MAX not MAX_PATH 2018-09-07 22:52:34 -07:00
Chris Robinson fa7993fe3e Load .alsoftrc from the app bundle root on macOS
Not sure what priority this should have. Currently it loads after system-level
configs, and before user-level configs.
2018-09-07 22:32:48 -07:00
Chris Robinson 46cfedb117 Pass the device name list to the backend probe method 2018-09-07 22:02:37 -07:00
Chris Robinson 212cb8e298 Implement capture support for SoundIO 2018-09-07 20:08:24 -07:00
Chris Robinson 7394dd512d Rename ALCsndioBackend 2018-09-07 18:45:24 -07:00
Chris Robinson 96aacac10c Release 1.19.0 2018-09-06 14:35:18 -07:00
Chris Robinson 4c32a0aba7 Update changelog about 32-bit using SSE2 by default 2018-09-03 21:09:31 -07:00
Chris Robinson 0f243b927c Slightly restructure some loops 2018-09-03 17:07:43 -07:00
Chris Robinson fce86815f4 Extract SIMD values right before using them 2018-09-03 12:42:31 -07:00
Chris Robinson 3b4f28d173 Avoid double-resizing when appending a char to a string 2018-09-02 21:15:21 -07:00
Chris Robinson b854f4037f Read the whole Features string for neon support 2018-09-02 18:15:56 -07:00
Chris Robinson 30bd84a935 Fix a typo in the changelog 2018-08-31 17:45:59 -07:00
Chris Robinson 0a8398dd99 Update a comment 2018-08-31 17:36:50 -07:00
Chris Robinson 01cc0cd787 Pass the appropriate /arch switch for x86 MSVC 2018-08-31 13:22:21 -07:00
Chris Robinson 0d4b68239a Include the mid-band decay with the density gain 2018-08-31 08:30:57 -07:00
Chris Robinson 68a8c42176 Calcualte and use the maximum reverb update size
Instead of requiring it to be at least as big as MAX_UPDATE_SAMPLES, which may
not be true in some situations.
2018-08-31 07:20:46 -07:00
Chris Robinson 8b733728af Apply the decay gain when reading from the late line 2018-08-31 06:31:58 -07:00
Chris Robinson 1fa464ec3e Don't modify the late reverb density with the echo parameters 2018-08-30 18:40:53 -07:00
Chris Robinson 3368bd75c7 Enable HRTF data set embedding by default 2018-08-29 15:31:04 -07:00
Chris Robinson 827c66f4f6 Widen the plain stereo width
Now full right and left is +-60 degrees instead of +-30. This should help
create a smoother panning for a sound moving in front for plain stereo output
(surround sound and HRTF are not changed).

Multi-channel sources are also not affected by this change. The stereo channel
defaults of +-pi/6 (30 degrees) still correspond to full left/right panning.
This is an unfortuante discrepancy, but is necessary for AL_EXT_STEREO_ANGLES
to work.
2018-08-29 07:13:54 -07:00
Chris Robinson 21dc2c761d Check for and use copysignf 2018-08-29 03:53:09 -07:00
Chris Robinson 529f387695 Use a separate method to warp the azimuth for plain stereo output 2018-08-29 01:45:27 -07:00
Chris Robinson dacd08dc5d Use shuffle+cvt to extract SIMD values instead of storing to memory 2018-08-26 22:36:30 -07:00
Chris Robinson 072ca731e2 Avoid making extraneous calls 2018-08-26 19:14:17 -07:00
Chris Robinson a3010f50be Pack two arrays into one 2018-08-26 19:08:50 -07:00
Chris Robinson 4be6584850 Further limit the upper frequency range for autowah 2018-08-26 19:04:49 -07:00
Chris Robinson 24b646dec0 Append the OpenSL capture device to the appropriate list 2018-08-23 18:20:09 -07:00
Chris Robinson 7d76cbddd6 Use second-order biquad filters for the reverb's T60 decay 2018-08-12 18:02:39 -07:00
Chris Robinson 90ca9b9ff4 Store the reverb state offset locally while processing 2018-08-11 01:29:15 -07:00
Chris Robinson 2b9c213f89 Correctly apply reverb coefficient fading over the entire fade length 2018-08-09 21:10:17 -07:00
Chris Robinson adfbe0d6cd Crossfade the early reflections delay coefficients 2018-08-08 22:47:07 -07:00
Chris Robinson 125ac166d1 Apply late reverb density gain on late reverb input
This also uses gain stepping for changes in density or decay, so that the
late reverb tap fades smoothly between delay offsets and density gain levels
simultaneously. Now with these changes, it's preferrable to apply density gain
adjustments on late reverb input instead of output so that samples currently in
the feedback loop won't see a sudden increase or decrease in amplitude.

A similar change can probably be made for the early reflection delays to
further smooth out delay changes.
2018-08-08 19:30:58 -07:00
Chris Robinson 1e33c4a922 Specify both scale values to FadedDelayLineOut 2018-08-08 06:24:12 -07:00
Chris Robinson a8250db2bb Use the correct input channel for the compressor effect 2018-08-08 04:05:07 -07:00
Chris Robinson 04921dd727 Properly set early reflection all-pass coefficient 2018-08-06 23:45:32 -07:00
Chris Robinson 06da60c555 Make the all-pass coefficient part of the all-pass structure 2018-08-06 21:57:08 -07:00
Chris Robinson 32edc7f33f Prevent the autowah filter frequency from reaching nyquist 2018-08-06 14:14:31 -07:00
Chris Robinson 4f92bd5938 Apply the vector all-pass separate on uninterleaved samples
With the vector all-pass applied in a self-contained function, the individual
steps of the early and late reverb stages can be better optimized with tighter
loops. That allows for more data to be held local, resulting in less thrashing
from reloading the same values multiple times.

There is room for further improvement, depending on the length of the early
delay lines and all-pass delay lines allowing for bulk reads.
2018-08-05 20:29:03 -07:00
Chris Robinson 4aa029183b Apply the reverb's T60 filter in groups of samples
The late reverb line lengths are long enough to ensure a single process loop
won't rely on reading samples it wrote in the same call. So we can safely read
in all samples we need from the feedback buffer up front, then more efficiently
filter them.
2018-08-05 00:45:13 -07:00
Chris Robinson 3f165040e2 Mix reverb samples right after generating them
Instead of generating both the early and late reverb samples first, then mixing
them both to output, this now generates and mixes the early reflections then
generates and mixes the late reverb. There's no reason to hold both at the same
time so this reduces the amount of temporary storage needed.
2018-08-04 22:52:36 -07:00
Chris Robinson 754a32fa16 Scale the reverb decay weights to cover up to 20khz
Otherwise, using the device's maximum frequency will cause the weighting
factors to shift for different sample rates, irrespective of the content being
processed. 20khz is the maximum allowed reference frequency, and also acts as
the upper limit of human hearing.
2018-08-03 20:04:08 -07:00
Chris Robinson 57b860d8ca Trace the NFC reference distance when set 2018-08-03 00:07:08 -07:00
Chris Robinson 0c67429a38 Apply late reverb density gain adjustment on output
Because density/late line length changes start affecting late reverb output
right away, with samples that are still going through feedback decay and not
just new input samples, it makes more sense to correct for it on output instead
of input. This has the additional benefit of working with the output mixer's
gain fading, avoiding discontinuities from significant density gain changes.
2018-08-01 16:08:27 -07:00
Chris Robinson aa58a5d208 Fix late reverb density gain blend weights
Now it only accounts for the representable frequency range (0.5 normalized, or
 0...pi radians instead of tau). Previously, the bulk of the weighting factors
was given to the HF decay (nearly 90%, given a 44.1khz sample rate and the
default 5khz reference), with low- and mid-frequency decays splitting the
remaining 10%. Now it's closer to 75%, matching the range of representable
frequencies above the reference.

This could probably be improved further due to human hearing being less
sensitive to higher frequencies, but that is much more complicated.
2018-08-01 02:48:31 -07:00
Chris Robinson 3894c580bf Use just the omnidirectional response for the compressor effect
This is not the output compressor/limiter, but the EFX effect. Consequently, it
simply compresses the dynamic range around 1.0 (boosting samples below it by up
to double, reducing samples above it by as much as half). This is not intended
to prevent clipping on the output, but to instead reduce the range between
quiet sounds and loud sounds.
2018-07-29 00:18:46 -07:00
Chris Robinson f1bf932a84 Update ChangeLog 2018-07-28 14:11:12 -07:00
Chris Robinson 8ab448b119 Inline the autowah peaking filter processing 2018-07-28 14:03:28 -07:00
Chris Robinson 39a5d0cb94 Mark a couple parameters as unused 2018-07-25 13:15:47 -07:00
Raulshc 7d68eeac88 EFX: Enable 3D processing
Use channel 0 envelope for calculate the frequency in all channels.
2018-07-25 12:51:09 -07:00
Raulshc 92fd59fe6a EFX: Add 3D processing for autowah
Add 3D processing code. It can be activated at compilation time.
2018-07-25 12:51:09 -07:00
Raulshc f79683d459 Alsoft-config: Add autowah effect 2018-07-25 12:51:09 -07:00
Raulshc 83dba26ea6 EFX: Autowah implementation
Add autowah effect using biquad peaking filter and envelope follower
2018-07-25 12:51:09 -07:00
Chris Robinson 809f709ba6 Limit the normalized filter frequency to under half
Nearing half, weird things can start happening with the filters' generated sine
and cosine values.
2018-07-21 14:22:52 -07:00
Chris Robinson 6fe1ffa3bb Improve handling of 0hz ring modulator frequency 2018-07-21 14:21:00 -07:00
Chris Robinson d117a5209f On 32-bit targets with SSE, enable SSE/SSE2 codegen by default
Two new CMake options are available for 32-bit targets that accept -msse:
ALSOFT_ENABLE_SSE_CODEGEN and ALSOFT_ENABLE_SSE2_CODEGEN, which default to
TRUE. This should not affect MSVC, which already defaults to SSE2 codegen.
2018-07-16 08:41:56 -07:00
Chris Robinson ed1f1d2bf3 Start an extension to allow source filter gains greater than 1 2018-07-15 21:01:26 -07:00
kcat fd3f52ea36 Merge pull request #210 from qwertychouskie/patch-1
Fix COPYING
2018-07-02 18:38:57 -07:00
QwertyChouskie 4c61103e34 Fix COPYING
Remove bad chars, remove section that shouldn't be there.
2018-07-02 17:09:53 -07:00
kcat dc3fa3e51f Merge pull request #207 from t6/patch-sndio-oss
Prefer sndio backend over OSS when both are enabled
2018-06-22 11:49:00 -07:00
Tobias Kortkamp e2d7c5dfb3 Prefer sndio over OSS when both are enabled
Signed-off-by: Tobias Kortkamp <tobik@FreeBSD.org>
2018-06-22 14:36:33 +02:00
kcat 7e2cac5292 Merge pull request #204 from jhasse/android-byte-order
Correctly check byte order for newer Android compiler, fix #203
2018-06-20 12:53:22 -07:00
Jan Niklas Hasse a80ab01227 Correctly check byte order for newer Android compiler, fix #203 2018-06-20 21:36:33 +02:00
Chris Robinson 73e08e51ba Properly get the full executable and pathname on FreeBSD 2018-06-08 19:49:13 -07:00
Chris Robinson fd1458ce1b Always prepare the ALSA PCM handle before starting capture
Draining the ALSA device via stopping puts it into a setup state, which
requires re-preparing before playback can start again. Preparing it prior to
the first start seems to cause no harm, so just always do it before starting.
2018-06-08 05:02:47 -07:00
kcat 9c643f0356 Merge pull request #200 from kdhp/fix-199
Stop capture devices while closing
2018-06-07 19:30:34 -07:00
kdhp 35b78d55a4 Stop capture devices while closing
In 'alcCaptureCloseDevice', check if the capture device is
running and stop it if necessary.
This fixes the case where the device data is deallocated while
a background thread is still running (Issue #199)
2018-06-08 01:42:13 +00:00
Chris Robinson bd9c6989c2 Clear the TLS pointer after running its destructor callback 2018-06-06 23:10:37 -07:00
Chris Robinson 72e4b60374 Clamp the maximum normalized reference frequency too 2018-06-03 01:52:05 -07:00
Chris Robinson 48b265e136 Use a higher normalized frequency limit for the ring modulator 2018-06-02 00:02:14 -07:00
Chris Robinson 7865ebb6d5 Use the biquad high-pass in the ring modulator 2018-05-31 22:21:00 -07:00
Chris Robinson f765099503 Slightly simplify the modulator square wave generator 2018-05-31 19:43:02 -07:00
kcat d5dd4937eb Merge pull request #197 from Raulshc/Modulator
EFX: Modulator fixes
2018-05-31 19:38:42 -07:00
Raulshc 1bba276aaf EFX: Ring modulator fixes
Change from unipolar to bipolar carrier signal in the Ring modulator effect.
2018-05-31 15:47:47 +02:00
Raulshc 5233c4aa21 Merge from kcat/master
Update git repository
2018-05-31 14:17:20 +02:00
Chris Robinson 0b7f35b289 Avoid extra sample copies and storage in the modulator effect 2018-05-29 22:55:21 -07:00
Chris Robinson 46c59f382f Use fastf2i instead of manual rounding in another place 2018-05-29 22:49:45 -07:00
Chris Robinson 8854720e8a Avoid separate in/out parameters when they're always the same 2018-05-29 22:15:49 -07:00
kcat 42d3235a66 Merge pull request #196 from rindeal/cmake-sse2-typo-fix
CMake: fix SSE3 typo
2018-05-29 16:24:42 -07:00
Jan Chren (rindeal) 5cba81cf35 CMake: fix SSE3 typo
Closes: https://github.com/kcat/openal-soft/issues/195
2018-05-29 18:32:30 +02:00
Chris Robinson a5edf487dd Use a macro to handle common case formatting 2018-05-27 01:03:18 -07:00
Chris Robinson 728dd5a4e0 Avoid setting unnecessary variables 2018-05-26 20:43:05 -07:00
Chris Robinson ac4061b9d2 Better sort the effect list in alsoft-config 2018-05-26 18:17:17 -07:00
Chris Robinson ec84a107a4 Don't hardcode the max channels for HRTF B-Format decoding 2018-05-26 00:26:11 -07:00
Chris Robinson 803d331711 Improve formatting of the hilbert function 2018-05-24 00:16:50 -07:00
Chris Robinson 422cf429e6 Clamp the dither depth between 2 and 20 bits 2018-05-23 19:49:49 -07:00
Chris Robinson 93de5350b9 Add some LIKELY and ASSUME statements 2018-05-23 19:22:21 -07:00
Chris Robinson 9bf3ee722c Fix a function comment about a return value 2018-05-23 06:10:09 -07:00
Chris Robinson 1782208fde Remove unused function 2018-05-23 05:58:06 -07:00
Chris Robinson 84a94a6109 Update ChangeLog 2018-05-23 01:04:52 -07:00
Chris Robinson c2a4a35c2e Use a tighter loop to handle the frequency shifter's fifo 2018-05-22 07:28:53 -07:00
Chris Robinson f3d4220cab Use fixed point for the frequency shifter's phase 2018-05-22 07:02:14 -07:00
Chris Robinson 8219bb374d Fix a couple property variable names 2018-05-22 06:19:59 -07:00
Chris Robinson 80df89d0db Improve the frequency shifter output accum handling 2018-05-22 06:12:55 -07:00
Chris Robinson ecdc58c1c9 Fix formatting and line endings 2018-05-22 05:59:03 -07:00
kcat da63b4a596 Merge pull request #193 from Raulshc/Frequency-shifter
EFX: Frequency shifter
2018-05-22 04:44:50 -07:00
Chris Robinson 4e315353c8 Add a method to queue multiple buffer layers onto a source 2018-05-21 23:25:56 -07:00
Chris Robinson 720ec2beea Use the __BYTE_ORDER__ macro when available 2018-05-21 17:51:57 -07:00
Chris Robinson a235259b5e Further clarify a comment about float precision 2018-05-21 06:16:03 -07:00
Raulshc 97c165b951 Add correct cast 2018-05-20 18:44:24 +02:00
Raulshc da6f32a1c0 Alsoft-config: Add frequency shifter effect 2018-05-20 17:27:37 +02:00
Raulshc d3a81f4f28 EFX: Frequency Shifter implementation
Add frequency shifter effect using discrete Hilbert transform. Only mono signal processing by now (LEFT_DIRECTION).
2018-05-20 17:23:03 +02:00
Raulshc dd51ba396b Common: Implement discrete Hilbert transform 2018-05-20 17:21:50 +02:00
Chris Robinson 7501c8b483 Simplify counting for the bsinc FIR loop 2018-05-19 08:18:08 -07:00
Chris Robinson 5e7513d0a0 Accumulate ambisonic upsampler gains using double-precision 2018-05-18 18:59:27 -07:00
Chris Robinson cd2fd126c4 Add an alffplay option to play stereo streams with wide angles 2018-05-17 03:54:32 -07:00
Chris Robinson 0cd375f03c Add a function to calculate coefficients from X, Y, Z components 2018-05-17 03:52:40 -07:00
Chris Robinson 588a6bcb4f Simplify counting for SIMD MixRow functions 2018-05-15 23:14:23 -07:00
Chris Robinson 72e39ba1c9 Fix a function comment 2018-05-15 22:11:10 -07:00
Chris Robinson 6534aa0c27 Undefine the correct macros 2018-05-15 21:46:22 -07:00
Chris Robinson e42489b3e9 Include math_defs.h to ensure M_PI is defined 2018-05-15 18:50:32 -07:00
Chris Robinson 2b9064cb6e Move the ALcomplex and FFT functions to a separate file 2018-05-15 18:31:41 -07:00
Chris Robinson 197e88cdcc Avoid using unsigned values for signed 2018-05-15 01:20:39 -07:00
Chris Robinson 4ac4889912 Avoid unnecessary function-like macros 2018-05-15 00:53:12 -07:00
Chris Robinson 4ee04cd444 Use a step counter for gain stepping
This should provide more stable stepping, preventing floating-point errors from
accumulating on each step/sample.
2018-05-14 23:41:29 -07:00
Chris Robinson 43dccc8807 Add a faster double-to-int converter for x87 builds 2018-05-14 18:00:43 -07:00
Chris Robinson df9faba689 Use more accurate elevations for virtual HRTF speaker positions 2018-05-14 05:28:06 -07:00
Chris Robinson 5272caf7f4 More accurately convert between degrees and radians 2018-05-14 00:20:31 -07:00
Chris Robinson 1e04c4c689 Make a faster float2int method for x87 targets 2018-05-12 22:34:15 -07:00
Chris Robinson dd055fc858 Use fastf2i instead of manually rounding with float2int 2018-05-12 21:36:23 -07:00
Chris Robinson 5c0673049c Fix MSVC 2018-05-12 03:31:58 -07:00
Chris Robinson f240952bcc Fix non-SEE (32-bit) GCC builds 2018-05-12 00:58:27 -07:00
Chris Robinson e787a241c0 Add and use a method for fast float rounding
Unlike fastf2i, this keeps the result as a float instead of converting to
integer.
2018-05-12 00:52:09 -07:00
Chris Robinson 3867cad94d Simplify calculating the HRTF B-Format IR length 2018-05-04 06:48:20 -07:00
Chris Robinson be30e6bf8f Don't assume the FPU is round-to-zero in the pitch shifter 2018-05-04 04:53:50 -07:00
Chris Robinson 1fb6428ffa Another fix attempt for 32-bit MSVC 2018-05-04 04:02:57 -07:00
Chris Robinson b31a54e972 Try to fix 32-bit MSVC builds 2018-05-04 02:56:01 -07:00
Chris Robinson 75e2cb97f7 Don't assume round-to-zero for fastf2i 2018-05-04 02:09:32 -07:00
Chris Robinson ac8dbd7a56 Add a specific function for truncating float-to-int conversions 2018-05-03 22:02:32 -07:00
Chris Robinson d8a659c6f2 Avoid fastf2i in the converter init 2018-05-03 17:27:30 -07:00
Chris Robinson af90d89b6b Use a fixed-point scale for the pitch shifter frequency index 2018-05-03 17:06:55 -07:00
Chris Robinson a19296e3cf Avoid excessive if block depths 2018-05-02 21:06:57 -07:00
Chris Robinson 85c03925fb Avoid duplication for getting the cpuid 2018-05-02 20:59:53 -07:00
Chris Robinson 22413b82ca Pass in the number of channels per order to InitNearFieldCtrl 2018-05-01 20:21:24 -07:00
Chris Robinson e8aaa9cb13 An output device buffer is likely 2018-04-29 18:03:33 -07:00
Chris Robinson 2385ab700c Avoid potentially calling log10f(0) 2018-04-29 14:02:33 -07:00
Chris Robinson 242ed45f65 Rename some struct members for clarity 2018-04-28 22:10:22 -07:00
Chris Robinson e420752e82 Fix a comment about a float's mantissa 2018-04-28 18:52:40 -07:00
Chris Robinson 54109043b8 Remove some unnecessary floor calls 2018-04-28 01:14:17 -07:00
Chris Robinson ddd4751f87 Increase the band-split IR for decoding ambisonics to HRTF 2018-04-27 18:42:26 -07:00
Chris Robinson 492c75cb7b Rename some variables for clarity 2018-04-27 00:41:02 -07:00
Chris Robinson 1e0728af64 Transpose the band-splitter all-pass section 2018-04-26 23:26:11 -07:00
Chris Robinson 1cc6983b96 Use doubles for the pitch shifter's FFTs and processing 2018-04-24 21:40:41 -07:00
Chris Robinson 8311b57ca6 Update ChangeLog 2018-04-24 00:18:02 -07:00
Chris Robinson 510efae066 Don't specialize biquad processing for a single sample 2018-04-24 00:17:07 -07:00
Chris Robinson b3ba90f5fa Use transposed direct form 2 for the UHJ all-pass filters
This has one extra multiply, but avoids two moves and uses almost half as much
memory for the encoder state.
2018-04-22 03:34:29 -07:00
Chris Robinson e10595df31 Pre-calculate the squares of the UHJ filter coefficients 2018-04-22 02:39:14 -07:00
Chris Robinson b51d30f84d Change some if checks to asserts since they must be true 2018-04-22 02:38:09 -07:00
Chris Robinson ea8b52ee2c Include header files in CMake's source lists 2018-04-21 23:42:04 -07:00
Chris Robinson 9575eebac4 Move the bnad-splitter filters to a separate source 2018-04-21 23:23:46 -07:00
Chris Robinson ace8e64850 Only use fast float-to-int workarounds for x87
At least SSE and ARM have opcodes that handle float-to-int conversions well
enough. Also, Clang doesn't inline lrintf, incurring function call overhead for
what should be a single opcode.
2018-04-21 21:07:20 -07:00
Chris Robinson 4ee26f4ca3 Add some more ASSUME statements 2018-04-21 02:44:01 -07:00
Chris Robinson a55c93e1f5 Improve ASSUME for Clang
For some reason, the { if(!x)__builtin_unreachable(); } construct does not
provide the same optimization opportunity for Clang (even though the condition
being false would trigger undefined behavior by reaching unreachable code, it
still performs checks and such for the condition potentially being false).
Using __builtin_assume seems to work better.
2018-04-21 01:54:43 -07:00
Chris Robinson 400ab8766c Adjust the stereo panning front gain
This gives it a (more) precise -4.5dB gain drop for front-center panned sounds.
2018-04-19 21:55:20 -07:00
Chris Robinson 90b8d639f1 Remove unnecessary undefs 2018-04-19 18:24:27 -07:00
Chris Robinson 525b6fe168 Clear ALSA's PCM handle after closing it 2018-04-19 12:22:39 -07:00
Chris Robinson f96a8fe369 Add some ASSUME statements that ensure mixing at least 1 sample 2018-04-18 20:39:52 -07:00
Chris Robinson 150586d7fe Add an ASSUME macro that requires a true condition 2018-04-17 22:50:50 -07:00
Chris Robinson 09194fd488 Accumulate the B-Format HRTF responses using doubles
The final result is still truncated to single-precision float, but this should
keep the responses more stable as it accumulates the various inputs.
2018-04-17 21:27:47 -07:00
Chris Robinson e619b64175 Don't minimize the HRTF per-response delay
The reverts both fa9ab9af7c and
79604c3c0e. As helpful as it was for the high
frequencies, the overall response's gain suffered.
2018-04-16 18:54:41 -07:00
Chris Robinson 795ed65797 Preliminary ChangeLog update 2018-04-15 19:01:38 -07:00
Chris Robinson 5b82dd8733 Update .gitignore 2018-04-14 13:02:21 -07:00
Chris Robinson 9c5307a48a Rename BiquadState to BiquadFilter 2018-04-04 18:07:46 -07:00
Chris Robinson b1fe405861 Improve ordering of channel processing in makehrtf 2018-04-03 11:21:15 -07:00
Chris Robinson 869637af2e Apply biquad and T60 filters using transposed direct form II 2018-04-03 10:15:35 -07:00
Chris Robinson 414b56edec Initialize COM using the multithreaded apartment
I honestly have no idea which is the correct (or better) mode to use given the
confusing mess COM is, but CoInitialize uses single-threaded apartments which
seems to be a problem for with at least a couple games in the STALKER series
(the call fails, which causes us to drop back to the DSound backend).
2018-04-01 16:39:20 -07:00
Chris Robinson 334bc4f551 Limit the near-field control distance to 10m. 2018-03-31 23:11:48 -07:00
Chris Robinson e288c3b44a Fix HRTF HOA coefficients
ACN8 was accidentally put on ACN7. However, rather than adding the missing
channel of silence, set up the coefficient map to only use the ambisonic
channels that can contribute to output.
2018-03-31 20:44:14 -07:00
Chris Robinson 91900b0599 Fix misspellings of quadraphonic
AL_LOKI_quadriphonic is left alone since that is what the extension is called
and what code expects. All other instances have been fixed for consistency.
2018-03-30 09:18:17 -07:00
Chris Robinson 29ae74d4bb Fix lower elevation synthesis for stereo HRTFs 2018-03-29 20:44:44 -07:00
Chris Robinson a2c24ff946 Specify Libs.private for the pkg-config file
Only used when building the static lib for its dependencies, since the shared
lib automatically handles its own dependencies.
2018-03-29 18:57:48 -07:00
Chris Robinson 61de399806 Don't cache SDL2_LIBRARY_TEMP with CMake
Otherwise it keeps prepending and appending the same extra libs and flags with
each invocation.
2018-03-29 18:10:58 -07:00
Chris Robinson f48a1e3a31 Add simple descriptions to some extension formats in alext.h 2018-03-29 17:18:24 -07:00
Chris Robinson 4a8c3b50b6 Apply the initial decay and air absorption after gain clamping 2018-03-29 16:11:46 -07:00
Chris Robinson 852ad41176 Pass the azimuth and elevation to CalcPanningAndFilters 2018-03-29 16:11:46 -07:00
Chris Robinson 880d555060 Combine two loops 2018-03-29 16:11:46 -07:00
kcat 5523913a81 Merge pull request #184 from hhyyrylainen/master
Added missing string include to alffplay
2018-03-29 16:11:13 -07:00
Henri Hyyryläinen a7da29804d Added missing string include to alffplay 2018-03-30 01:53:55 +03:00
Chris Robinson cae4b1a062 Don't specify macros as arguments to CHECK_INCLUDE_FILE(S) 2018-03-28 14:34:58 -07:00
Chris Robinson 2b16ff3ca8 Check all buffers to update a source with 2018-03-28 06:28:46 -07:00
Chris Robinson 5aecce5a0d Store the ALbufferlistitem's composited/max sample length 2018-03-27 18:09:28 -07:00
Chris Robinson d85ddf8aae Rename NfcFilterUpdate* to NfcFilterProcess* for consistency 2018-03-27 05:58:58 -07:00
Chris Robinson 788f5398b0 Slightly relax the memory order for ref counters 2018-03-26 10:14:27 -07:00
Chris Robinson 964723691a Condense an if check 2018-03-26 06:04:11 -07:00
Chris Robinson 2475d4652c Clear mixing target parameters before setting them 2018-03-25 18:02:07 -07:00
Chris Robinson 79eb2ea26e Reduce the gain of front-panned sounds with plain stereo
Now front-center sounds are attenuated by roughly -4.5dB instead of -3dB. This
will help keep rear-panned sounds from attenuating too much while not making
front-panned also attenuate too much.
2018-03-25 12:24:43 -07:00
Chris Robinson f3672ab26b Annotate the B-Format rotation/conversion matrix 2018-03-25 08:24:53 -07:00
Chris Robinson f757fbce4d Fix Hanning -> Hann window name 2018-03-24 14:18:29 -07:00
Chris Robinson e5e3b05015 Fix capture device closing in the CoreAudio backend 2018-03-24 09:25:49 -07:00
kcat eccf679ca9 Merge pull request #181 from jhasse/sdl2-include
Also add SDL2's include path
2018-03-24 09:25:13 -07:00
Jan Niklas Hasse 3fc9d3a1be Also add SDL2's include path 2018-03-24 16:45:13 +01:00
Chris Robinson 3f071a90a4 Use proc_pidpath to get the process path on macOS when available 2018-03-24 08:25:58 -07:00
Chris Robinson 413d55aaa5 Fix a couple comments about coordinate handedness 2018-03-24 07:02:27 -07:00
Raulshc 6d8062a2a7 EFX: Align some arrays used in intrinsics (#180) 2018-03-24 05:43:56 -07:00
Chris Robinson 6990478369 Rename ALfilterState/Type to BiquadState/Type 2018-03-23 14:52:59 -07:00
Chris Robinson e37634e908 Remove an unused struct and move some functions to where they're used 2018-03-23 11:58:11 -07:00
Chris Robinson 63c9d95b84 Combine multiple allocations into one 2018-03-23 11:39:07 -07:00
Chris Robinson 63c35248be Some formatting cleanup 2018-03-22 11:32:23 -07:00
Chris Robinson 6685e0a078 Don't use an ALsizei for a potentially negative value 2018-03-22 11:11:45 -07:00
Chris Robinson 7789cc8e32 Define the Hanning window globally once for the pitch shifter 2018-03-22 09:55:15 -07:00
Chris Robinson 1d6622aa2b Fix a delta phase offset calculation in the pitch shifter
tmp can be negative, and &1 is not the same as %2 in that case.
2018-03-22 08:56:48 -07:00
Chris Robinson eeff730034 Hold some immediate values on the stack 2018-03-22 08:49:35 -07:00
Chris Robinson 23fd7451d0 Avoid some memset calls in the pitch shifter process loop 2018-03-22 08:49:22 -07:00
Chris Robinson bc3a96308e Use appropriately-sized buffers for the pitch shifter 2018-03-22 08:05:48 -07:00
Chris Robinson 8e976a92f7 Move NFC filters to the filter directory 2018-03-22 07:17:48 -07:00
Chris Robinson 7a23330ffe Move the filter implementation to a separate directory 2018-03-22 07:05:40 -07:00
Chris Robinson 6ea3b5445f Rename mixer_inc.c to hrtf_inc.c 2018-03-22 05:41:42 -07:00
Chris Robinson 091e676db3 Move mixer sources into a sub-directory 2018-03-22 05:06:15 -07:00
Chris Robinson 6ad171781a Use the global MixSamples for the pitch shifter output 2018-03-22 03:13:09 -07:00
Chris Robinson d157d66678 Change a parameter type to float since that's what it's used as 2018-03-22 02:13:40 -07:00
Chris Robinson 4da6364fc2 Slightly restructure the pitch-shifter process loop 2018-03-22 02:10:51 -07:00
Chris Robinson 7b95753cf7 Fix typo (Substraction -> Subtraction) and rename related function 2018-03-21 23:10:12 -07:00
Chris Robinson 8f3d496541 Avoid duplicate path searches in the router
And avoid inadvertently increasing the priority of the system path over the
executable's path, or either of them over the current working directory.
2018-03-21 20:39:04 -07:00
Chris Robinson 0a6c17c544 Don't auto-attenuate the pitch shifter output 2018-03-21 09:32:04 -07:00
Chris Robinson f11d7eeadf Don't use mutable fields for constant values 2018-03-21 09:23:58 -07:00
Chris Robinson 22a7bcd5ad Avoid placing a 2K sample buffer on the stack 2018-03-21 09:01:32 -07:00
Chris Robinson ecc51c8c55 Clean up some code formatting in the pitch shifter source
Clean up excessive newlines and extra-long comments, move static inline
definitions to their declarations.
2018-03-21 08:56:26 -07:00
Chris Robinson 55a73b8df9 Fix for older MSVC lacking strtof 2018-03-21 08:35:37 -07:00
kcat c40b4ce3f2 Merge pull request #177 from Raulshc/Pitch-shifter
EFX:Pitch shifter
2018-03-21 08:35:09 -07:00
Chris Robinson 47260fc70b Update the multi-reverb example with improved transitions
This better calculates the environment coverage by correctly calculating the
portal's extents, improves the panning direction when close to the portal, and
applies attenuation based on contribution. Movement has changed to make the
listener move back and forth between environments with a stationary source,
rather than continually looping environments with a position-relative source.
2018-03-20 23:58:32 -07:00
Raulshc c38854fe3d EFX:Own size for pitch shifter buffers 2018-03-19 16:48:00 +01:00
Chris Robinson df6e4617e4 Fix the reverb panning behavior to better fit the spec
Previously it would attenuate the response from direction opposite to the
vector, whereas the property descriptions say it should simply move all
reflections toward the given direction.
2018-03-19 06:41:38 -07:00
Raulshc 56423b9ef1 Alsoft-config: Add pitch shifter effect 2018-03-18 17:53:07 +01:00
Raulshc 6fd23f0984 EFX:Pitch Shifter implementation
Add pitch shifter effect using standard phase vocoder, based on work of Stephan Bernsee. Only mono signal processing by now.
2018-03-18 17:47:17 +01:00
Chris Robinson 325fca5215 Change the presets for the multireverb example 2018-03-16 05:01:00 -07:00
Chris Robinson 3fde27d890 Use a macro template to define similar functions 2018-03-15 00:28:49 -07:00
Chris Robinson 445f63f350 Remove an extraneous + 2018-03-14 23:59:20 -07:00
Chris Robinson 603e3e23ac Remove an outdated comment 2018-03-14 23:51:13 -07:00
Chris Robinson 2011421e18 Release the PulseAudio mainloop lock sooner 2018-03-14 23:48:46 -07:00
Chris Robinson 7c0e68a33e Store the filter history in local variables
Despite being marked as restrict (and const for src) to mark the pointers as
being non-aliased, it seems the compiler optimizes better this way.
2018-03-14 17:27:41 -07:00
Chris Robinson f65e83c349 Avoid using pa_stream_begin_write with PulseAudio
It seems to actually have a negative performance impact when the system is
under load. Without having actual measurements for any potential benefits,
simply go with the recommended (and previous fallback) method of allocating
space for the write and passing the free method.

Ideally some kind of ring buffer could be used, so rather than constantly
allocating and freeing blocks of memory, it uses the same memory over again
with the callback marking each one as reusable. Unfortunately the callback
isn't given much information to work with, and the update size (minreq) can
potentially change during playback, which complicates things.
2018-03-14 03:21:26 -07:00
Chris Robinson 942abab8f9 Move a loop into a function 2018-03-13 03:45:24 -07:00
Chris Robinson 8eb7a94e1c Apply a distance decay on the source send for the reverb's DecayLFRatio 2018-03-11 22:40:08 -07:00
Chris Robinson 6b35a4534f Comment and clarify some code 2018-03-11 22:18:11 -07:00
Chris Robinson dac9379449 Add methods to clean up althrd and altss data 2018-03-10 12:10:58 -08:00
Chris Robinson a6ddeaf5f1 Rename the mmdevapi backend to wasapi 2018-03-09 18:56:24 -08:00
Chris Robinson 30821e978b Add extern "C" to some headers 2018-03-09 17:24:03 -08:00
Chris Robinson c0e7a5b8b0 Prefix SDL2 device names on Windows 2018-03-09 11:41:28 -08:00
Chris Robinson 8b24255557 Request the device's sample type for SDL2 2018-03-09 10:53:03 -08:00
Chris Robinson 46e7e96eb3 Store the frame size for the SDL2 backend 2018-03-08 22:23:36 -08:00
Chris Robinson 41cc82e84b Print the unsupported values from SDL2 2018-03-08 22:09:30 -08:00
Chris Robinson 2866c6cf28 Init and quit the SDL2 audio subsystem with the factory 2018-03-08 18:59:22 -08:00
Chris Robinson 0c7fa1b955 Avoid deinitializing backends that never initialized 2018-03-08 18:53:49 -08:00
Chris Robinson aa7df8183f Also save the update size with SDL2 2018-03-08 18:22:03 -08:00
Chris Robinson 58760e6184 Use the audio device lock for the SDL2 backend 2018-03-08 18:16:10 -08:00
Chris Robinson 52ca70d98e Properly close the SDL2 audio device 2018-03-08 18:12:14 -08:00
Chris Robinson d4fc87fc9c Avoid calling SDL_GetNumAudioDevices multiple times 2018-03-08 18:07:49 -08:00
Chris Robinson 2187316bce Use function-like macros to call filter and effect vtable methods 2018-03-08 17:48:22 -08:00
Chris Robinson 52dee31270 Only calculate the modulation samples once for all 4 channels 2018-03-08 17:48:22 -08:00
Chris Robinson 22d3e3b563 Simplify modulator effect index updating 2018-03-08 17:48:21 -08:00
Chris Robinson 4e613c5d4b Rename UpdateContextSources for clarity 2018-03-08 17:48:21 -08:00
Chris Robinson 07c8857558 Fix struct forward declaration typo 2018-03-08 17:48:21 -08:00
Chris Robinson 9bd1678299 Fix adding to and removing from the active effect slots
It wasn't properly removing all duplicates on insertion, and didn't remove the
first effect slot when removing them.
2018-03-08 17:48:21 -08:00
Chris Robinson 19281868dc Properly limit the max number of effect slots to 2^31 - 1 2018-03-08 17:48:21 -08:00
kcat 5133fe5115 Merge pull request #175 from jhasse/sdl2-backend
Add SDL2 backend for playback, fix #173
2018-03-08 17:47:32 -08:00
Jan Niklas Hasse 2eb29d538b SDL2 backend: Reset device parameters 2018-03-08 20:42:47 +01:00
Jan Niklas Hasse 0af075b5ff SDL2 backend: Allow changes to channels and format 2018-03-08 20:27:02 +01:00
Jan Niklas Hasse ef2b813776 SDL2 backend: Allow frequency changes 2018-03-08 10:17:02 +01:00
Jan Niklas Hasse a6f93a501a Handle passing NULL to SDL_OpenAudioDevice as a special device
https://discourse.libsdl.org/t/changing-audio-output-when-the-default-audio-playback-device-changes/22399/2
2018-03-08 10:07:22 +01:00
Jan Niklas Hasse 767807cd22 SDL2 backend: Use callback instead of a timing loop
The system clock and audio clock aren't necessarily synchronized.
2018-03-08 09:51:44 +01:00
Jan Niklas Hasse 86319127e3 Add SDL2 backend for playback, fix #173 2018-03-07 20:57:53 +01:00
Chris Robinson 179e1c4dbc Don't check for space in the ringbuffer before trying to write
The write method already checks and returns how much it managed to fit in.
2018-03-03 21:57:42 -08:00
Chris Robinson 945d74cbc9 Avoid some code duplication 2018-03-03 14:04:10 -08:00
Chris Robinson b8636a3dbf Add some missing locks to protect reading state 2018-03-03 13:54:21 -08:00
Chris Robinson 4e6c758daf Use a plain mutex for the property lock 2018-03-03 13:42:37 -08:00
Chris Robinson 4bd02eada6 Init more stuff in InitDevice 2018-03-03 09:00:14 -08:00
Chris Robinson bd32a682f7 Use atomic variables instead of volatile 2018-03-02 19:16:55 -08:00
Chris Robinson 03274a5b95 Ensure at least the specified ringbuffer size is writable
Previously, all but one of the specified size could be written (so for a size
of n, only n-1 was guaranteed writable). All users pretty much compensated for
this, but it makes more sense to fix it at the source.
2018-03-02 13:01:11 -08:00
Chris Robinson 6f62fed65c Add an option to limit the write size of the ringbuffer 2018-03-01 21:23:13 -08:00
Chris Robinson cba37819d1 Add missing include 2018-03-01 21:19:49 -08:00
Chris Robinson 4b85104c4f Apply the limiter, distance comp, and others even with no output buffer 2018-03-01 16:16:37 -08:00
Chris Robinson 0d91d63cf3 Fix use of argv[0] as the program name after incrementing 2018-02-28 21:04:45 -08:00
Chris Robinson abc6e37e6e Apply distance attenuation to reverb zones in almultireverb 2018-02-28 20:59:41 -08:00
Chris Robinson 812cb62617 Fix a comment regarding the effect slot channel scaling 2018-02-28 20:39:53 -08:00
Chris Robinson a211c2f5e4 Avoid AL prefix on internal effect state factory types
Also avoid using the generic V/V0 macros for them
2018-02-28 19:37:12 -08:00
Chris Robinson d25398d2c7 Avoid using static inline in headers 2018-02-25 13:14:14 -08:00
Chris Robinson 654a45833a Only send one buffer completed event per update
The 'param' callback parameter indicates the number of buffers that were
completed.
2018-02-25 10:05:25 -08:00
Chris Robinson 455763aa9a Count all buffers in a list item for processed and queued 2018-02-25 09:51:07 -08:00
Chris Robinson c7456affd5 Don't make the source state atomic 2018-02-24 09:44:52 -08:00
Chris Robinson a1ef5e38b6 Handle source state change events 2018-02-24 09:24:18 -08:00
Chris Robinson 14bdc6c2ef Use separate functions to add and remove active effect slots 2018-02-23 23:02:31 -08:00
Chris Robinson bf886444d2 Avoid multiple GetChannelIdxByName calls 2018-02-21 19:53:18 -08:00
Chris Robinson 9fc2dbe063 Update the multi-zone reverb example for clarity 2018-02-20 08:08:18 -08:00
Chris Robinson 51c6d13c0f Add almultireverb to the install target 2018-02-20 08:08:18 -08:00
kcat e7ac63be4d Merge pull request #171 from digitalist/patch-1
fix building on freebsd (clang), maybe osx too
2018-02-20 07:46:38 -08:00
Alexey Elymanov d7d043a5ef fix building on freebsd (clang)
fixes error: 'pow' is not a member of 'std'
2018-02-20 11:53:55 +03:00
Chris Robinson 1002bc36e0 Add an example for multi-zone reverb 2018-02-18 23:56:51 -08:00
Chris Robinson 6ff50558a0 Use a proper struct for specifying angular points 2018-02-18 23:56:51 -08:00
Chris Robinson fa9ab9af7c Minimize each response delay for the ambisonic HRTF decode 2018-02-18 23:56:51 -08:00
Chris Robinson b5aa0c0759 Remove the unnecessary ComputeAmbientGains 2018-02-18 23:56:51 -08:00
Chris Robinson 79604c3c0e Preserve RMS for the high-frequency HRTF ambisonic coefficients 2018-02-18 23:56:51 -08:00
Chris Robinson 7a974b2460 Combine the vector reverse and partial scatter where they're together 2018-02-18 23:56:51 -08:00
Chris Robinson 7f3d69fdbc Remove an unnecessary comment about if meters_per_unit is > 0 2018-02-18 23:56:51 -08:00
Chris Robinson 7dafac0c34 Avoid duplicating some scale tables 2018-02-18 23:56:51 -08:00
kcat c24aea712b Merge pull request #170 from digitalist/patch-1
freebsd fix: typo
2018-02-18 23:56:02 -08:00
Alexey Elymanov 063ad490a2 freebsd fix: typo
Alc/helpers.c:738:30: error: use of undeclared identifier 'KERN_PROCARGS'
    int mib[4] = { CTL_KERN, KERN_PROCARGS, getpid() };

/usr/include/sys/sysctl.h:
`#define	KERN_PROC_ARGS		7	/* get/set arguments/proctitle */`
there's no KERN_PROCARGS
2018-02-18 20:55:31 +03:00
Chris Robinson 59768674f1 Use an alternate ambisonic HRTF decode layout
Similar to the previous, but includes the top and bottom HRTF responses. The
higher-order decode (for the "basic" HRTF output) also now uses 2H1P mixed-
order instead of 2H1V, which seems more stable.
2018-02-13 03:03:26 -08:00
Chris Robinson 2cb49e51a0 Fix NULL pointer dereference 2018-02-12 22:20:43 -08:00
Chris Robinson 6b76eddbe3 Supply HF coefficient scales with a single-band ambisonic matrix 2018-02-12 21:24:58 -08:00
Chris Robinson 59934b47de Avoid using an enum for array indices 2018-02-12 20:58:39 -08:00
Chris Robinson 4d417f3dd4 Make bformatdec_free and ambiup_free clear the freed pointer 2018-02-12 20:48:28 -08:00
Chris Robinson dce497fbca Use a bitfield for enabled bformatdec channels 2018-02-12 20:33:31 -08:00
Chris Robinson e63e0ee596 Combine multiple functions called sequentially 2018-02-11 22:23:03 -08:00
Chris Robinson 494e9fd61d Don't asynchronously modify the source on disconnect 2018-02-11 16:58:34 -08:00
Chris Robinson b11e31fbfd Clear stale 'post's on the event semphaphore 2018-02-11 06:14:18 -08:00
Chris Robinson 1e93122470 Remove unused reverb modulation code
Still unsure how to handle reverb modulation without some kind of reference
output, so remove the related functions to not waste CPU time. It'll remain in
the Git history should it ever need to be resurrected.
2018-02-10 19:50:42 -08:00
Chris Robinson 5b11129eaa Use a function pointer for applying the dry mix post-process 2018-02-10 15:50:05 -08:00
Chris Robinson ee3d53a673 Use an alternate virtual layout for Ambisonic HRTF decoding
This uses 16 channels, an 8-channel octagon + 8-channel cube, which should
improve horizontal resolution without affecting vertical too much.
2018-02-10 05:16:28 -08:00
Chris Robinson c346baff5b Clarify some macro names using ambisonic mixed-mode notation 2018-02-09 18:43:34 -08:00
Chris Robinson 20bee69955 Attempt to improve the reverb panning vectors
This should now retain the original orientation of the soundfield and merely
focus on the panning vector direction, as intended.
2018-02-08 01:18:40 -08:00
Chris Robinson 4cd04f192d Alter tha curve of the density-related delay scale
The delay scale is roughly linear with respect to room size, however the
density is not linear with room size. The density is calculated by taking the
room size cubed, then normalized by some factor. Unnormalizing the density and
taking the cube root restores the original room size to use as a delay scale.

The patch also alters the delay and all-pass line lengths to be based on a 1
meter room size, so the the room size recovered from the density acts as a
direct multiple for the desired target length.

Note that the room scale range is unchanged (5m to 50m), so the minimum and
maximum delays are the same. It should also be noted that 50m may not be the
correct room size for a density value of 1. A density value of 1 corresponds to
an environment size of roughly 2.52m when converted from EAX (DENSITY_SCALE
should be 16 rather than 125000), but sizes that low result in undesirable
resonance in the feedback, indicating other changes are necessary for that to
work.
2018-02-08 00:05:29 -08:00
Chris Robinson f5f996c14a Check for a cbrtf function 2018-02-06 02:35:08 -08:00
Chris Robinson da1ee3baba Revert "Don't fade the all-pass delay changes"
This reverts commit 799dfb732b.
2018-02-04 19:57:22 -08:00
Chris Robinson 1d7a5dbede Remove unsupported source queries 2018-02-04 15:01:04 -08:00
Chris Robinson 9b878c64f9 Make the Connected state atomic
Also don't send the Disconnected event more than once.
2018-02-04 00:01:12 -08:00
Chris Robinson 1f61472e77 Avoid potentially writing partial samples 2018-02-03 14:02:47 -08:00
Chris Robinson b99e64a0e3 Provide more descriptive messages to disconnection events 2018-02-03 13:54:42 -08:00
Chris Robinson e49607078e Handle disconnected events in alffplay 2018-02-03 11:27:03 -08:00
Chris Robinson 4ef60d7214 Add a couple missing lock calls 2018-02-03 01:26:04 -08:00
Chris Robinson 40bda4d93f Add a disconnected event type 2018-02-03 01:07:06 -08:00
Chris Robinson 8f4c078fb5 Add missing header for UINT_MAX 2018-02-02 23:08:29 -08:00
Chris Robinson 80cc32d77b Remove the unused thunk code 2018-02-02 22:59:06 -08:00
Chris Robinson 28fa82378b Remove the individual source queue and buffer locks
They're inherently protected by the mutex for their respective lists. Should
those mutexes be replaced by rwlocks the individual locks should also be
reinstated, but they're unlikely to be unless a lot of contention starts
happening in the read-only case.
2018-02-02 22:24:33 -08:00
Chris Robinson 6a4a88f8f5 Store an index to a given source's voice
For more efficient voice lookups when needed.
2018-02-01 23:56:35 -08:00
Chris Robinson a114d6cbb5 Remove unused _timed methods
They're not reliably implemented anyway, as some systems will just flat out
fail when trying to use them.
2018-02-01 22:24:17 -08:00
Chris Robinson 3acd2a55ad Don't generate Buffer Completed events for static sources 2018-02-01 21:07:55 -08:00
Chris Robinson 3a90fd5751 Avoid an unnecessary temp variable 2018-02-01 19:11:23 -08:00
Chris Robinson ec14c98f2d Use an atomic instead of volatile to tell a thread to quit 2018-02-01 18:59:32 -08:00
Chris Robinson 975c682ec3 Use semaphores to signal for more samples with JACK and OpenSL 2018-02-01 18:54:13 -08:00
Chris Robinson e240351d81 Use a semaphore to signal the event handler
Semaphores allow for semi-persistent signals, compared to a condition variable
which requires a mutex for proper detection. A semaphore can be 'post'ed after
writing some data on one thread, and another thread will be able to recognize
it quickly even if the post occured in between checking for data and waiting.

This more correctly fixes a race condition with events since the mixer
shouldn't be using mutexes, and arbitrary wake-ups just to make sure an event
wasn't missed was quite inefficient.
2018-02-01 18:20:14 -08:00
Chris Robinson 4ec31291c0 Add semaphore functions to the thread API wrapper 2018-02-01 17:37:31 -08:00
Chris Robinson 7a538141c9 Signal a condition variable when a buffer completes in alffplay 2018-02-01 02:24:44 -08:00
Chris Robinson bcdc399029 Send buffer completed events when enabled 2018-02-01 01:36:03 -08:00
Chris Robinson 8652ae046b Fix check for matching event type 2018-02-01 00:50:06 -08:00
Chris Robinson 833eface38 Don't print buffer completed events in alffplay 2018-01-31 20:27:14 -08:00
Chris Robinson 7256bc92fa Add a thread to marshal events from the mixer
To avoid having unknown user code running in the mixer thread that could
significantly delay the mixed output, a lockless ringbuffer is used for the
mixer to provide events that a secondary thread will pop off and process.
2018-01-31 20:21:54 -08:00
Chris Robinson 0394d5a44f Rename EventLock to make it more clear it's protecting the callback 2018-01-30 12:34:25 -08:00
Chris Robinson e7217760f3 Don't bother with an explicit stop backend method 2018-01-29 01:00:53 -08:00
Chris Robinson a042dbf305 Call the backend close method in the destructor 2018-01-28 23:32:28 -08:00
Chris Robinson 328fd7329d Combine common initialization code into a function 2018-01-28 18:03:54 -08:00
Chris Robinson a24a22c39a Fix the effect slot limit check 2018-01-28 17:56:47 -08:00
Chris Robinson c2710ffe87 Make EnabledEvts atomic 2018-01-28 16:58:41 -08:00
Chris Robinson 782eb650c7 Use std::array instead of a plain array in alffplay 2018-01-28 13:18:33 -08:00
Chris Robinson 38a3ba74d4 Report the problem value for global state errors 2018-01-28 13:13:40 -08:00
Chris Robinson d1da9f1f67 Remove some now-unused NoLock function variants 2018-01-28 00:53:21 -08:00
Chris Robinson dcc5a10c7b Use a fixed array for the effect state factory list 2018-01-28 00:10:12 -08:00
Chris Robinson 0051ebace0 Don't bother with a return value that's never used 2018-01-27 23:50:04 -08:00
Chris Robinson 0cd61fd197 Don't allocate more effect slots than allowed 2018-01-27 23:07:29 -08:00
Chris Robinson f16ece6048 Move some inline functions into a header instead of copying them
Unfortunately does not include the Lookup* functions, which need the full type
declaration to offset the pointer.
2018-01-27 21:16:24 -08:00
Chris Robinson e12059a311 Fix error reporting for resource generation 2018-01-27 20:04:21 -08:00
Chris Robinson cb9fb31214 Store filters in an array of lists 2018-01-27 19:40:47 -08:00
Chris Robinson e8c268ef09 Store effects in an array of lists 2018-01-27 19:04:32 -08:00
Chris Robinson 6a839600b9 Use a vector to store the effect slot pointers
And make the ID a simple index into it (1-base, to avoid ID 0).
2018-01-27 19:04:32 -08:00
Chris Robinson 4d1795e90b Remove an unused function 2018-01-27 19:04:32 -08:00
kcat 277127829a Merge pull request #166 from Ybalrid/betterReadme
Better readme
2018-01-27 19:03:50 -08:00
Arthur Brainville 58fd9d70d2 Additional corrections as mentionned in the PR 2018-01-28 03:26:02 +01:00
Arthur Brainville d2cf61c99a Added AppVeyor badge 2018-01-28 02:29:54 +01:00
Arthur Brainville 6562a939ea Add project overview and travis build badge
Signed-off-by: Arthur Brainville (Ybalrid) <ybalrid@ybalrid.info>
2018-01-28 01:37:00 +01:00
Chris Robinson 031cf24880 Use an array lookup for source IDs
This is now similar to buffers, being stored in groups of 64 in a vector with
the ID providing the array indices.
2018-01-27 14:34:05 -08:00
Chris Robinson 38261a0f2a Make some more functions static where they're used 2018-01-27 14:34:05 -08:00
Chris Robinson c4a76af397 Add some casts to pacify MSVC 2018-01-27 14:34:05 -08:00
Chris Robinson f76ab02bd4 Add a ctz64 fallback using _BitScanForward when available 2018-01-27 14:34:05 -08:00
kcat 4d392a8b87 Merge pull request #165 from Ybalrid/Ybalrid-readme-patch
Reformat README for a nicer display on github
2018-01-27 14:33:38 -08:00
Arthur Brainville c969dad6c2 Reformat README for a nicer display on github 2018-01-27 23:16:36 +01:00
Chris Robinson 9718502e5d Check for _BitScanForward64 before using it 2018-01-27 11:11:39 -08:00
Chris Robinson 9613b4bfe2 Use a different method for storing and looking up buffers
Rather than each buffer being individually allocated with a generated 'thunk'
ID that's used with a uint:ptr map, buffers are allocated in arrays of 64
within a vector. Each group of 64 has an associated 64-bit mask indicating
which are free to use, and the buffer ID is comprised of the two array indices
which directly locate the buffer (no searching, binary or otherwise).

Currently no buffers are actually deallocated after being allocated, though
they are reused. So an app that creates a ton of buffers once, then deletes
them all and uses only a couple from then on, will have a bit of waste, while
an app that's more consistent with the number of used buffers won't be a
problem. This can be improved by removing elements of the containing vector
that contain all-free buffers while there are plenty of other free buffers.

Also, this method can easily be applied to other resources, like sources.
2018-01-27 01:51:01 -08:00
Chris Robinson 5d2196c119 Don't rely on alMain.h in alBuffer.h 2018-01-26 21:32:43 -08:00
Chris Robinson a885fd6d0e Make some functions static that are only used in one source 2018-01-26 21:11:12 -08:00
Chris Robinson 3ba4746ff8 Allow preserving converted samples 2018-01-26 16:41:14 -08:00
Chris Robinson e1e1c73ae6 Read the buffer unpack alignment under the buffer lock 2018-01-26 15:58:38 -08:00
Chris Robinson fbafbe6272 Improve error reporting for buffers 2018-01-26 00:13:03 -08:00
Chris Robinson 6ae7fc5df1 Add the printf-format attribute to alSetError 2018-01-25 23:52:09 -08:00
Chris Robinson 99f0377ae3 Construct error messages using parameterized values 2018-01-25 15:59:59 -08:00
Chris Robinson 932939c5a8 Enable events in alffplay 2018-01-24 20:04:57 -08:00
Chris Robinson 0c9c846522 Report AL_SOFTX_events as an in-progress extension 2018-01-24 19:59:22 -08:00
Chris Robinson f5236ab186 Use more appropriate enum values for events 2018-01-24 19:25:15 -08:00
Chris Robinson 2a7f5aa569 Add a deprecated event type for alDopplerVelocity 2018-01-24 18:47:19 -08:00
Chris Robinson 1d86aea61c Fix ordering of alGetPointervSOFT 2018-01-24 18:30:32 -08:00
Chris Robinson 2ded5547ba Provide messages for the remaining AL errors 2018-01-24 17:07:01 -08:00
Chris Robinson 395278fcdb Set the buffer load error in LoadData 2018-01-24 12:45:56 -08:00
Chris Robinson 786a05876e Call the event callback when an error is generated
Most errors don't yet provide correct object IDs or text messages for the AL
error.
2018-01-24 12:10:48 -08:00
Chris Robinson caa3b4f7f8 Handle event properties
This just implements the event methods insofar as tracked state. No events are
generated/reported yet.
2018-01-23 18:25:59 -08:00
Chris Robinson 2266a9e01e Declare the beginnings of an event extension 2018-01-23 17:42:39 -08:00
Chris Robinson def2547e40 Fix some 'may be used initialized' warnings 2018-01-23 16:03:06 -08:00
Chris Robinson 2ac0adaebb Use a new proper buffer function with a flags parameter
Rather than hackily combining bit flags with the format, to increase the number
of potential flags. alBufferData now behaves as if calling alBufferStorageSOFT
with a flags value of 0.
2018-01-23 14:33:30 -08:00
Chris Robinson 4ebb97bf73 Test mapped buffers in alffplay 2018-01-23 11:15:25 -08:00
Chris Robinson b05592b0ab Ensure read or write flags are specified with persistent mappings 2018-01-23 10:59:51 -08:00
Chris Robinson 9ee58fd454 Track the buffer's mapped section 2018-01-23 10:51:23 -08:00
Chris Robinson 4f9a0460c4 Add a flag for persistent mapping
And a function to "flush" a mapped buffer
2018-01-23 10:38:27 -08:00
Chris Robinson ad61392318 Slightly simplify alBufferSubDataSOFT 2018-01-22 16:17:45 -08:00
Chris Robinson a852ecf8ce Ensure proper alignment when preserving data too 2018-01-22 15:52:53 -08:00
Chris Robinson d6d6ec03f1 Don't bother allocating cleared memory for buffer storage 2018-01-22 15:46:46 -08:00
Chris Robinson fce72610bb Add a flag for alBufferData to non-destructively resize the data
Requires having the same format as the last call to alBufferData. Also only
makes sense when given a NULL data pointer, as otherwise the internal data will
be overwritten anyway.
2018-01-22 11:34:16 -08:00
Chris Robinson 4a368ab905 Fix the return type of the al_fwrite wrapper 2018-01-22 10:48:55 -08:00
Chris Robinson f1f7fba3b9 Don't convert/copy samples with a NULL dest buffer
Only happens with a 0 size, so there's nothing to copy or convert anyway.
2018-01-22 00:31:27 -08:00
Chris Robinson ca07e210a7 Remove now-unused alloca and VLA checks 2018-01-21 23:50:47 -08:00
Chris Robinson 5f3ae527c9 More cleanup for buffer loading
Don't bother with unnecessary and unused converters, and remove some
unsupported queries.
2018-01-21 23:35:28 -08:00
Chris Robinson db13af1935 Handle double-precision buffers in the mixer 2018-01-21 18:34:03 -08:00
Chris Robinson efd11f32a2 Remove support for (signed) byte and ushort sample storage
Also not used without buffer_samples
2018-01-21 18:16:27 -08:00
Chris Robinson 6489fb586b Remove (u)int32 sample storage conversion
Unused without the buffer_samples extension
2018-01-21 17:55:35 -08:00
Chris Robinson 427212705f Remove the old buffer_samples functions
The symbols are still there and exported to retain ABI compatibility, but they
no longer do anything except set an AL_INVALID_OPERATION error. They're also
removed from the function and enum tables, since they're not part of any
supported extension.
2018-01-21 17:19:57 -08:00
Chris Robinson f6276164f3 Fix the return type of the LPALUNMAPBUFFERSOFT typedef 2018-01-21 16:32:37 -08:00
Chris Robinson dcb934fc2e Avoid repeating some code 2018-01-21 10:32:48 -08:00
Chris Robinson c2c3846930 Expose a preliminary AL_SOFT_map_buffer extension for testing 2018-01-20 13:37:43 -08:00
Chris Robinson 03d4e4acc4 Add methods to "map" a buffer's storage
Requires the MAP_READ_BIT or MAP_WRITE_BIT flags to be OR'd with the format
upon a call to alBufferData, to enable mappable storage for the given access
types. This will fail if the format requires internal conversion and doesn't
resemble the original input data, so the app can be guaranteed the size, type,
and layout of the original data is the same as what's in storage.

Then alMapBufferSOFT may be called with appropriate bit flags to get a readable
and/or writable pointer to the buffer's sample storage. alUnmapBufferSOFT must
be called when access is finished. It is currently invalid to map a buffer that
is attached to a source, or to attach a buffer to a source that is currently
mapped. This restriction may be eased in the future, at least to allow read-
only access while in use (perhaps also to allow writing, if coherency can be
achieved).

Currently the access flags occupy the upper 8 bits of a 32-bit bitfield to
avoid clashing with format enum values, which don't use more than 16 or 17
bits. This means any future formats are limited to 24-bit enum values, and also
means only 8 flags are possible when declaring storage. The alternative would
be to add a new function (alBufferStorage?) with a separate flags parameter.
2018-01-20 11:49:01 -08:00
Chris Robinson 4e647bda07 Return the effective alignment from SanitizeAlignment 2018-01-19 21:00:53 -08:00
Chris Robinson ab2295b68f Store 8-bit sample types directly as unsigned byte 2018-01-19 20:10:31 -08:00
Chris Robinson 940c6146e8 Remove unnecessary private AL_SOFT_buffer_samples2 definitions 2018-01-19 19:28:23 -08:00
Chris Robinson b56673bbce Make a function static that's only used in one source file 2018-01-17 18:43:23 -08:00
Chris Robinson 2102625021 Reformat the format array 2018-01-17 18:03:31 -08:00
Chris Robinson 884fe40fd1 Store mulaw and alaw samples directly in the buffer
They're now decompressed on the fly in the mixer. This is not a significant
performance issue given that it only needs a 512-byte lookup table, and the
buffer stores half as much data (it may actually be faster, requiring less
overall memory).
2018-01-17 08:49:49 -08:00
Chris Robinson 3baf9d0e81 Add a method to get the system's page size 2018-01-16 19:28:10 -08:00
Chris Robinson 248832b266 Use a voice flag to indicate it being static 2018-01-16 18:07:59 -08:00
Chris Robinson bf8c889631 Define DECL_VLA where it's used 2018-01-16 12:57:06 -08:00
Chris Robinson 273dca2fa0 Avoid using alloca in the CoreAudio backend 2018-01-16 12:54:20 -08:00
Chris Robinson e80b016cbe Use a global RowMixerFunc 2018-01-16 12:18:59 -08:00
Chris Robinson 8c19497340 Properly cast some parameters for MSVC 2018-01-16 09:40:34 -08:00
Chris Robinson 5deb1df8db Add min/max/clamp functions for size_t 2018-01-16 09:39:28 -08:00
Chris Robinson 14a0df03e8 Do more samples at once with the distortion effect 2018-01-16 08:49:15 -08:00
Chris Robinson bc44efb91d Use ALsizei for some count/length variables 2018-01-15 18:31:43 -08:00
Chris Robinson 799dfb732b Don't fade the all-pass delay changes 2018-01-15 18:02:11 -08:00
Chris Robinson 813b06a3a4 Fix a comment regarding the T60 filter coefficients 2018-01-15 09:22:28 -08:00
Chris Robinson 47c9b0a17e Simplify preparing the T60 filters 2018-01-15 09:17:15 -08:00
Chris Robinson 44795b8614 Assign the HRTF storage pointers separately 2018-01-15 08:38:25 -08:00
Chris Robinson 5acb2e5165 Remove alffplay's duplicate ALC_SOFT_device_clock declarations 2018-01-15 07:14:35 -08:00
Chris Robinson 682205a3d4 Finalize ALC_SOFT_device_clock 2018-01-15 06:45:53 -08:00
Chris Robinson 7441311e3d Use separate input and outputs for the T60 and allpass processing 2018-01-15 05:37:00 -08:00
Chris Robinson 6f48dac4cc Use a proper type for the T60 decay filter 2018-01-15 05:23:35 -08:00
Chris Robinson bc078e5158 Separate some anonymous structs into defined structs 2018-01-14 15:42:25 -08:00
Chris Robinson 63416bf28f Avoid unnecessarily using type aliases 2018-01-14 09:02:59 -08:00
Chris Robinson 0152bc0d10 Remove the SAFE_CONST macro
Seems compilers are now allowing a pointer-to-type-array to implicitly convert
to pointer-to-const-type-array.
2018-01-14 08:51:03 -08:00
Chris Robinson 7e491ef2b2 Remove a redundant lower value clamp 2018-01-14 08:25:36 -08:00
Chris Robinson 6f532fa0e5 Don't check the modulation depth for changes multiple times 2018-01-14 07:33:53 -08:00
Chris Robinson 25b3ed3532 Use separate arrays for the T60 filter states 2018-01-14 07:29:18 -08:00
Chris Robinson 7a77a20a67 Use a macro for the number of reverb lines 2018-01-14 07:25:02 -08:00
Chris Robinson 2957d85924 Only pass the needed parameters to LateT60Filter 2018-01-14 05:24:23 -08:00
Chris Robinson 2b968766b9 Pre-apply the T60 filter midgain to the HF filter coeffs 2018-01-13 09:58:47 -08:00
Chris Robinson 78cb70a5f9 Replace some freq_mult variable names with f0norm
The latter is a bit more descriptive as f0 is often used to denote the
reference frequency of a filter, so f0norm indicates the normalized reference
frequency (ref_freq / sample_rate).
2018-01-13 09:14:46 -08:00
Chris Robinson 16e4e0fa7c Use a more normal vtable setup for filter methods 2018-01-13 08:07:03 -08:00
Chris Robinson c031b3cc6a Avoid fixed-PATH_MAX-size buffers
Windows still needs to use MAX_PATH in a couple places, but that macro's
guaranteed there.
2018-01-13 04:40:20 -08:00
Chris Robinson bbc4ebecab Use GetProcBinary to get the executable name for PulseAudio 2018-01-13 03:41:56 -08:00
Chris Robinson 71ad3b1715 Use KERN_PROCARGS to get the process path+filename on FreeBSD 2018-01-13 03:38:10 -08:00
Chris Robinson af8bbefcab Support procfs on *BSD to get the process binary 2018-01-13 03:22:41 -08:00
Chris Robinson 26043269e4 Allow GetProcPath to return the filename also 2018-01-13 02:59:21 -08:00
Chris Robinson c9edf7cf78 Add a string function to copy a wide-char range 2018-01-13 02:03:13 -08:00
Chris Robinson 77910afe57 Make NumAttrsForDevice do similar type checks as alcGetIntegerv 2018-01-12 11:34:23 -08:00
Chris Robinson 4db1328bc5 Move the FORCE_ALIGN macro to threads.h 2018-01-12 03:55:33 -08:00
Chris Robinson e634564b26 Make a couple functions inline 2018-01-12 02:37:48 -08:00
Chris Robinson dae5faedb0 Use atomic variables in place of volatile 2018-01-11 23:28:15 -08:00
Chris Robinson 1f236d8f20 Define a function where it's used 2018-01-11 22:42:25 -08:00
Chris Robinson 370817ba60 Move logging declarations to a separate header 2018-01-11 20:17:02 -08:00
Chris Robinson 2873abcbc0 Fix up some types for MSVC 2018-01-11 10:55:35 -08:00
Chris Robinson e89c183231 Avoid including alMain.h in ringbuffer.c 2018-01-11 10:03:26 -08:00
Chris Robinson ca9e6a4f94 Ensure NextPowerOf2 is being used correctly 2018-01-11 09:53:52 -08:00
Chris Robinson b11131ce0c Move a forward declaration to the others 2018-01-11 09:43:45 -08:00
Chris Robinson 2d2ca1d791 Remove SET_VTABLE1 2018-01-11 09:39:52 -08:00
Chris Robinson 85a8f965e5 Clean up the EffectList members and make the list size known 2018-01-11 09:34:01 -08:00
Chris Robinson 88545ccae5 Move the EffectList array to alEffect.c/h 2018-01-11 09:25:17 -08:00
Chris Robinson 3832b25f30 Move the ringbuffer declarations to a separate header
And rename alcRing.c to ringbuffer.c for consistency.
2018-01-11 09:16:28 -08:00
Chris Robinson 15ad5245bf Move the FPU mode declarations to a separate header
Also don't use inheritance with FPUCtl.
2018-01-11 08:44:52 -08:00
Chris Robinson 8aa9e35f8c Move the config function declarations to their own header
And rename alcConfig.c to alconfig.c for consistency.
2018-01-11 07:56:54 -08:00
Chris Robinson 81b13f78ea Move the CPU capability flags to a separate header 2018-01-11 07:19:19 -08:00
Chris Robinson 9b9ec2c21a Move the compressor/limiter declarations to their own header 2018-01-11 06:50:53 -08:00
Chris Robinson f3c9bc114c Move the polymorphic/inheritance macros to a separate header 2018-01-11 06:32:45 -08:00
Chris Robinson ff231b42ff Reorder some loops in the equalizer and use MixSamples 2018-01-11 05:03:00 -08:00
Chris Robinson 9c33f4aea8 Use MixSamples for the dedicated and ring modulator effect output 2018-01-11 04:48:18 -08:00
Chris Robinson f55583330b Apply the distortion gain to the mixing gains 2018-01-11 04:34:51 -08:00
Chris Robinson 2c8e4467c3 Move some HRTF structures to hrtf.h 2018-01-11 03:53:25 -08:00
Chris Robinson 15f9d15ba0 Avoid using macros to access anonymous structures 2018-01-11 03:45:23 -08:00
Chris Robinson 279799ad70 Don't return whether the bsinc filter cuts or not 2018-01-10 19:20:58 -08:00
Chris Robinson ea6b384980 Make a function pointer static 2018-01-10 19:13:41 -08:00
Chris Robinson ef63ec3fe9 Use one macro to handle both resample padding sizes 2018-01-09 23:55:59 -08:00
Chris Robinson de8c5b1824 Combine the chorus and flanger processing functions
Given that they're nearly identical, it should be relatively simple to use the
same effect state to process either of them, similar to the reverbs. The big
differences seem to be the delay range (much shorter with flanger) and the
defaults.
2018-01-09 23:21:16 -08:00
Chris Robinson 9e2eb5dc23 Rename the device's temp buffer storage to be more generic 2018-01-09 22:01:46 -08:00
Chris Robinson 5d1207104a Remove standard reverb specific processing functions 2018-01-07 22:48:03 -08:00
Chris Robinson d547f52d8f Move the UNEXPECTED macro to the main header and rename it 2018-01-07 22:20:08 -08:00
Chris Robinson 56e9cb69d3 Ensure the chorus delay and depth leave enough padding for resampling
Also use cubic resampling for the modulated tap. Applies to flanger too.
2018-01-07 21:15:18 -08:00
Chris Robinson 70973035de Use a separate function to get the cubic value 2018-01-07 17:24:29 -08:00
Chris Robinson c8a30592e8 Only print the time in alffplay when it changes 2018-01-07 17:23:51 -08:00
Chris Robinson d8258984b4 Use a -40dB drop for bsinc12
This improves the transition width at the cost of slightly more audible high-
frequency noise (nothing compared to linear or cubic, but still some). The
previous transition band caused a noticeable loss in higher frequencies, making
lower sample rate sources sound exceptionally dull or muffled.
2018-01-07 06:43:35 -08:00
Chris Robinson c423b6c8b7 Remove the sinc4 table 2018-01-07 05:58:52 -08:00
Chris Robinson 4cc1c64646 Replace the sinc4 resampler with cubic
Turns out the C version of the cubic resampler is just slightly faster than
even the SSE3 version of the FIR4 resampler. This is likely due to not using a
64KB random-access lookup table along with unaligned loads, both offseting the
gains from SSE.
2018-01-07 05:32:07 -08:00
Chris Robinson 0e1fd34c89 Fix a temp buffer leak in alffplay 2018-01-05 20:41:55 -08:00
Chris Robinson 10f2531b6c Handle the audio clock diff as nanoseconds in alffplay 2018-01-05 05:41:57 -08:00
Chris Robinson ecd327e5e1 Test the correct offset for the loop start range 2018-01-04 21:21:48 -08:00
Chris Robinson 510bccb6ac Use the correct start point when looping 2018-01-04 20:21:46 -08:00
Chris Robinson 32330c5eef Use separate outputs for the vector reverse and scatter functions 2018-01-04 20:04:39 -08:00
Chris Robinson e00ef16893 Avoid the modulated output tap for late reverb
And fix the output filtering. The modulation code is still there since it's
(probably) technically correct, but the interaction with the feedback loop and
filtering on the output caused improper behavior which needs to be sorted out.
2018-01-04 19:17:16 -08:00
Chris Robinson fd8143fea0 Use a non-recursive mutex for alffplay's source lock 2018-01-03 19:05:25 -08:00
Chris Robinson 08a4840298 Don't offset the reverb modulation sinus 2018-01-02 19:52:57 -08:00
Chris Robinson a16f933e3a Use a float literal for float math 2018-01-02 19:44:31 -08:00
Chris Robinson 9d61f429a5 Use ALC_SOFT_device_clock in alffplay 2018-01-02 19:32:28 -08:00
Chris Robinson b8de63d608 Reset CompLen when loading loop repeats in the mixer 2018-01-02 19:29:58 -08:00
Chris Robinson dcc0f6e25d Improve starting synchronization in alffplay
It waits until the internal buffers are filled before starting playback, then
triggering the audio and video to start as close together as possible.
2018-01-02 03:31:12 -08:00
Chris Robinson 21684fac69 Inline and simplify some code 2018-01-01 22:28:28 -08:00
Chris Robinson 329333ca8b Don't leak the AVIOContext 2018-01-01 19:16:05 -08:00
Chris Robinson 91e20dc12e Fix a couple alffplay comments still referencing Alure 2018-01-01 18:54:15 -08:00
Chris Robinson aecfa0b426 Make smart pointer wrappers for some ffmpeg types 2018-01-01 18:47:48 -08:00
Chris Robinson 9675068706 Print the running time and duration in alffplay 2018-01-01 16:15:32 -08:00
Chris Robinson cbd55ad31e Use aliases to simplify some time type names 2018-01-01 02:26:39 -08:00
Chris Robinson 5eb1753d31 Wrap av_gettime to ensure it's interpreted as microseconds 2018-01-01 01:39:28 -08:00
Chris Robinson e277f5e268 Rename the do_direct_out variable 2018-01-01 01:14:53 -08:00
Chris Robinson 145bf52b7a Use a less-intense memory order to check for quitting 2018-01-01 01:13:32 -08:00
Chris Robinson fe7bdc93eb Check for AL_SOFT_direct_channels once when initializing 2018-01-01 01:07:29 -08:00
Chris Robinson d889435334 Declare the total buffer time instead of buffer count 2018-01-01 01:04:47 -08:00
Chris Robinson dad17a7d91 Use a std::array for the buffers in alffplay 2018-01-01 00:32:27 -08:00
Chris Robinson 830adf6ae8 Use inline member initialization where possible 2018-01-01 00:17:40 -08:00
Chris Robinson babfa70cab Make more values global const 2018-01-01 00:02:51 -08:00
Chris Robinson 258eadd003 Use a global to specify alffplay's audio sync threshold 2017-12-31 23:44:33 -08:00
Chris Robinson 6dd1643a70 Avoid dereferencing a NULL pointer
Even though it's taking the address of a member, it's still technically a
derefernce and thus undefined behavior. sizeof doesn't "execute" the
expression, so derefering in it instead is fine.
2017-12-31 23:19:41 -08:00
Chris Robinson 48916ebd76 Clear the buffer queue when underrun 2017-12-31 16:29:52 -08:00
Chris Robinson 506d316f55 Wake up the packet send loop when more frames are needed 2017-12-31 16:11:00 -08:00
Chris Robinson 490b71378e Readd AL_SOFT_source_latency support to alffplay 2017-12-30 23:12:19 -08:00
Chris Robinson 0e8b0c520e Use standard duration types for stream clocks 2017-12-30 22:46:09 -08:00
Chris Robinson 0136df1e62 Improve the alffplay queue for FFmpeg's send/receive API
The packet handling thread now calls avcodec_send_packet to give compressed
data to libavcodec, while the audio/video threads call avcodec_receive_frame to
handle decoded frames. The packet thread still maintains local queues for each
stream to avoid starving an A/V thread when the other doesn't want another
frame yet.
2017-12-30 20:34:33 -08:00
Chris Robinson 98731bb574 Update resampler padding checks 2017-12-29 16:28:49 -08:00
Chris Robinson 27dfb7fd11 Fix up a comment 2017-12-24 20:41:20 -08:00
Chris Robinson 6457df54bb Don't make a pass-through macro for FadedDelayLineOut 2017-12-24 20:41:20 -08:00
Chris Robinson 07f3ed0419 Use linear interpolation for reverb modulation
The core LateReverb_* functions are explicitly written out now, since the
tapping and blending done by the Faded version is a bit more complex and it's
not so easy to ensure proper optimizing on the Unfaded version.
2017-12-24 20:40:53 -08:00
Chris Robinson dabb99de8d Don't offset the chorus/flanger delay and LFO
The delay being added to the scaled LFO will ensure a proper positive result
regardless.
2017-12-24 20:40:33 -08:00
Chris Robinson 254ebe5f96 Fade between depths in the reverb modulator 2017-12-24 16:23:30 -08:00
Chris Robinson b32a366137 Use a separate unmodulated feedback tap for reverb 2017-12-24 14:21:13 -08:00
Chris Robinson 3633b65e04 Only link to the common lib when building as shared 2017-12-23 21:23:32 -08:00
Chris Robinson ce74098b40 Clarify some reverb values 2017-12-23 21:13:57 -08:00
Chris Robinson 5769efe48e Reorder some global variables 2017-12-23 18:50:38 -08:00
Chris Robinson 02051ab51a Cleanup reverb modulation scaling 2017-12-23 14:18:14 -08:00
Chris Robinson 02c0620141 Add missing integer queries 2017-12-21 11:46:01 -08:00
Chris Robinson d7895db166 Fix the lfo_offset for a 0-rate flanger 2017-12-19 19:15:34 -08:00
Chris Robinson 0f84e32520 Use MixSamples for the echo output 2017-12-19 15:13:11 -08:00
Chris Robinson 1a911be617 Update the chorus and flanger state struct less often 2017-12-19 15:12:06 -08:00
Chris Robinson a0565dc9bb Make the echo effect only apply feedback to repeated samples 2017-12-19 14:11:37 -08:00
Chris Robinson 52569ef562 Fade gains in the chorus and flanger output 2017-12-19 13:52:05 -08:00
Chris Robinson 661bd054aa Use a single delay line for chorus feedback on a fixed tap
The outputs themselves use a variale-delay tap, but using a separate fixed-
delay tap on the feedback helps improve the perceived "wobble" with sustained
notes. This also applies to the flanger effect.
2017-12-18 13:41:12 -08:00
Chris Robinson eee4aca40b Apply chorus and flanger feedback on the tapped re-feed 2017-12-17 22:14:20 -08:00
Chris Robinson 04cf832fe6 Use the selected mixer for chorus and flanger output 2017-12-17 22:10:58 -08:00
Chris Robinson 2346426b6e Make MixSamples non-static global 2017-12-17 21:48:07 -08:00
Chris Robinson 8253014fe9 Fix some types to make MSVC happy 2017-12-17 15:56:30 -08:00
Chris Robinson 57e516720e Avoid a potential calloc of 0 2017-12-17 14:30:51 -08:00
Chris Robinson d229afb83d Build common code once 2017-12-16 15:53:24 -08:00
Chris Robinson 3cd1f30577 Use the right path for android's cmake toolchain 2017-12-16 15:15:11 -08:00
Chris Robinson 94347d4e64 Update travis for android-ndk-r15, required for its newer cmake 2017-12-16 15:10:20 -08:00
Chris Robinson 6fe6c370c2 Include the fpu=neon switch when testing for arm_neon.h 2017-12-16 15:01:05 -08:00
Chris Robinson 42acafcbc3 Don't cache the Android NDK in Travis 2017-12-16 14:05:13 -08:00
Chris Robinson ebd42ab619 Mix multiple buffers in each buffer list item
Basically now this just relies on being able to specify composited buffers.
2017-12-16 11:34:52 -08:00
Chris Robinson b10780f119 Pre-clear the source temp buffer and accumulate into it 2017-12-16 09:41:27 -08:00
Chris Robinson d657c51be8 Rename SrcDataSize to be less confusing 2017-12-16 08:58:46 -08:00
Chris Robinson 30007263e5 Allow storing multiple buffers in a ALbufferlistitem
This will be to allow buffer layering, multiple buffers of the same format and
sample rate that are mixed together prior to resampling, filtering, and
panning. This will allow composing sounds from individual components that can
be swapped around on different invocations (e.g. layer SoundA and SoundB on one
instance and SoundA and SoundC on a different instance for a slightly different
sound, then just SoundA for a third instance, and so on). The longest buffer
within the list item determines the length of the list item.

More work needs to be done to fully support it, namely the ability to specity
multiple buffers to layer for static and streaming sources. Also the behavior
of loop points for layered static sources should be worked out. Should also
consider allowing each layer to have a sample offset.
2017-12-15 22:59:51 -08:00
Chris Robinson ff3d31e17f Update flanger with the same changes as chorus 2017-12-15 17:35:46 -08:00
Chris Robinson d281b25723 Use a separate LFO offset in the chorus effect
Given that the LFO range is not a power-of-two, it won't correctly wrap on
overflow.
2017-12-15 17:26:03 -08:00
Chris Robinson 2a3b5ab9e9 Use linear interpolation for the chorus delay output 2017-12-15 16:50:06 -08:00
Chris Robinson d9f57c099b Use the correct functions set to the compiler switches 2017-12-15 12:25:50 -08:00
Chris Robinson 32f72c7471 Add queries to get the source offset with the device clock 2017-12-03 14:45:19 -08:00
Chris Robinson 5993ef0baa It's getFactory that may be NULL, not its return value... 2017-11-26 09:59:55 -08:00
Chris Robinson d96be76875 Don't probe a NULL backend factory 2017-11-26 09:53:26 -08:00
Chris Robinson 0e2e9e9a29 Add a simple non-streaming play example 2017-11-07 23:12:12 -08:00
Chris Robinson 7c88e4a16b Properly initialize with the default distance model 2017-10-29 18:25:04 -07:00
Chris Robinson eac0539ea0 Enable NFC filters for HRTF
Only applies to the Ambisonic mix (basic HRTF rendering, or B-Format buffers).
2017-10-23 13:30:01 -07:00
Chris Robinson b82d2cf055 Store the HRTF distance in the Hrtf handle 2017-10-23 13:26:35 -07:00
Chris Robinson 7d4028b2df Update default HRTFs for 24-bit samples
This is still using the (mono) KEMAR set, although perhaps the IRCAM 1005 set
could be made the default (stereo and smaller).
2017-10-22 15:45:30 -07:00
Chris Robinson 0349bcc500 Update mhr format for 24-bit, multi-field, stereo measurements
Currently only single field HRTFs are supported, but the format now allows up
to 16.
2017-10-22 15:36:42 -07:00
Chris Robinson 2f5b86dd38 Add an "un-exposed" method to get the library version
This reports the same ALSOFT version as alGetString(AL_VERSION), but doesn't
require a current context (which requires a ALCdevice) to call. Do *NOT* use
this version to determine feature support, use the standard interfaces. If you
think you need to use this, you probably don't, and shouldn't.
2017-10-16 05:47:12 -07:00
Chris Robinson 5ec11a017c Add casts for assigning the SSE bsinc filter pointers 2017-10-07 15:28:35 -07:00
Chris Robinson b3f7df6f5b Use a typedef to declare extern atomic variables
Some systems use anonymous structs for atomic storage, and extern declarations
need to have the same type as their non-extern definition.
2017-10-07 14:58:35 -07:00
Chris Robinson d57eca57a9 Use _wfopen_s to silence MSVC security warnings 2017-10-07 14:48:07 -07:00
Chris Robinson 71ce90d083 Don't hide the log2 macro behind an Android-only macro 2017-10-07 14:39:41 -07:00
Chris Robinson 296abf03de Avoid a separate function to query ambisonic mode support
Now FuMa and ACN channel orders are required, as are FuMa, SN3D, and N3D
normalization schemes. An integer query (alcGetIntegerv) is added for the
maximum ambisonic order.
2017-09-27 11:58:36 -07:00
Chris Robinson 1ab8902621 Re-update effect slots when context properties change
Also keep all free property update structs together in the context instead of
per-object.
2017-09-27 11:13:18 -07:00
Chris Robinson fd70b0bca6 Don't update context and listener props unnecessarily 2017-09-27 09:36:34 -07:00
Chris Robinson 101d284a18 Update the context state properties separately
The context state properties are less likely to change compared to the listener
state, and future changes may prefer more infrequent updates to the context
state.

Note that this puts the MetersPerUnit in as a context state, even though it's
handled through the listener functions. Considering the infrequency that it's
updated at (generally set just once for the context's lifetime), it makes more
sense to put it there than with the more frequently updated listener
properties. The aforementioned future changes would also prefer MetersPerUnit
to not be updated unnecessarily.
2017-09-27 08:55:42 -07:00
Chris Robinson 2f66139053 Update version for 1.18.2 release
Note the real release is in the v1.18 branch! This is just for numbering
consistency.
2017-09-24 07:06:50 -07:00
Chris Robinson 8e64adb2d3 Update ChangeLog with JACK fix 2017-09-23 15:11:07 -07:00
Chris Robinson c5a917b401 Restore the original JACK message callback when possible 2017-09-23 03:56:45 -07:00
Chris Robinson 27ab921e5b Update ChangeLog 2017-09-22 05:52:27 -07:00
Chris Robinson 369f52a0d7 Add an option to ignore the app's speed of sound for reverb decay 2017-09-22 05:42:04 -07:00
kcat 9007b77355 Merge pull request #149 from dscharrer/master
Fix build on Gentoo FreeBSD with freebsd-lib 9.1
2017-09-21 10:28:04 -07:00
Chris Robinson fc9cb2fbd8 Use the app-specified speed of sound for reverb decay
Specifically, the initial reverb decay as determined by the source distance,
and the reverb decayhf limit from air absorption.
2017-09-21 10:20:59 -07:00
Daniel Scharrer 46b9efc5a2 Fix build on Gentoo FreeBSD with freebsd-lib 9.1 2017-09-21 17:37:10 +02:00
Chris Robinson 90cedbea49 Pass the context to the auxiliary effect update method 2017-09-21 05:42:35 -07:00
Chris Robinson 0b243f1aaf Update ChangeLog 2017-09-19 10:47:22 -07:00
Chris Robinson bc386af5c5 Manually save and restore the FPU rounding mode on Windows
Apparently there is a bug with at least MinGW-W64 where fegetenv and fesetenv
do not properly save and restore the FPU rounding mode, resulting in the
rounding mode remaining as round-to-zero after certain function calls. I do not
know if this also affects MSVC, but better safe than sorry for now.
2017-09-19 03:42:00 -07:00
Chris Robinson 4ca8b4080a Always link to ossaudio when found 2017-09-15 22:40:51 -07:00
Chris Robinson c7273ada8e Handle libossaudio as an optional OSS library 2017-09-15 22:22:45 -07:00
Chris Robinson 724d6267c8 Add a check for pthread_setname_np with three parameters
As found in NetBSD.
2017-09-15 22:09:37 -07:00
Chris Robinson 653edd4b02 Don't hide -msse and -mfpu=neon checks behind a not-msvc check
Apparently Clang gets reported as being MSVC on Windows, but still needs the
GCC switches to enable SSE code generation.
2017-08-30 19:14:59 -07:00
Chris Robinson 0b0ae75ccf I guess -1 isn't allowed for the output 2017-08-30 18:04:04 -07:00
Chris Robinson 226efffd21 Free the args returned by CommandLineToArgvW 2017-08-30 17:08:38 -07:00
Chris Robinson 88d76bf069 Depend on native-tools sources using IMPLICIT_DEPENDS 2017-08-30 16:38:07 -07:00
Chris Robinson 67f183f206 Avoid using wmain on Windows 2017-08-30 16:33:44 -07:00
Chris Robinson 0408f9b7df Pass the current cmake generator to the native-tools build 2017-08-30 14:47:13 -07:00
Chris Robinson e3d99412a2 Include limits.h where INT_MAX is used 2017-08-30 12:01:49 -07:00
Chris Robinson cd15b1775e Avoid some extraneous load calls
This likely doesn't change anything given a working optimizer, but it cleans up
the code some.
2017-08-30 11:30:19 -07:00
Chris Robinson 2916efee21 Automatically generate the bsinc table when building
This makes bsincgen a native tool like bin2h, so it can run automatically when
compiling.
2017-08-28 10:31:23 -07:00
Chris Robinson 6c367cad6e Ensure some macros have the correct size 2017-08-28 05:56:57 -07:00
Chris Robinson fde02abc35 Rename resampler labels 2017-08-27 10:47:04 -07:00
Chris Robinson fdce192aab Add bsinc24 to alsoft-config 2017-08-27 10:38:33 -07:00
Chris Robinson a4d357de06 Add a higher quality bsinc resampler using 24 sample points
This improves the transition width, allowing more of the higher frequencies
remain audible. It would be preferrable to have an upper limit of 32 points
instead of 48, to reduce the overall table size and the CPU cost for down-
sampling.
2017-08-27 10:16:36 -07:00
Chris Robinson 773d4664ff Properly open the output file for writing 2017-08-27 06:40:39 -07:00
Chris Robinson 72e3398baf Avoid including AL headers in makehrtf 2017-08-27 06:23:31 -07:00
Chris Robinson f1bbf2e48a Use a common header for Unicode-awareness on Windows 2017-08-27 06:01:31 -07:00
Chris Robinson bfcde9ae8a Allow specifying the output filename with bsincgen 2017-08-26 04:43:16 -07:00
Chris Robinson b1ff2a05af Always declare a native-tools build target 2017-08-26 02:21:50 -07:00
Chris Robinson 561e95528f Rename the bsinc resampler to bsinc12 2017-08-25 05:52:19 -07:00
Chris Robinson 9ea32713b5 Constify some pointers 2017-08-23 23:01:11 -07:00
Chris Robinson 3d76e1c514 Parameterize the filter order and rejection in bsincgen 2017-08-23 03:32:47 -07:00
Chris Robinson 61097d467c Parameterize the bsinc table name 2017-08-22 00:52:17 -07:00
Chris Robinson 46f18ba114 Install the itu5.1-nocenter.ambdec preset 2017-08-21 06:03:49 -07:00
Chris Robinson ef7ce82828 Properly postfix the filter order number 2017-08-21 04:54:49 -07:00
Chris Robinson 921a820867 Meters per unit can't be 0 2017-08-21 00:30:14 -07:00
Chris Robinson e7c4681e9a 0 meters per unit is invalid 2017-08-21 00:27:52 -07:00
Chris Robinson 877ecda14d Properly check if a pointer is different when cleaning it up 2017-08-20 21:23:25 -07:00
Chris Robinson 079e4b1edb Fix -l switch 2017-08-20 18:39:26 -07:00
Chris Robinson 65bcbe0dc8 Clarify argument errors in makehrtf 2017-08-20 18:38:22 -07:00
Chris Robinson bbabc9705e Ensure the arglist is properly terminated 2017-08-20 18:16:52 -07:00
Chris Robinson 520938da57 Print the input and output filenames in makehrtf 2017-08-20 18:00:52 -07:00
Chris Robinson 97d09b4cee Remove the -m command from makehrtf
It only ever outputs mhr files, so there's no need to have a command for it. If
another output format is ever made, it can be a normal option flag.
2017-08-20 16:54:08 -07:00
Chris Robinson 0a147693e8 Properly add getopt.c to makehrtf 2017-08-20 04:46:45 -07:00
Chris Robinson 55643d6370 Use getopt to handle options in makehrtf 2017-08-20 04:30:53 -07:00
Chris Robinson 051828344e Improve unicode handling for makehrtf
Command line parameters and filenames are now unicode-aware (the .def files
should be UTF-8 encoded, if they contain any non-ASCII-7 characters). Unicode
characters might not display correctly in the console, but it should process
them correctly.
2017-08-20 01:50:27 -07:00
Chris Robinson 3e56e7f562 Properly clear the extra ChannelsPerOrder entries 2017-08-19 23:52:24 -07:00
Chris Robinson e8e1a05f38 Add a function to multiply a complex with a scalar 2017-08-18 20:58:58 -07:00
Chris Robinson 5f50d085ad Pass the filter entry to apply to resample_fir4 2017-08-18 19:20:30 -07:00
Chris Robinson f75020da5a Show progress when processing HRTFs 2017-08-18 19:00:42 -07:00
Chris Robinson 75bf45376c Use a proper complex number types in makehrtf 2017-08-18 04:32:55 -07:00
Chris Robinson d2e485cc6d Simplify bsincgen's Kaiser function 2017-08-18 03:09:36 -07:00
Chris Robinson fdd5070c3b "Unfix" the filter length calculation
Partially reverts 3f3a3ac4f1. The l*2 + 1 is
correct when you want an odd number of sample points, which avoids an
unnecessary phase offset in the fitler. However, the rounding is still needed
to calculating the left offset (l), or else the transition width can increase
with an odd-numbered order.
2017-08-18 02:48:32 -07:00
Chris Robinson 5048956ff0 Keep bsinc filter quality more consistent between scales
This generates the filters using the proper size and scale. The 'a' divisor
should represent the +/- sample range (and thus be a whole number), with the
number of sample points being double that. Increasing the filter size to a
multiple of 4 (for SIMD) can be done by padding in 0s afterward.
2017-08-17 23:21:41 -07:00
Chris Robinson 080c076643 Workaround Android not having log2 2017-08-17 15:51:42 -07:00
Chris Robinson 3f3a3ac4f1 Properly calculate the filter size 2017-08-17 05:53:16 -07:00
Chris Robinson 9fd3e5f529 Correct the bsinc filter order
Despite the claim that it was an 11th order filter, the transition width was
generated by specifying 12th order. A 12th order filter would need 14 sample
points rather than the 12 it had.
2017-08-17 05:51:18 -07:00
Chris Robinson e0408d8edf Make the sinc4 table static 2017-08-16 18:19:04 -07:00
Chris Robinson 5008024e73 Store the sinc4 table in the filter state
Also rename the resampler functions to remove the unnecessary '32' token.
2017-08-16 18:09:53 -07:00
Chris Robinson f9c09cc845 Simplify bsinc filter storage in the filter state
Rather than storing individual pointers to filter, scale delta, phase delta,
and scale  phase delta entries, per phase index, the new table layout makes it
trivial to access the per-phase filter and delta entries given the base offset
and coefficient count.
2017-08-16 02:45:25 -07:00
Chris Robinson 520dd5c779 Make the bsinc table layout more efficient
The old layout separated filters, scale deltas, phase deltas, and scale phase
deltas into separate segments that each contained a numbers of scale and phase
entries, Since processing a sample needed a filter and one of each delta entry
relating to a particular scale and phase, the memory needed would be spread
across the whole table. And since subsequent samples would use a different
phase, it would jump around the table a whole lot as well.

The new layout packs the data in a way more consistent with its use. The
filters, scale deltas, phase deltas, and scale phase deltas are interleaved,
such that for a particular scale and phase, the filter and delta entries used
are contiguous. And the phase entries for a particular scale are kept together,
so the ~500 to ~1000 samples processed per source update stay within the same
3KB to 6KB area of the 70+KB table, which is much more cache friendly.
2017-08-16 02:05:10 -07:00
Chris Robinson 4dd53ab942 Keep bsinc info together in a struct 2017-08-15 04:15:50 -07:00
Chris Robinson 0604b00360 Check the source type once when mixing 2017-08-14 22:58:24 -07:00
Chris Robinson a931314d3f Add restrict to a few more parameters 2017-08-10 00:14:55 -07:00
Chris Robinson 2987c87645 Check the right size for the in-progress mhr update 2017-08-09 20:42:22 -07:00
Chris Robinson 649bf575e2 Declare that a couple arrays are aligned 2017-08-09 14:57:01 -07:00
Chris Robinson ad3c4b81e2 Add experimental support for 24-bit, dual-ear HRTFs
Currently makehrtf only handles 24-bit output, not dual-ear, and only when
given the --experimental switch. Files produced this way will not be guaranteed
future compatibility. When the mhr format is also updated with multi-distance
measurements, the experimental switch can go away.
2017-08-08 20:30:26 -07:00
Chris Robinson 530002e168 Avoid re-selecting the direct HRTF mix function 2017-08-07 01:38:26 -07:00
Chris Robinson 6eadccc99d Replace makehrtf's dither
This uses a custom RNG instead of relying on the system-dependent rand(). It
also removes the high-pass/noise-shaping, to flatten the noise floor.
2017-08-01 23:43:25 -07:00
Chris Robinson 8a735d0ba9 Add a front-stablizer config option for surround sound modes
This improves a stereo (front-left + front-right) sound "image" by generating a
front-center channel signal. Done correctly, it helps reduce the comb effects
and phase errors associated with using only two speakers to simulate center
sounds.

Note that it shouldn't be used if the front-center channel is already included
in the positional audio mix (the dialog effect is okay). In general, it may
actually be better to exclude the front-center channel from the positional
audio mix and use this to generate front-center output.
2017-07-31 23:49:48 -07:00
Chris Robinson 88c0d22e7c Don't bother returning the IR length for B-Format decoding 2017-07-31 01:20:42 -07:00
Chris Robinson e5431bbef7 Print an error if the input channel isn't in the device
Rather than the other way around, if a device channel isn't in the channel map.
2017-07-31 01:11:36 -07:00
Chris Robinson de51ba088e Update default 5.1 and 6.1 coefficients to exclude front-center
Not all speaker kits have a front-center speaker capable of outputing full-
range content. It's best to err on the side of caution and not include front-
center for normal positional sound by default, leaving it instead for the
dedicated dialog effect.
2017-07-30 23:39:06 -07:00
Chris Robinson bf9c36408a Release 1.18.1 2017-07-29 22:09:21 -07:00
Chris Robinson 12db67f548 Cleanup output write functions 2017-07-27 19:07:02 -07:00
Chris Robinson e25ba747e6 Remove unused macros 2017-07-25 17:42:16 -07:00
Chris Robinson 11b44d29e6 Update default HRTFs 2017-07-25 17:11:44 -07:00
Chris Robinson 75841642bf Update makehrtf to use a larger FFT by default
Also fixes DC offset removal and increases the max IR size.
2017-07-25 16:17:46 -07:00
Chris Robinson 1ab082caaf Update ChangeLog with OSS enumeration fix 2017-07-23 22:10:13 -07:00
Chris Robinson 82a990e08e Downgrade some ERRs to TRACEs
These don't exist outside OSSv4, e.g. with OSS/Free, padsp, or aoss, so no need
to be concerned.
2017-07-23 16:43:39 -07:00
Chris Robinson 5ab4e584ee Make sure OSS device files exist before adding them 2017-07-23 16:38:54 -07:00
Chris Robinson 4ec67e6226 Add missing include for std::array 2017-07-23 00:18:32 -07:00
Chris Robinson 21b71b2fd2 Update ChangeLog 2017-07-19 18:54:12 -07:00
Chris Robinson f91445029d Fix default effect initialization 2017-07-19 18:26:46 -07:00
Chris Robinson fea74124c8 Add an all-pass filter that replicates the band splitter's phase shift 2017-07-19 02:48:01 -07:00
Chris Robinson 0135ddc2e5 Scale the source volume by +3dB for a full spread
This effectively turns a full spread source into an ambient response,
preventing such sources from being unexpectedly quiet.
2017-07-18 22:15:32 -07:00
Chris Robinson 5f7268c0cc Add a 5.1 preset that excludes the front-center speaker
On some speaker setups, the front-center speaker is not designed for full-range
content and should be used exclusively for dialog. Consequently it should not
be used for positional sounds, only the dedicated dialog effect.
2017-07-18 20:30:45 -07:00
Chris Robinson c484935542 Apply the output buffer offset before writing to it 2017-07-15 23:13:08 -07:00
Chris Robinson ff696bc1fe Set the float PCM GUID for wave files only when outputting float 2017-07-15 01:45:48 -07:00
Chris Robinson 8fa3f6da64 Add the default auxiliary slot to the active slot array 2017-07-13 23:13:02 -07:00
Chris Robinson 249afde5f9 Initialize the default effect after device update 2017-07-13 22:35:37 -07:00
Chris Robinson a535169bbd Use macros to set and restore the mixer FPU mode 2017-07-13 22:30:39 -07:00
Chris Robinson 22d77b87a3 Store the default effect slot in the context 2017-07-13 21:44:25 -07:00
Chris Robinson 67ab9ec466 Don't trace for every GetDriverIndexForName call 2017-07-11 22:43:22 -07:00
Chris Robinson 0da55fd912 Trace a version for the router 2017-07-11 00:43:15 -07:00
Chris Robinson 5048322fff Update alffplay's command line message 2017-07-11 00:38:52 -07:00
Chris Robinson f313f9c117 Rename the OpenAL target if also building the router
This is rather ugly, but it's necessary to get a proper export configuration.
The issue was that the main OpenAL target library name is set to soft_oal when
the router is being built, which is incorrect for the exported config library.
Exporting the router would have the incorrect name of OpenAL::Router.

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

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

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

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

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

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

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

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

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

On TravisCI with MacOSX this requires the CoreAudio backend.

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

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

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

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

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

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

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

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

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

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

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

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

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

Fixes issue #54.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Note that currently the "direct panning" output is quieter than it should be.
Work is underway to attenuate the early reflections and late reverb better.
2016-02-01 22:24:40 -08:00
Chris Robinson b4a9b40d68 Apply the main reverb gain with the panning 2016-02-01 01:45:16 -08:00
Chris Robinson 46cae36ef9 Fix an out of date comment 2016-01-31 09:39:07 -08:00
Chris Robinson 7f908d90af Rename ComputeBFormatGains to ComputeFirstOrderGains 2016-01-31 09:00:23 -08:00
Chris Robinson d69dd6dc7a Make the source's buffer queue a singly-linked list 2016-01-31 00:42:58 -08:00
Chris Robinson 063ef9c2fc Properly silence the LFE input channel gain on the source sends 2016-01-30 07:13:07 -08:00
Chris Robinson 729f213c09 Fix scaling for effect sends of B-Format sources 2016-01-30 07:10:36 -08:00
Chris Robinson 7111322526 Make the modulator effect multichannel 2016-01-29 23:44:43 -08:00
Chris Robinson a046a951e9 Use separate modulator functions only for the waveform 2016-01-29 23:28:38 -08:00
Chris Robinson 3ac786c6af Convert the equalizer effect to multichannel 2016-01-29 07:26:19 -08:00
Chris Robinson 210b4c1fcd Make the compressor effect multichannel 2016-01-28 02:49:40 -08:00
Chris Robinson c1f87414c5 Mix to multichannel for effects
This mixes to a 4-channel first-order ambisonics buffer. With ACN ordering and
N3D scaling, this makes it easy to remain compatible with effects that only
care about mono input since channel 0 is an unattenuated mono signal.
2016-01-28 00:02:46 -08:00
Chris Robinson 2fa3ae85c9 Pass a pointer to the input samples array for effect processing 2016-01-27 08:16:47 -08:00
Chris Robinson fd387beda1 Avoid passing the device to SetChannelMap 2016-01-27 04:44:21 -08:00
Chris Robinson f547ef6d39 Separate calculating ambisonic coefficients from the panning gains 2016-01-25 06:11:51 -08:00
Chris Robinson 79e0f3e747 Don't write one byte at a time for the wave writer on big endian 2016-01-25 01:49:28 -08:00
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
217 changed files with 52609 additions and 31189 deletions
+4 -6
View File
@@ -1,7 +1,5 @@
build
winbuild
win64build
include/SLES
include/sndio.h
include/sys
build*/
winbuild/
win64build/
openal-soft.kdev4
.kdev4/
+66
View File
@@ -0,0 +1,66 @@
language: c
matrix:
include:
- os: linux
dist: trusty
- os: linux
dist: trusty
env:
- BUILD_ANDROID=true
- os: osx
sudo: required
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
curl -o ~/android-ndk.zip https://dl.google.com/android/repository/android-ndk-r15-linux-x86_64.zip
unzip -q ~/android-ndk.zip -d ~ \
'android-ndk-r15/build/cmake/*' \
'android-ndk-r15/build/core/toolchains/arm-linux-androideabi-*/*' \
'android-ndk-r15/platforms/android-14/arch-arm/*' \
'android-ndk-r15/source.properties' \
'android-ndk-r15/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/*' \
'android-ndk-r15/sources/cxx-stl/gnu-libstdc++/4.9/include/*' \
'android-ndk-r15/sysroot/*' \
'android-ndk-r15/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/*' \
'android-ndk-r15/toolchains/llvm/prebuilt/linux-x86_64/*'
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-r15/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
+2125 -1165
View File
File diff suppressed because it is too large Load Diff
+1539 -937
View File
File diff suppressed because it is too large Load Diff
-515
View File
@@ -1,515 +0,0 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#ifdef _WIN32
#ifdef __MINGW32__
#define _WIN32_IE 0x501
#else
#define _WIN32_IE 0x400
#endif
#endif
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#ifdef _WIN32_IE
#include <shlobj.h>
#endif
#include "alMain.h"
#include "compat.h"
typedef struct ConfigEntry {
char *key;
char *value;
} ConfigEntry;
typedef struct ConfigBlock {
ConfigEntry *entries;
unsigned int entryCount;
} ConfigBlock;
static ConfigBlock cfgBlock;
static char *lstrip(char *line)
{
while(isspace(line[0]))
line++;
return line;
}
static char *rstrip(char *line)
{
size_t len = strlen(line);
while(len > 0 && isspace(line[len-1]))
len--;
line[len] = 0;
return line;
}
static int readline(FILE *f, char **output, size_t *maxlen)
{
size_t len = 0;
int c;
while((c=fgetc(f)) != EOF && (c == '\r' || c == '\n'))
;
if(c == EOF)
return 0;
do {
if(len+1 >= *maxlen)
{
void *temp = NULL;
size_t newmax;
newmax = (*maxlen ? (*maxlen)<<1 : 32);
if(newmax > *maxlen)
temp = realloc(*output, newmax);
if(!temp)
{
ERR("Failed to realloc "SZFMT" bytes from "SZFMT"!\n", newmax, *maxlen);
return 0;
}
*output = temp;
*maxlen = newmax;
}
(*output)[len++] = c;
(*output)[len] = '\0';
} while((c=fgetc(f)) != EOF && c != '\r' && c != '\n');
return 1;
}
static char *expdup(const char *str)
{
char *output = NULL;
size_t maxlen = 0;
size_t len = 0;
while(*str != '\0')
{
const char *addstr;
size_t addstrlen;
size_t i;
if(str[0] != '$')
{
const char *next = strchr(str, '$');
addstr = str;
addstrlen = next ? (size_t)(next-str) : strlen(str);
str += addstrlen;
}
else
{
str++;
if(*str == '$')
{
const char *next = strchr(str+1, '$');
addstr = str;
addstrlen = next ? (size_t)(next-str) : strlen(str);
str += addstrlen;
}
else
{
char envname[1024];
size_t k = 0;
while((isalnum(*str) || *str == '_') && k < sizeof(envname)-1)
envname[k++] = *(str++);
envname[k++] = '\0';
if((addstr=getenv(envname)) == NULL)
continue;
addstrlen = strlen(addstr);
}
}
if(addstrlen == 0)
continue;
if(addstrlen >= maxlen-len)
{
void *temp = NULL;
size_t newmax;
newmax = len+addstrlen+1;
if(newmax > maxlen)
temp = realloc(output, newmax);
if(!temp)
{
ERR("Failed to realloc "SZFMT" bytes from "SZFMT"!\n", newmax, maxlen);
return output;
}
output = temp;
maxlen = newmax;
}
for(i = 0;i < addstrlen;i++)
output[len++] = addstr[i];
output[len] = '\0';
}
return output ? output : calloc(1, 1);
}
static void LoadConfigFromFile(FILE *f)
{
char curSection[128] = "";
char *buffer = NULL;
size_t maxlen = 0;
ConfigEntry *ent;
while(readline(f, &buffer, &maxlen))
{
char *line, *comment;
char key[256] = "";
char value[256] = "";
comment = strchr(buffer, '#');
if(comment) *(comment++) = 0;
line = rstrip(lstrip(buffer));
if(!line[0])
continue;
if(line[0] == '[')
{
char *section = line+1;
char *endsection;
endsection = strchr(section, ']');
if(!endsection || section == endsection || endsection[1] != 0)
{
ERR("config parse error: bad line \"%s\"\n", line);
continue;
}
*endsection = 0;
if(strcasecmp(section, "general") == 0)
curSection[0] = 0;
else
{
strncpy(curSection, section, sizeof(curSection)-1);
curSection[sizeof(curSection)-1] = 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)
{
/* sscanf doesn't handle '' or "" as empty values, so clip it
* manually. */
if(strcmp(value, "\"\"") == 0 || strcmp(value, "''") == 0)
value[0] = 0;
}
else if(sscanf(line, "%255[^=] %255[=]", key, value) == 2)
{
/* Special case for 'key =' */
value[0] = 0;
}
else
{
ERR("config parse error: malformed option line: \"%s\"\n\n", line);
continue;
}
rstrip(key);
if(curSection[0] != 0)
{
size_t len = strlen(curSection);
memmove(&key[len+1], key, sizeof(key)-1-len);
key[len] = '/';
memcpy(key, curSection, len);
}
/* Check if we already have this option set */
ent = cfgBlock.entries;
while((unsigned int)(ent-cfgBlock.entries) < cfgBlock.entryCount)
{
if(strcasecmp(ent->key, key) == 0)
break;
ent++;
}
if((unsigned int)(ent-cfgBlock.entries) >= cfgBlock.entryCount)
{
/* Allocate a new option entry */
ent = realloc(cfgBlock.entries, (cfgBlock.entryCount+1)*sizeof(ConfigEntry));
if(!ent)
{
ERR("config parse error: error reallocating config entries\n");
continue;
}
cfgBlock.entries = ent;
ent = cfgBlock.entries + cfgBlock.entryCount;
cfgBlock.entryCount++;
ent->key = strdup(key);
ent->value = NULL;
}
free(ent->value);
ent->value = expdup(value);
TRACE("found '%s' = '%s'\n", ent->key, ent->value);
}
free(buffer);
}
#ifdef _WIN32
void ReadALConfig(void)
{
WCHAR buffer[PATH_MAX];
const WCHAR *str;
FILE *f;
if(SHGetSpecialFolderPathW(NULL, buffer, CSIDL_APPDATA, FALSE) != FALSE)
{
size_t p = lstrlenW(buffer);
_snwprintf(buffer+p, PATH_MAX-p, L"\\alsoft.ini");
TRACE("Loading config %ls...\n", buffer);
f = _wfopen(buffer, L"rt");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
}
if((str=_wgetenv(L"ALSOFT_CONF")) != NULL && *str)
{
TRACE("Loading config %ls...\n", str);
f = _wfopen(str, L"rt");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
}
}
#else
void ReadALConfig(void)
{
char buffer[PATH_MAX];
const char *str;
FILE *f;
str = "/etc/openal/alsoft.conf";
TRACE("Loading config %s...\n", str);
f = al_fopen(str, "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
if(!(str=getenv("XDG_CONFIG_DIRS")) || str[0] == 0)
str = "/etc/xdg";
strncpy(buffer, str, sizeof(buffer)-1);
buffer[sizeof(buffer)-1] = 0;
/* Go through the list in reverse, since "the order of base directories
* denotes their importance; the first directory listed is the most
* important". Ergo, we need to load the settings from the later dirs
* first so that the settings in the earlier dirs override them.
*/
while(1)
{
char *next = strrchr(buffer, ':');
if(next) *(next++) = 0;
else next = buffer;
if(next[0] != '/')
WARN("Ignoring XDG config dir: %s\n", next);
else
{
size_t len = strlen(next);
strncpy(next+len, "/alsoft.conf", buffer+sizeof(buffer)-next-len);
buffer[sizeof(buffer)-1] = 0;
TRACE("Loading config %s...\n", next);
f = al_fopen(next, "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
}
if(next == buffer)
break;
}
if((str=getenv("HOME")) != NULL && *str)
{
snprintf(buffer, sizeof(buffer), "%s/.alsoftrc", str);
TRACE("Loading config %s...\n", buffer);
f = al_fopen(buffer, "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
}
if((str=getenv("XDG_CONFIG_HOME")) != NULL && str[0] != 0)
snprintf(buffer, sizeof(buffer), "%s/%s", str, "alsoft.conf");
else
{
buffer[0] = 0;
if((str=getenv("HOME")) != NULL && str[0] != 0)
snprintf(buffer, sizeof(buffer), "%s/.config/%s", str, "alsoft.conf");
}
if(buffer[0] != 0)
{
TRACE("Loading config %s...\n", buffer);
f = al_fopen(buffer, "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
}
if((str=getenv("ALSOFT_CONF")) != NULL && *str)
{
TRACE("Loading config %s...\n", str);
f = al_fopen(str, "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
}
}
#endif
void FreeALConfig(void)
{
unsigned int i;
for(i = 0;i < cfgBlock.entryCount;i++)
{
free(cfgBlock.entries[i].key);
free(cfgBlock.entries[i].value);
}
free(cfgBlock.entries);
}
const char *GetConfigValue(const char *blockName, const char *keyName, const char *def)
{
unsigned int i;
char key[256];
if(!keyName)
return def;
if(blockName && strcasecmp(blockName, "general") != 0)
snprintf(key, sizeof(key), "%s/%s", blockName, 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)
{
TRACE("Found %s = \"%s\"\n", key, cfgBlock.entries[i].value);
if(cfgBlock.entries[i].value[0])
return cfgBlock.entries[i].value;
return def;
}
}
TRACE("Key %s not found\n", key);
return def;
}
int ConfigValueExists(const char *blockName, const char *keyName)
{
const char *val = GetConfigValue(blockName, keyName, "");
return !!val[0];
}
int ConfigValueStr(const char *blockName, const char *keyName, const char **ret)
{
const char *val = GetConfigValue(blockName, keyName, "");
if(!val[0]) return 0;
*ret = val;
return 1;
}
int ConfigValueInt(const char *blockName, const char *keyName, int *ret)
{
const char *val = GetConfigValue(blockName, keyName, "");
if(!val[0]) return 0;
*ret = strtol(val, NULL, 0);
return 1;
}
int ConfigValueUInt(const char *blockName, const char *keyName, unsigned int *ret)
{
const char *val = GetConfigValue(blockName, keyName, "");
if(!val[0]) return 0;
*ret = strtoul(val, NULL, 0);
return 1;
}
int ConfigValueFloat(const char *blockName, const char *keyName, float *ret)
{
const char *val = GetConfigValue(blockName, keyName, "");
if(!val[0]) return 0;
#ifdef HAVE_STRTOF
*ret = strtof(val, NULL);
#else
*ret = (float)strtod(val, NULL);
#endif
return 1;
}
int GetConfigValueBool(const char *blockName, const char *keyName, int def)
{
const char *val = GetConfigValue(blockName, keyName, "");
if(!val[0]) return !!def;
return (strcasecmp(val, "true") == 0 || strcasecmp(val, "yes") == 0 ||
strcasecmp(val, "on") == 0 || atoi(val) != 0);
}
-129
View File
@@ -1,129 +0,0 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <string.h>
#include <stdlib.h>
#include "alMain.h"
#include "threads.h"
#include "compat.h"
struct RingBuffer {
ALubyte *mem;
ALsizei frame_size;
ALsizei length;
ALint read_pos;
ALint write_pos;
almtx_t mtx;
};
RingBuffer *CreateRingBuffer(ALsizei frame_size, ALsizei length)
{
RingBuffer *ring = calloc(1, sizeof(*ring) + ((length+1) * frame_size));
if(ring)
{
ring->mem = (ALubyte*)(ring+1);
ring->frame_size = frame_size;
ring->length = length+1;
ring->read_pos = 0;
ring->write_pos = 0;
almtx_init(&ring->mtx, almtx_plain);
}
return ring;
}
void DestroyRingBuffer(RingBuffer *ring)
{
if(ring)
{
almtx_destroy(&ring->mtx);
free(ring);
}
}
ALsizei RingBufferSize(RingBuffer *ring)
{
ALsizei s;
almtx_lock(&ring->mtx);
s = (ring->write_pos-ring->read_pos+ring->length) % ring->length;
almtx_unlock(&ring->mtx);
return s;
}
void WriteRingBuffer(RingBuffer *ring, const ALubyte *data, ALsizei len)
{
int remain;
almtx_lock(&ring->mtx);
remain = (ring->read_pos-ring->write_pos-1+ring->length) % ring->length;
if(remain < len) len = remain;
if(len > 0)
{
remain = ring->length - ring->write_pos;
if(remain < len)
{
memcpy(ring->mem+(ring->write_pos*ring->frame_size), data,
remain*ring->frame_size);
memcpy(ring->mem, data+(remain*ring->frame_size),
(len-remain)*ring->frame_size);
}
else
memcpy(ring->mem+(ring->write_pos*ring->frame_size), data,
len*ring->frame_size);
ring->write_pos += len;
ring->write_pos %= ring->length;
}
almtx_unlock(&ring->mtx);
}
void ReadRingBuffer(RingBuffer *ring, ALubyte *data, ALsizei len)
{
int remain;
almtx_lock(&ring->mtx);
remain = ring->length - ring->read_pos;
if(remain < len)
{
memcpy(data, ring->mem+(ring->read_pos*ring->frame_size), remain*ring->frame_size);
memcpy(data+(remain*ring->frame_size), ring->mem, (len-remain)*ring->frame_size);
}
else
memcpy(data, ring->mem+(ring->read_pos*ring->frame_size), len*ring->frame_size);
ring->read_pos += len;
ring->read_pos %= ring->length;
almtx_unlock(&ring->mtx);
}
+721
View File
@@ -0,0 +1,721 @@
/**
* 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
*/
#ifdef _WIN32
#ifdef __MINGW32__
#define _WIN32_IE 0x501
#else
#define _WIN32_IE 0x400
#endif
#endif
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#ifdef _WIN32_IE
#include <windows.h>
#include <shlobj.h>
#endif
#ifdef __APPLE__
#include <CoreFoundation/CoreFoundation.h>
#endif
#include "alMain.h"
#include "alconfig.h"
#include "compat.h"
#include "bool.h"
typedef struct ConfigEntry {
char *key;
char *value;
} ConfigEntry;
typedef struct ConfigBlock {
ConfigEntry *entries;
unsigned int entryCount;
} ConfigBlock;
static ConfigBlock cfgBlock;
static char *lstrip(char *line)
{
while(isspace(line[0]))
line++;
return line;
}
static char *rstrip(char *line)
{
size_t len = strlen(line);
while(len > 0 && isspace(line[len-1]))
len--;
line[len] = 0;
return line;
}
static int readline(FILE *f, char **output, size_t *maxlen)
{
size_t len = 0;
int c;
while((c=fgetc(f)) != EOF && (c == '\r' || c == '\n'))
;
if(c == EOF)
return 0;
do {
if(len+1 >= *maxlen)
{
void *temp = NULL;
size_t newmax;
newmax = (*maxlen ? (*maxlen)<<1 : 32);
if(newmax > *maxlen)
temp = realloc(*output, newmax);
if(!temp)
{
ERR("Failed to realloc "SZFMT" bytes from "SZFMT"!\n", newmax, *maxlen);
return 0;
}
*output = temp;
*maxlen = newmax;
}
(*output)[len++] = c;
(*output)[len] = '\0';
} while((c=fgetc(f)) != EOF && c != '\r' && c != '\n');
return 1;
}
static char *expdup(const char *str)
{
char *output = NULL;
size_t maxlen = 0;
size_t len = 0;
while(*str != '\0')
{
const char *addstr;
size_t addstrlen;
size_t i;
if(str[0] != '$')
{
const char *next = strchr(str, '$');
addstr = str;
addstrlen = next ? (size_t)(next-str) : strlen(str);
str += addstrlen;
}
else
{
str++;
if(*str == '$')
{
const char *next = strchr(str+1, '$');
addstr = str;
addstrlen = next ? (size_t)(next-str) : strlen(str);
str += addstrlen;
}
else
{
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);
}
}
if(addstrlen == 0)
continue;
if(addstrlen >= maxlen-len)
{
void *temp = NULL;
size_t newmax;
newmax = len+addstrlen+1;
if(newmax > maxlen)
temp = realloc(output, newmax);
if(!temp)
{
ERR("Failed to realloc "SZFMT" bytes from "SZFMT"!\n", newmax, maxlen);
return output;
}
output = temp;
maxlen = newmax;
}
for(i = 0;i < addstrlen;i++)
output[len++] = addstr[i];
output[len] = '\0';
}
return output ? output : calloc(1, 1);
}
static void LoadConfigFromFile(FILE *f)
{
char curSection[128] = "";
char *buffer = NULL;
size_t maxlen = 0;
ConfigEntry *ent;
while(readline(f, &buffer, &maxlen))
{
char *line, *comment;
char key[256] = "";
char value[256] = "";
line = rstrip(lstrip(buffer));
if(!line[0]) continue;
if(line[0] == '[')
{
char *section = line+1;
char *endsection;
endsection = strchr(section, ']');
if(!endsection || section == endsection)
{
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;
if(strcasecmp(section, "general") == 0)
curSection[0] = 0;
else
{
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)
{
/* sscanf doesn't handle '' or "" as empty values, so clip it
* manually. */
if(strcmp(value, "\"\"") == 0 || strcmp(value, "''") == 0)
value[0] = 0;
}
else if(sscanf(line, "%255[^=] %255[=]", key, value) == 2)
{
/* Special case for 'key =' */
value[0] = 0;
}
else
{
ERR("config parse error: malformed option line: \"%s\"\n\n", line);
continue;
}
rstrip(key);
if(curSection[0] != 0)
{
size_t len = strlen(curSection);
memmove(&key[len+1], key, sizeof(key)-1-len);
key[len] = '/';
memcpy(key, curSection, len);
}
/* Check if we already have this option set */
ent = cfgBlock.entries;
while((unsigned int)(ent-cfgBlock.entries) < cfgBlock.entryCount)
{
if(strcasecmp(ent->key, key) == 0)
break;
ent++;
}
if((unsigned int)(ent-cfgBlock.entries) >= cfgBlock.entryCount)
{
/* Allocate a new option entry */
ent = realloc(cfgBlock.entries, (cfgBlock.entryCount+1)*sizeof(ConfigEntry));
if(!ent)
{
ERR("config parse error: error reallocating config entries\n");
continue;
}
cfgBlock.entries = ent;
ent = cfgBlock.entries + cfgBlock.entryCount;
cfgBlock.entryCount++;
ent->key = strdup(key);
ent->value = NULL;
}
free(ent->value);
ent->value = expdup(value);
TRACE("found '%s' = '%s'\n", ent->key, ent->value);
}
free(buffer);
}
#ifdef _WIN32
void ReadALConfig(void)
{
al_string ppath = AL_STRING_INIT_STATIC();
WCHAR buffer[MAX_PATH];
const WCHAR *str;
FILE *f;
if(SHGetSpecialFolderPathW(NULL, buffer, CSIDL_APPDATA, FALSE) != FALSE)
{
al_string filepath = AL_STRING_INIT_STATIC();
alstr_copy_wcstr(&filepath, buffer);
alstr_append_cstr(&filepath, "\\alsoft.ini");
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);
}
GetProcBinary(&ppath, NULL);
if(!alstr_empty(ppath))
{
alstr_append_cstr(&ppath, "\\alsoft.ini");
TRACE("Loading config %s...\n", alstr_get_cstr(ppath));
f = al_fopen(alstr_get_cstr(ppath), "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
}
if((str=_wgetenv(L"ALSOFT_CONF")) != NULL && *str)
{
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);
}
#elif defined __vita__
void ReadALConfig(void)
{
const char* config_paths[] =
{
"app0:/alsoft.conf",
"ux0:/data/openal/alsoft.conf"
};
FILE* f;
unsigned int i;
for(i = 0; i < sizeof(config_paths) / sizeof(*config_paths); ++i)
{
TRACE("Loading config %s...\n", config_paths[i]);
f = al_fopen(config_paths[i], "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
break;
}
}
}
#else
void ReadALConfig(void)
{
al_string confpaths = AL_STRING_INIT_STATIC();
al_string fname = AL_STRING_INIT_STATIC();
const char *str;
FILE *f;
str = "/etc/openal/alsoft.conf";
TRACE("Loading config %s...\n", str);
f = al_fopen(str, "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
if(!(str=getenv("XDG_CONFIG_DIRS")) || str[0] == 0)
str = "/etc/xdg";
alstr_copy_cstr(&confpaths, str);
/* Go through the list in reverse, since "the order of base directories
* denotes their importance; the first directory listed is the most
* important". Ergo, we need to load the settings from the later dirs
* first so that the settings in the earlier dirs override them.
*/
while(!alstr_empty(confpaths))
{
char *next = strrchr(alstr_get_cstr(confpaths), ':');
if(next)
{
size_t len = next - alstr_get_cstr(confpaths);
alstr_copy_cstr(&fname, next+1);
VECTOR_RESIZE(confpaths, len, len+1);
VECTOR_ELEM(confpaths, len) = 0;
}
else
{
alstr_reset(&fname);
fname = confpaths;
AL_STRING_INIT(confpaths);
}
if(alstr_empty(fname) || VECTOR_FRONT(fname) != '/')
WARN("Ignoring XDG config dir: %s\n", alstr_get_cstr(fname));
else
{
if(VECTOR_BACK(fname) != '/') alstr_append_cstr(&fname, "/alsoft.conf");
else alstr_append_cstr(&fname, "alsoft.conf");
TRACE("Loading config %s...\n", alstr_get_cstr(fname));
f = al_fopen(alstr_get_cstr(fname), "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
}
alstr_clear(&fname);
}
#ifdef __APPLE__
CFBundleRef mainBundle = CFBundleGetMainBundle();
if(mainBundle)
{
unsigned char fileName[PATH_MAX];
CFURLRef configURL;
if((configURL=CFBundleCopyResourceURL(mainBundle, CFSTR(".alsoftrc"), CFSTR(""), NULL)) &&
CFURLGetFileSystemRepresentation(configURL, true, fileName, sizeof(fileName)))
{
f = al_fopen((const char*)fileName, "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
}
}
#endif
if((str=getenv("HOME")) != NULL && *str)
{
alstr_copy_cstr(&fname, str);
if(VECTOR_BACK(fname) != '/') alstr_append_cstr(&fname, "/.alsoftrc");
else alstr_append_cstr(&fname, ".alsoftrc");
TRACE("Loading config %s...\n", alstr_get_cstr(fname));
f = al_fopen(alstr_get_cstr(fname), "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
}
if((str=getenv("XDG_CONFIG_HOME")) != NULL && str[0] != 0)
{
alstr_copy_cstr(&fname, str);
if(VECTOR_BACK(fname) != '/') alstr_append_cstr(&fname, "/alsoft.conf");
else alstr_append_cstr(&fname, "alsoft.conf");
}
else
{
alstr_clear(&fname);
if((str=getenv("HOME")) != NULL && str[0] != 0)
{
alstr_copy_cstr(&fname, str);
if(VECTOR_BACK(fname) != '/') alstr_append_cstr(&fname, "/.config/alsoft.conf");
else alstr_append_cstr(&fname, ".config/alsoft.conf");
}
}
if(!alstr_empty(fname))
{
TRACE("Loading config %s...\n", alstr_get_cstr(fname));
f = al_fopen(alstr_get_cstr(fname), "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
}
alstr_clear(&fname);
GetProcBinary(&fname, NULL);
if(!alstr_empty(fname))
{
if(VECTOR_BACK(fname) != '/') alstr_append_cstr(&fname, "/alsoft.conf");
else alstr_append_cstr(&fname, "alsoft.conf");
TRACE("Loading config %s...\n", alstr_get_cstr(fname));
f = al_fopen(alstr_get_cstr(fname), "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
}
if((str=getenv("ALSOFT_CONF")) != NULL && *str)
{
TRACE("Loading config %s...\n", str);
f = al_fopen(str, "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
}
alstr_reset(&fname);
alstr_reset(&confpaths);
}
#endif
void FreeALConfig(void)
{
unsigned int i;
for(i = 0;i < cfgBlock.entryCount;i++)
{
free(cfgBlock.entries[i].key);
free(cfgBlock.entries[i].value);
}
free(cfgBlock.entries);
}
const char *GetConfigValue(const char *devName, const char *blockName, const char *keyName, const char *def)
{
unsigned int i;
char key[256];
if(!keyName)
return def;
if(blockName && strcasecmp(blockName, "general") != 0)
{
if(devName)
snprintf(key, sizeof(key), "%s/%s/%s", blockName, devName, keyName);
else
snprintf(key, sizeof(key), "%s/%s", blockName, keyName);
}
else
{
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(strcmp(cfgBlock.entries[i].key, key) == 0)
{
TRACE("Found %s = \"%s\"\n", key, cfgBlock.entries[i].value);
if(cfgBlock.entries[i].value[0])
return cfgBlock.entries[i].value;
return def;
}
}
if(!devName)
{
TRACE("Key %s not found\n", key);
return def;
}
return GetConfigValue(NULL, blockName, keyName, def);
}
int ConfigValueExists(const char *devName, const char *blockName, const char *keyName)
{
const char *val = GetConfigValue(devName, blockName, keyName, "");
return !!val[0];
}
int ConfigValueStr(const char *devName, const char *blockName, const char *keyName, const char **ret)
{
const char *val = GetConfigValue(devName, blockName, keyName, "");
if(!val[0]) return 0;
*ret = val;
return 1;
}
int ConfigValueInt(const char *devName, const char *blockName, const char *keyName, int *ret)
{
const char *val = GetConfigValue(devName, blockName, keyName, "");
if(!val[0]) return 0;
*ret = strtol(val, NULL, 0);
return 1;
}
int ConfigValueUInt(const char *devName, const char *blockName, const char *keyName, unsigned int *ret)
{
const char *val = GetConfigValue(devName, blockName, keyName, "");
if(!val[0]) return 0;
*ret = strtoul(val, NULL, 0);
return 1;
}
int ConfigValueFloat(const char *devName, const char *blockName, const char *keyName, float *ret)
{
const char *val = GetConfigValue(devName, blockName, keyName, "");
if(!val[0]) return 0;
#ifdef HAVE_STRTOF
*ret = strtof(val, NULL);
#else
*ret = (float)strtod(val, NULL);
#endif
return 1;
}
int ConfigValueBool(const char *devName, const char *blockName, const char *keyName, int *ret)
{
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 ||
strcasecmp(val, "on") == 0 || atoi(val) != 0);
}
+17
View File
@@ -0,0 +1,17 @@
#ifndef ALCONFIG_H
#define ALCONFIG_H
void ReadALConfig(void);
void FreeALConfig(void);
int ConfigValueExists(const char *devName, const char *blockName, const char *keyName);
const char *GetConfigValue(const char *devName, const char *blockName, const char *keyName, const char *def);
int GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, int def);
int ConfigValueStr(const char *devName, const char *blockName, const char *keyName, const char **ret);
int ConfigValueInt(const char *devName, const char *blockName, const char *keyName, int *ret);
int ConfigValueUInt(const char *devName, const char *blockName, const char *keyName, unsigned int *ret);
int ConfigValueFloat(const char *devName, const char *blockName, const char *keyName, float *ret);
int ConfigValueBool(const char *devName, const char *blockName, const char *keyName, int *ret);
#endif /* ALCONFIG_H */
+28 -15
View File
@@ -6,40 +6,53 @@
#include "vector.h"
#ifdef __cplusplus
extern "C" {
#endif
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_copy_wrange(al_string *str, const wchar_t *from, const wchar_t *to);
void alstr_append_wrange(al_string *str, const wchar_t *from, const wchar_t *to);
#endif
#ifdef __cplusplus
} /* extern "C" */
#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 */
+208 -131
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
*/
@@ -26,6 +26,8 @@
#include "alMain.h"
#include "alu.h"
#include "alconfig.h"
#include "ringbuffer.h"
#include "threads.h"
#include "compat.h"
@@ -199,15 +201,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 +223,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 +246,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 +278,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 +344,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 +361,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 +371,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);
@@ -397,7 +438,7 @@ typedef struct ALCplaybackAlsa {
ALvoid *buffer;
ALsizei size;
volatile int killNow;
ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCplaybackAlsa;
@@ -405,15 +446,14 @@ static int ALCplaybackAlsa_mixerProc(void *ptr);
static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr);
static void ALCplaybackAlsa_Construct(ALCplaybackAlsa *self, ALCdevice *device);
static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, Destruct)
static void ALCplaybackAlsa_Destruct(ALCplaybackAlsa *self);
static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name);
static void ALCplaybackAlsa_close(ALCplaybackAlsa *self);
static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self);
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)
@@ -425,6 +465,19 @@ static void ALCplaybackAlsa_Construct(ALCplaybackAlsa *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCplaybackAlsa, ALCbackend, self);
self->pcmHandle = NULL;
self->buffer = NULL;
ATOMIC_INIT(&self->killNow, AL_TRUE);
}
void ALCplaybackAlsa_Destruct(ALCplaybackAlsa *self)
{
if(self->pcmHandle)
snd_pcm_close(self->pcmHandle);
self->pcmHandle = NULL;
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
@@ -444,14 +497,14 @@ static int ALCplaybackAlsa_mixerProc(void *ptr)
update_size = device->UpdateSize;
num_updates = device->NumUpdates;
while(!self->killNow)
while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire))
{
int state = verify_state(self->pcmHandle);
if(state < 0)
{
ERR("Invalid state detected: %s\n", snd_strerror(state));
ALCplaybackAlsa_lock(self);
aluHandleDisconnect(device);
aluHandleDisconnect(device, "Bad state: %s", snd_strerror(state));
ALCplaybackAlsa_unlock(self);
break;
}
@@ -534,14 +587,14 @@ static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr)
update_size = device->UpdateSize;
num_updates = device->NumUpdates;
while(!self->killNow)
while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire))
{
int state = verify_state(self->pcmHandle);
if(state < 0)
{
ERR("Invalid state detected: %s\n", snd_strerror(state));
ALCplaybackAlsa_lock(self);
aluHandleDisconnect(device);
aluHandleDisconnect(device, "Bad state: %s", snd_strerror(state));
ALCplaybackAlsa_unlock(self);
break;
}
@@ -588,7 +641,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 +685,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,16 +709,11 @@ 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;
}
static void ALCplaybackAlsa_close(ALCplaybackAlsa *self)
{
snd_pcm_close(self->pcmHandle);
}
static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
@@ -677,6 +727,7 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
unsigned int rate;
const char *funcerr;
int allowmmap;
int dir;
int err;
switch(device->FmtType)
@@ -704,7 +755,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 +799,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 +812,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 +842,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 +894,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");
@@ -852,11 +912,11 @@ static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self)
}
thread_func = ALCplaybackAlsa_mixerProc;
}
self->killNow = 0;
ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
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;
}
@@ -873,28 +933,33 @@ static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self)
{
int res;
if(self->killNow)
if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
return;
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,21 +972,20 @@ typedef struct ALCcaptureAlsa {
ALsizei size;
ALboolean doCapture;
RingBuffer *ring;
ll_ringbuffer_t *ring;
snd_pcm_sframes_t last_avail;
} ALCcaptureAlsa;
static void ALCcaptureAlsa_Construct(ALCcaptureAlsa *self, ALCdevice *device);
static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, Destruct)
static void ALCcaptureAlsa_Destruct(ALCcaptureAlsa *self);
static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name);
static void ALCcaptureAlsa_close(ALCcaptureAlsa *self);
static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, ALCboolean, reset)
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)
@@ -933,6 +997,25 @@ static void ALCcaptureAlsa_Construct(ALCcaptureAlsa *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCcaptureAlsa, ALCbackend, self);
self->pcmHandle = NULL;
self->buffer = NULL;
self->ring = NULL;
}
void ALCcaptureAlsa_Destruct(ALCcaptureAlsa *self)
{
if(self->pcmHandle)
snd_pcm_close(self->pcmHandle);
self->pcmHandle = NULL;
al_free(self->buffer);
self->buffer = NULL;
ll_ringbuffer_free(self->ring);
self->ring = NULL;
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
@@ -955,17 +1038,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 +1100,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 +1122,19 @@ 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,
FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder),
false
);
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,31 +1143,29 @@ 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);
self->pcmHandle = NULL;
return ALC_INVALID_VALUE;
}
static void ALCcaptureAlsa_close(ALCcaptureAlsa *self)
{
snd_pcm_close(self->pcmHandle);
DestroyRingBuffer(self->ring);
free(self->buffer);
self->buffer = NULL;
}
static ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self)
{
int err = snd_pcm_start(self->pcmHandle);
int err = snd_pcm_prepare(self->pcmHandle);
if(err < 0)
ERR("prepare failed: %s\n", snd_strerror(err));
else
{
err = snd_pcm_start(self->pcmHandle);
if(err < 0)
ERR("start failed: %s\n", snd_strerror(err));
}
if(err < 0)
{
ERR("start failed: %s\n", snd_strerror(err));
aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice);
aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice, "Capture state failure: %s",
snd_strerror(err));
return ALC_FALSE;
}
@@ -1114,11 +1190,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,12 +1211,12 @@ 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;
}
self->last_avail -= samples;
while(device->Connected && samples > 0)
while(ATOMIC_LOAD(&device->Connected, almemory_order_acquire) && samples > 0)
{
snd_pcm_sframes_t amt = 0;
@@ -1160,7 +1236,7 @@ static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buff
}
else
{
free(self->buffer);
al_free(self->buffer);
self->buffer = NULL;
self->size = 0;
}
@@ -1183,7 +1259,7 @@ static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buff
if(amt < 0)
{
ERR("restore error: %s\n", snd_strerror(amt));
aluHandleDisconnect(device);
aluHandleDisconnect(device, "Capture recovery failure: %s", snd_strerror(amt));
break;
}
/* If the amount available is less than what's asked, we lost it
@@ -1208,7 +1284,7 @@ static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self)
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
snd_pcm_sframes_t avail = 0;
if(device->Connected && self->doCapture)
if(ATOMIC_LOAD(&device->Connected, almemory_order_acquire) && self->doCapture)
avail = snd_pcm_avail_update(self->pcmHandle);
if(avail < 0)
{
@@ -1224,7 +1300,7 @@ static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self)
if(avail < 0)
{
ERR("restore error: %s\n", snd_strerror(avail));
aluHandleDisconnect(device);
aluHandleDisconnect(device, "Capture recovery failure: %s", snd_strerror(avail));
}
}
@@ -1238,12 +1314,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));
@@ -1260,40 +1339,42 @@ static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self)
if(amt < 0)
{
ERR("restore error: %s\n", snd_strerror(amt));
aluHandleDisconnect(device);
aluHandleDisconnect(device, "Capture recovery failure: %s", snd_strerror(amt));
break;
}
avail = amt;
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)); }
static inline void AppendCaptureDeviceList2(const DevMap *entry)
{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); }
typedef struct ALCalsaBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCalsaBackendFactory;
@@ -1331,19 +1412,25 @@ static ALCboolean ALCalsaBackendFactory_querySupport(ALCalsaBackendFactory* UNUS
return ALC_FALSE;
}
static void ALCalsaBackendFactory_probe(ALCalsaBackendFactory* UNUSED(self), enum DevProbe type)
static void ALCalsaBackendFactory_probe(ALCalsaBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
switch(type)
{
#define APPEND_OUTNAME(i) do { \
if(!alstr_empty((i)->name)) \
alstr_append_range(outnames, VECTOR_BEGIN((i)->name), \
VECTOR_END((i)->name)+1); \
} while(0)
case ALL_DEVICE_PROBE:
probe_devices(SND_PCM_STREAM_PLAYBACK, &PlaybackDevices);
VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_OUTNAME);
break;
case CAPTURE_DEVICE_PROBE:
probe_devices(SND_PCM_STREAM_CAPTURE, &CaptureDevices);
VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_OUTNAME);
break;
#undef APPEND_OUTNAME
}
}
@@ -1352,25 +1439,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);
}
+28 -176
View File
@@ -4,17 +4,22 @@
#include <stdlib.h>
#include "alMain.h"
#include "alu.h"
#include "backends/base.h"
extern inline ALuint64 GetDeviceClockTime(ALCdevice *device);
extern inline void ALCdevice_Lock(ALCdevice *device);
extern inline void ALCdevice_Unlock(ALCdevice *device);
extern inline ClockLatency GetClockLatency(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 +42,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 +82,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;
}
+57 -11
View File
@@ -3,6 +3,26 @@
#include "alMain.h"
#include "threads.h"
#include "alstring.h"
#ifdef __cplusplus
extern "C" {
#endif
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;
@@ -20,7 +40,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);
@@ -28,7 +48,6 @@ struct ALCbackendVtable {
void (*const Destruct)(ALCbackend*);
ALCenum (*const open)(ALCbackend*, const ALCchar*);
void (*const close)(ALCbackend*);
ALCboolean (*const reset)(ALCbackend*);
ALCboolean (*const start)(ALCbackend*);
@@ -37,7 +56,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*);
@@ -48,13 +67,12 @@ struct ALCbackendVtable {
#define DEFINE_ALCBACKEND_VTABLE(T) \
DECLARE_THUNK(T, ALCbackend, void, Destruct) \
DECLARE_THUNK1(T, ALCbackend, ALCenum, open, const ALCchar*) \
DECLARE_THUNK(T, ALCbackend, void, close) \
DECLARE_THUNK(T, ALCbackend, ALCboolean, reset) \
DECLARE_THUNK(T, ALCbackend, ALCboolean, start) \
DECLARE_THUNK(T, ALCbackend, void, stop) \
DECLARE_THUNK2(T, ALCbackend, ALCenum, captureSamples, void*, ALCuint) \
DECLARE_THUNK(T, ALCbackend, ALCuint, availableSamples) \
DECLARE_THUNK(T, ALCbackend, ALint64, getLatency) \
DECLARE_THUNK(T, ALCbackend, ClockLatency, getClockLatency) \
DECLARE_THUNK(T, ALCbackend, void, lock) \
DECLARE_THUNK(T, ALCbackend, void, unlock) \
static void T##_ALCbackend_Delete(void *ptr) \
@@ -64,13 +82,12 @@ static const struct ALCbackendVtable T##_ALCbackend_vtable = { \
T##_ALCbackend_Destruct, \
\
T##_ALCbackend_open, \
T##_ALCbackend_close, \
T##_ALCbackend_reset, \
T##_ALCbackend_start, \
T##_ALCbackend_stop, \
T##_ALCbackend_captureSamples, \
T##_ALCbackend_availableSamples, \
T##_ALCbackend_getLatency, \
T##_ALCbackend_getClockLatency, \
T##_ALCbackend_lock, \
T##_ALCbackend_unlock, \
\
@@ -99,7 +116,7 @@ struct ALCbackendFactoryVtable {
ALCboolean (*const querySupport)(ALCbackendFactory *self, ALCbackend_Type type);
void (*const probe)(ALCbackendFactory *self, enum DevProbe type);
void (*const probe)(ALCbackendFactory *self, enum DevProbe type, al_string *outnames);
ALCbackend* (*const createBackend)(ALCbackendFactory *self, ALCdevice *device, ALCbackend_Type type);
};
@@ -108,7 +125,7 @@ struct ALCbackendFactoryVtable {
DECLARE_THUNK(T, ALCbackendFactory, ALCboolean, init) \
DECLARE_THUNK(T, ALCbackendFactory, void, deinit) \
DECLARE_THUNK1(T, ALCbackendFactory, ALCboolean, querySupport, ALCbackend_Type) \
DECLARE_THUNK1(T, ALCbackendFactory, void, probe, enum DevProbe) \
DECLARE_THUNK2(T, ALCbackendFactory, void, probe, enum DevProbe, al_string*) \
DECLARE_THUNK2(T, ALCbackendFactory, ALCbackend*, createBackend, ALCdevice*, ALCbackend_Type) \
\
static const struct ALCbackendFactoryVtable T##_ALCbackendFactory_vtable = { \
@@ -122,12 +139,41 @@ static const struct ALCbackendFactoryVtable T##_ALCbackendFactory_vtable = { \
ALCbackendFactory *ALCpulseBackendFactory_getFactory(void);
ALCbackendFactory *ALCalsaBackendFactory_getFactory(void);
ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void);
ALCbackendFactory *ALCossBackendFactory_getFactory(void);
ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void);
ALCbackendFactory *ALCjackBackendFactory_getFactory(void);
ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void);
ALCbackendFactory *SndioBackendFactory_getFactory(void);
ALCbackendFactory *ALCqsaBackendFactory_getFactory(void);
ALCbackendFactory *ALCwasapiBackendFactory_getFactory(void);
ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void);
ALCbackendFactory *ALCwinmmBackendFactory_getFactory(void);
ALCbackendFactory *ALCportBackendFactory_getFactory(void);
ALCbackendFactory *ALCopenslBackendFactory_getFactory(void);
ALCbackendFactory *ALCvitaBackendFactory_getFactory(void);
ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
ALCbackendFactory *ALCwaveBackendFactory_getFactory(void);
ALCbackendFactory *ALCsdl2BackendFactory_getFactory(void);
ALCbackendFactory *ALCloopbackFactory_getFactory(void);
ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type);
inline void ALCdevice_Lock(ALCdevice *device)
{ V0(device->Backend,lock)(); }
inline void ALCdevice_Unlock(ALCdevice *device)
{ V0(device->Backend,unlock)(); }
inline ClockLatency GetClockLatency(ALCdevice *device)
{
ClockLatency ret = V0(device->Backend,getClockLatency)();
ret.Latency += device->FixedLatency;
return ret;
}
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* AL_BACKENDS_BASE_H */
+356 -247
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
*/
@@ -23,195 +23,145 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <alloca.h>
#include "alMain.h"
#include "alu.h"
#include "ringbuffer.h"
#include <CoreServices/CoreServices.h>
#include <unistd.h>
#include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioToolbox.h>
#include "backends/base.h"
typedef struct {
AudioUnit audioUnit;
ALuint frameSize;
ALdouble sampleRateRatio; // Ratio of hardware sample rate / requested sample rate
AudioStreamBasicDescription format; // This is the OpenAL format as a CoreAudio ASBD
AudioConverterRef audioConverter; // Sample rate converter if needed
AudioBufferList *bufferList; // Buffer for data coming from the input device
ALCvoid *resampleBuffer; // Buffer for returned RingBuffer data when resampling
RingBuffer *ring;
} ca_data;
static const ALCchar ca_device[] = "CoreAudio Default";
static void destroy_buffer_list(AudioBufferList* list)
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 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)
{
if(list)
{
UInt32 i;
for(i = 0;i < list->mNumberBuffers;i++)
free(list->mBuffers[i].mData);
free(list);
}
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCcoreAudioPlayback, ALCbackend, self);
self->frameSize = 0;
memset(&self->format, 0, sizeof(self->format));
}
static AudioBufferList* allocate_buffer_list(UInt32 channelCount, UInt32 byteSize)
static void ALCcoreAudioPlayback_Destruct(ALCcoreAudioPlayback *self)
{
AudioBufferList *list;
AudioUnitUninitialize(self->audioUnit);
AudioComponentInstanceDispose(self->audioUnit);
list = calloc(1, sizeof(AudioBufferList) + sizeof(AudioBuffer));
if(list)
{
list->mNumberBuffers = 1;
list->mBuffers[0].mNumberChannels = channelCount;
list->mBuffers[0].mDataByteSize = byteSize;
list->mBuffers[0].mData = malloc(byteSize);
if(list->mBuffers[0].mData == NULL)
{
free(list);
list = NULL;
}
}
return list;
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
static OSStatus ca_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
{
ALCdevice *device = (ALCdevice*)inRefCon;
ca_data *data = (ca_data*)device->ExtraData;
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;
ALCcoreAudioPlayback_lock(self);
aluMixData(device, ioData->mBuffers[0].mData,
ioData->mBuffers[0].mDataByteSize / data->frameSize);
ioData->mBuffers[0].mDataByteSize / self->frameSize);
ALCcoreAudioPlayback_unlock(self);
return noErr;
}
static OSStatus ca_capture_conversion_callback(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets,
AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void* inUserData)
static ALCenum ALCcoreAudioPlayback_open(ALCcoreAudioPlayback *self, const ALCchar *name)
{
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;
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
AudioComponentDescription desc;
AudioComponent comp;
OSStatus err;
// fill the bufferList with data from the input device
err = AudioUnitRender(data->audioUnit, &flags, inTimeStamp, 1, inNumberFrames, data->bufferList);
if(err != noErr)
{
ERR("AudioUnitRender error: %d\n", err);
return err;
}
WriteRingBuffer(data->ring, data->bufferList->mBuffers[0].mData, inNumberFrames);
return noErr;
}
static ALCenum ca_open_playback(ALCdevice *device, const ALCchar *deviceName)
{
ComponentDescription desc;
Component comp;
ca_data *data;
OSStatus err;
if(!deviceName)
deviceName = ca_device;
else if(strcmp(deviceName, ca_device) != 0)
if(!name)
name = ca_device;
else if(strcmp(name, ca_device) != 0)
return ALC_INVALID_VALUE;
/* open the default output unit */
desc.componentType = kAudioUnitType_Output;
#if TARGET_OS_IOS
desc.componentSubType = kAudioUnitSubType_RemoteIO;
#else
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
#endif
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
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 ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self)
{
ca_data *data = (ca_data*)device->ExtraData;
AudioUnitUninitialize(data->audioUnit);
CloseComponent(data->audioUnit);
free(data);
device->ExtraData = NULL;
}
static ALCboolean ca_reset_playback(ALCdevice *device)
{
ca_data *data = (ca_data*)device->ExtraData;
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 +179,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 +188,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 +263,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 +271,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 +283,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 +293,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,59 +305,196 @@ 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 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 AudioBufferList *allocate_buffer_list(UInt32 channelCount, UInt32 byteSize)
{
AudioBufferList *list;
list = calloc(1, FAM_SIZE(AudioBufferList, mBuffers, 1) + byteSize);
if(list)
{
list->mNumberBuffers = 1;
list->mBuffers[0].mNumberChannels = channelCount;
list->mBuffers[0].mDataByteSize = byteSize;
list->mBuffers[0].mData = &list->mBuffers[1];
}
return list;
}
static void destroy_buffer_list(AudioBufferList *list)
{
free(list);
}
static void ALCcoreAudioCapture_Construct(ALCcoreAudioCapture *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCcoreAudioCapture, ALCbackend, self);
self->audioUnit = 0;
self->audioConverter = NULL;
self->bufferList = NULL;
self->resampleBuffer = NULL;
self->ring = NULL;
}
static void ALCcoreAudioCapture_Destruct(ALCcoreAudioCapture *self)
{
ll_ringbuffer_free(self->ring);
self->ring = NULL;
free(self->resampleBuffer);
self->resampleBuffer = NULL;
destroy_buffer_list(self->bufferList);
self->bufferList = NULL;
if(self->audioConverter)
AudioConverterDispose(self->audioConverter);
self->audioConverter = NULL;
if(self->audioUnit)
AudioComponentInstanceDispose(self->audioUnit);
self->audioUnit = 0;
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;
AudioDeviceID inputDevice;
AudioComponentDescription desc;
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;
#if TARGET_OS_IOS
desc.componentSubType = kAudioUnitSubType_RemoteIO;
#else
desc.componentSubType = kAudioUnitSubType_HALOutput;
#endif
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
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,22 +503,28 @@ 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");
goto error;
}
#if !TARGET_OS_IOS
// Get the default input device
AudioDeviceID inputDevice = kAudioDeviceUnknown;
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;
}
if(inputDevice == kAudioDeviceUnknown)
{
ERR("No input device found\n");
@@ -442,18 +532,19 @@ 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");
goto error;
}
#endif
// 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,92 +659,83 @@ 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(
(size_t)ceil(device->UpdateSize*self->sampleRateRatio*device->NumUpdates),
self->frameSize, false
);
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);
self->resampleBuffer = NULL;
destroy_buffer_list(self->bufferList);
self->bufferList = NULL;
if(data->audioConverter)
AudioConverterDispose(data->audioConverter);
if(data->audioUnit)
CloseComponent(data->audioUnit);
free(data);
device->ExtraData = NULL;
if(self->audioConverter)
AudioConverterDispose(self->audioConverter);
self->audioConverter = NULL;
if(self->audioUnit)
AudioComponentInstanceDispose(self->audioUnit);
self->audioUnit = 0;
return ALC_INVALID_VALUE;
}
static void ca_close_capture(ALCdevice *device)
static ALCboolean ALCcoreAudioCapture_start(ALCcoreAudioCapture *self)
{
ca_data *data = (ca_data*)device->ExtraData;
DestroyRingBuffer(data->ring);
free(data->resampleBuffer);
destroy_buffer_list(data->bufferList);
AudioConverterDispose(data->audioConverter);
CloseComponent(data->audioUnit);
free(data);
device->ExtraData = NULL;
}
static void ca_start_capture(ALCdevice *device)
{
ca_data *data = (ca_data*)device->ExtraData;
OSStatus err = AudioOutputUnitStart(data->audioUnit);
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;
union {
ALbyte _[sizeof(AudioBufferList) + sizeof(AudioBuffer)];
AudioBufferList list;
} audiobuf = { { 0 } };
UInt32 frameCount;
OSStatus err;
// If no samples are requested, just return
if(samples == 0)
return ALC_NO_ERROR;
// Allocate a temporary AudioBufferList to use as the return resamples data
list = alloca(sizeof(AudioBufferList) + sizeof(AudioBuffer));
if(samples == 0) return ALC_NO_ERROR;
// Point the resampling buffer to the capture buffer
list->mNumberBuffers = 1;
list->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame;
list->mBuffers[0].mDataByteSize = samples * data->frameSize;
list->mBuffers[0].mData = buffer;
audiobuf.list.mNumberBuffers = 1;
audiobuf.list.mBuffers[0].mNumberChannels = self->format.mChannelsPerFrame;
audiobuf.list.mBuffers[0].mDataByteSize = samples * self->frameSize;
audiobuf.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, &audiobuf.list, NULL
);
if(err != noErr)
{
ERR("AudioConverterFillComplexBuffer error: %d\n", err);
@@ -661,47 +744,73 @@ 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, al_string *outnames);
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, al_string *outnames)
{
switch(type)
{
case ALL_DEVICE_PROBE:
AppendAllDevicesList(ca_device);
break;
case CAPTURE_DEVICE_PROBE:
AppendCaptureDeviceList(ca_device);
alstr_append_range(outnames, ca_device, ca_device+sizeof(ca_device));
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;
}
+226 -201
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,7 @@
#include "alMain.h"
#include "alu.h"
#include "ringbuffer.h"
#include "threads.h"
#include "compat.h"
#include "alstring.h"
@@ -43,6 +44,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 +61,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 +124,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 +142,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);
}
@@ -179,22 +185,21 @@ typedef struct ALCdsoundPlayback {
IDirectSoundNotify *Notifies;
HANDLE NotifyEvent;
volatile int killNow;
ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCdsoundPlayback;
static int ALCdsoundPlayback_mixerProc(void *ptr);
static void ALCdsoundPlayback_Construct(ALCdsoundPlayback *self, ALCdevice *device);
static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, Destruct)
static void ALCdsoundPlayback_Destruct(ALCdsoundPlayback *self);
static ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *name);
static void ALCdsoundPlayback_close(ALCdsoundPlayback *self);
static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self);
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)
@@ -206,6 +211,35 @@ static void ALCdsoundPlayback_Construct(ALCdsoundPlayback *self, ALCdevice *devi
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCdsoundPlayback, ALCbackend, self);
self->DS = NULL;
self->PrimaryBuffer = NULL;
self->Buffer = NULL;
self->Notifies = NULL;
self->NotifyEvent = NULL;
ATOMIC_INIT(&self->killNow, AL_TRUE);
}
static void ALCdsoundPlayback_Destruct(ALCdsoundPlayback *self)
{
if(self->Notifies)
IDirectSoundNotify_Release(self->Notifies);
self->Notifies = NULL;
if(self->Buffer)
IDirectSoundBuffer_Release(self->Buffer);
self->Buffer = NULL;
if(self->PrimaryBuffer != NULL)
IDirectSoundBuffer_Release(self->PrimaryBuffer);
self->PrimaryBuffer = NULL;
if(self->DS)
IDirectSound_Release(self->DS);
self->DS = NULL;
if(self->NotifyEvent)
CloseHandle(self->NotifyEvent);
self->NotifyEvent = NULL;
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
@@ -234,16 +268,17 @@ FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr)
{
ERR("Failed to get buffer caps: 0x%lx\n", err);
ALCdevice_Lock(device);
aluHandleDisconnect(device);
aluHandleDisconnect(device, "Failure retrieving playback buffer info: 0x%lx", err);
ALCdevice_Unlock(device);
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);
while(!self->killNow)
while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
{
// Get current play cursor
IDirectSoundBuffer_GetCurrentPosition(self->Buffer, &PlayCursor, NULL);
@@ -258,7 +293,7 @@ FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr)
{
ERR("Failed to play buffer: 0x%lx\n", err);
ALCdevice_Lock(device);
aluHandleDisconnect(device);
aluHandleDisconnect(device, "Failure starting playback: 0x%lx", err);
ALCdevice_Unlock(device);
return 1;
}
@@ -294,8 +329,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);
@@ -304,7 +341,7 @@ FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr)
{
ERR("Buffer lock error: %#lx\n", err);
ALCdevice_Lock(device);
aluHandleDisconnect(device);
aluHandleDisconnect(device, "Failed to lock output buffer: 0x%lx", err);
ALCdevice_Unlock(device);
return 1;
}
@@ -336,23 +373,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,29 +411,11 @@ 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;
}
static void ALCdsoundPlayback_close(ALCdsoundPlayback *self)
{
if(self->Notifies)
IDirectSoundNotify_Release(self->Notifies);
self->Notifies = NULL;
if(self->Buffer)
IDirectSoundBuffer_Release(self->Buffer);
self->Buffer = NULL;
if(self->PrimaryBuffer != NULL)
IDirectSoundBuffer_Release(self->PrimaryBuffer);
self->PrimaryBuffer = NULL;
IDirectSound_Release(self->DS);
self->DS = NULL;
CloseHandle(self->NotifyEvent);
self->NotifyEvent = NULL;
}
static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
@@ -441,28 +460,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 +504,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 +539,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;
@@ -612,7 +638,7 @@ retry_open:
static ALCboolean ALCdsoundPlayback_start(ALCdsoundPlayback *self)
{
self->killNow = 0;
ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
if(althrd_create(&self->thread, ALCdsoundPlayback_mixerProc, self) != althrd_success)
return ALC_FALSE;
@@ -623,10 +649,8 @@ static void ALCdsoundPlayback_stop(ALCdsoundPlayback *self)
{
int res;
if(self->killNow)
if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
return;
self->killNow = 1;
althrd_join(self->thread, &res);
IDirectSoundBuffer_Stop(self->Buffer);
@@ -641,19 +665,19 @@ typedef struct ALCdsoundCapture {
IDirectSoundCaptureBuffer *DSCbuffer;
DWORD BufferBytes;
DWORD Cursor;
RingBuffer *Ring;
ll_ringbuffer_t *Ring;
} ALCdsoundCapture;
static void ALCdsoundCapture_Construct(ALCdsoundCapture *self, ALCdevice *device);
static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, Destruct)
static void ALCdsoundCapture_Destruct(ALCdsoundCapture *self);
static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *name);
static void ALCdsoundCapture_close(ALCdsoundCapture *self);
static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ALCboolean, reset)
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)
@@ -664,6 +688,29 @@ static void ALCdsoundCapture_Construct(ALCdsoundCapture *self, ALCdevice *device
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCdsoundCapture, ALCbackend, self);
self->DSC = NULL;
self->DSCbuffer = NULL;
self->Ring = NULL;
}
static void ALCdsoundCapture_Destruct(ALCdsoundCapture *self)
{
ll_ringbuffer_free(self->Ring);
self->Ring = NULL;
if(self->DSCbuffer != NULL)
{
IDirectSoundCaptureBuffer_Stop(self->DSCbuffer);
IDirectSoundCaptureBuffer_Release(self->DSCbuffer);
self->DSCbuffer = NULL;
}
if(self->DSC)
IDirectSoundCapture_Release(self->DSC);
self->DSC = NULL;
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
@@ -689,17 +736,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 +766,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,
InputType.Format.nBlockAlign, false);
if(self->Ring == NULL)
hr = DSERR_OUTOFMEMORY;
}
@@ -818,7 +866,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,27 +881,11 @@ 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);
self->Ring = NULL;
if(self->DSCbuffer != NULL)
{
IDirectSoundCaptureBuffer_Stop(self->DSCbuffer);
IDirectSoundCaptureBuffer_Release(self->DSCbuffer);
self->DSCbuffer = NULL;
}
IDirectSoundCapture_Release(self->DSC);
self->DSC = NULL;
}
static ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self)
{
HRESULT hr;
@@ -862,7 +894,8 @@ static ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self)
if(FAILED(hr))
{
ERR("start failed: 0x%08lx\n", hr);
aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice);
aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice,
"Failure starting capture: 0x%lx", hr);
return ALC_FALSE;
}
@@ -877,13 +910,14 @@ static void ALCdsoundCapture_stop(ALCdsoundCapture *self)
if(FAILED(hr))
{
ERR("stop failed: 0x%08lx\n", hr);
aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice);
aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice,
"Failure stopping capture: 0x%lx", hr);
}
}
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;
}
@@ -896,10 +930,10 @@ static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self)
DWORD FrameSize;
HRESULT hr;
if(!device->Connected)
if(!ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
goto done;
FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
BufferBytes = self->BufferBytes;
LastCursor = self->Cursor;
@@ -915,9 +949,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);
@@ -927,19 +961,14 @@ static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self)
if(FAILED(hr))
{
ERR("update failed: 0x%08lx\n", hr);
aluHandleDisconnect(device);
aluHandleDisconnect(device, "Failure retrieving capture data: 0x%lx", hr);
}
done:
return RingBufferSize(self->Ring);
return (ALCuint)ll_ringbuffer_read_space(self->Ring);
}
static inline void AppendAllDevicesList2(const DevMap *entry)
{ AppendAllDevicesList(al_string_get_cstr(entry->name)); }
static inline void AppendCaptureDeviceList2(const DevMap *entry)
{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); }
typedef struct ALCdsoundBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCdsoundBackendFactory;
@@ -950,7 +979,7 @@ ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void);
static ALCboolean ALCdsoundBackendFactory_init(ALCdsoundBackendFactory *self);
static void ALCdsoundBackendFactory_deinit(ALCdsoundBackendFactory *self);
static ALCboolean ALCdsoundBackendFactory_querySupport(ALCdsoundBackendFactory *self, ALCbackend_Type type);
static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory *self, enum DevProbe type);
static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory *self, enum DevProbe type, al_string *outnames);
static ALCbackend* ALCdsoundBackendFactory_createBackend(ALCdsoundBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCdsoundBackendFactory);
@@ -994,7 +1023,7 @@ static ALCboolean ALCdsoundBackendFactory_querySupport(ALCdsoundBackendFactory*
return ALC_FALSE;
}
static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory* UNUSED(self), enum DevProbe type)
static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
HRESULT hr, hrcom;
@@ -1002,12 +1031,17 @@ static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory* UNUSED(self),
hrcom = CoInitialize(NULL);
switch(type)
{
#define APPEND_OUTNAME(e) do { \
if(!alstr_empty((e)->name)) \
alstr_append_range(outnames, VECTOR_BEGIN((e)->name), \
VECTOR_END((e)->name)+1); \
} while(0)
case ALL_DEVICE_PROBE:
clear_devlist(&PlaybackDevices);
hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
if(FAILED(hr))
ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr);
VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_OUTNAME);
break;
case CAPTURE_DEVICE_PROBE:
@@ -1015,8 +1049,9 @@ static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory* UNUSED(self),
hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
if(FAILED(hr))
ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr);
VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_OUTNAME);
break;
#undef APPEND_OUTNAME
}
if(SUCCEEDED(hrcom))
CoUninitialize();
@@ -1027,26 +1062,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);
}
+607
View File
@@ -0,0 +1,607 @@
/**
* 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 "alconfig.h"
#include "ringbuffer.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);
static __typeof(jack_error_callback) * pjack_error_callback;
#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
#define jack_error_callback (*pjack_error_callback)
#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
/* Optional symbols. These don't exist in all versions of JACK. */
#define LOAD_SYM(f) p##f = GetSymbol(jack_handle, #f)
LOAD_SYM(jack_error_callback);
#undef LOAD_SYM
if(error)
{
WARN("Missing expected functions:%s\n", alstr_get_cstr(missing_funcs));
CloseLib(jack_handle);
jack_handle = NULL;
}
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;
alsem_t Sem;
ATOMIC(ALenum) 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 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);
alsem_init(&self->Sem, 0);
self->Client = NULL;
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
self->Port[i] = NULL;
self->Ring = NULL;
ATOMIC_INIT(&self->killNow, AL_TRUE);
}
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;
}
alsem_destroy(&self->Sem);
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);
device->NumUpdates = (bufsize+device->UpdateSize) / 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),
true
);
if(!self->Ring)
{
ERR("Failed to reallocate ringbuffer\n");
aluHandleDisconnect(device, "Failed to reallocate %u-sample buffer", bufsize);
}
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);
alsem_post(&self->Sem);
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(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
{
ALuint todo, len1, len2;
if(ll_ringbuffer_write_space(self->Ring) < device->UpdateSize)
{
ALCjackPlayback_unlock(self);
alsem_wait(&self->Sem);
ALCjackPlayback_lock(self);
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 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.
*/
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);
device->NumUpdates = (bufsize+device->UpdateSize) / 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),
true
);
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);
ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
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(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
return;
alsem_post(&self->Sem);
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))
{
void (*old_error_cb)(const char*);
jack_client_t *client;
jack_status_t status;
if(!jack_load())
return ALC_FALSE;
if(!GetConfigValueBool(NULL, "jack", "spawn-server", 0))
ClientOptions |= JackNoStartServer;
old_error_cb = (&jack_error_callback ? jack_error_callback : NULL);
jack_set_error_function(jack_msg_handler);
client = jack_client_open("alsoft", ClientOptions, &status, NULL);
jack_set_error_function(old_error_cb);
if(client == NULL)
{
WARN("jack_client_open() failed, 0x%02x\n", status);
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, al_string *outnames)
{
switch(type)
{
case ALL_DEVICE_PROBE:
alstr_append_range(outnames, jackDevice, jackDevice+sizeof(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);
}
+7 -17
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
*/
@@ -35,13 +35,12 @@ typedef struct ALCloopback {
static void ALCloopback_Construct(ALCloopback *self, ALCdevice *device);
static DECLARE_FORWARD(ALCloopback, ALCbackend, void, Destruct)
static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name);
static void ALCloopback_close(ALCloopback *self);
static ALCboolean ALCloopback_reset(ALCloopback *self);
static ALCboolean ALCloopback_start(ALCloopback *self);
static void ALCloopback_stop(ALCloopback *self);
static DECLARE_FORWARD2(ALCloopback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCloopback, ALCbackend, ALCuint, availableSamples)
static DECLARE_FORWARD(ALCloopback, ALCbackend, ALint64, getLatency)
static DECLARE_FORWARD(ALCloopback, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCloopback, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCloopback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCloopback)
@@ -59,14 +58,10 @@ 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;
}
static void ALCloopback_close(ALCloopback* UNUSED(self))
{
}
static ALCboolean ALCloopback_reset(ALCloopback *self)
{
SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice);
@@ -92,7 +87,7 @@ ALCbackendFactory *ALCloopbackFactory_getFactory(void);
static ALCboolean ALCloopbackFactory_init(ALCloopbackFactory *self);
static DECLARE_FORWARD(ALCloopbackFactory, ALCbackendFactory, void, deinit)
static ALCboolean ALCloopbackFactory_querySupport(ALCloopbackFactory *self, ALCbackend_Type type);
static void ALCloopbackFactory_probe(ALCloopbackFactory *self, enum DevProbe type);
static void ALCloopbackFactory_probe(ALCloopbackFactory *self, enum DevProbe type, al_string *outnames);
static ALCbackend* ALCloopbackFactory_createBackend(ALCloopbackFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCloopbackFactory);
@@ -115,7 +110,7 @@ static ALCboolean ALCloopbackFactory_querySupport(ALCloopbackFactory* UNUSED(sel
return ALC_FALSE;
}
static void ALCloopbackFactory_probe(ALCloopbackFactory* UNUSED(self), enum DevProbe UNUSED(type))
static void ALCloopbackFactory_probe(ALCloopbackFactory* UNUSED(self), enum DevProbe UNUSED(type), al_string* UNUSED(outnames))
{
}
@@ -124,13 +119,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);
}
File diff suppressed because it is too large Load Diff
+18 -26
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,7 +36,7 @@
typedef struct ALCnullBackend {
DERIVE_FROM_TYPE(ALCbackend);
volatile int killNow;
ATOMIC(int) killNow;
althrd_t thread;
} ALCnullBackend;
@@ -45,13 +45,12 @@ static int ALCnullBackend_mixerProc(void *ptr);
static void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device);
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, Destruct)
static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name);
static void ALCnullBackend_close(ALCnullBackend *self);
static ALCboolean ALCnullBackend_reset(ALCnullBackend *self);
static ALCboolean ALCnullBackend_start(ALCnullBackend *self);
static void ALCnullBackend_stop(ALCnullBackend *self);
static DECLARE_FORWARD2(ALCnullBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ALCuint, availableSamples)
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ALint64, getLatency)
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCnullBackend)
@@ -66,6 +65,8 @@ static void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCnullBackend, ALCbackend, self);
ATOMIC_INIT(&self->killNow, AL_TRUE);
}
@@ -87,7 +88,8 @@ static int ALCnullBackend_mixerProc(void *ptr)
ERR("Failed to get starting time\n");
return 1;
}
while(!self->killNow && device->Connected)
while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
{
if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC)
{
@@ -106,10 +108,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,15 +132,11 @@ 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;
}
static void ALCnullBackend_close(ALCnullBackend* UNUSED(self))
{
}
static ALCboolean ALCnullBackend_reset(ALCnullBackend *self)
{
SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice);
@@ -145,7 +145,7 @@ static ALCboolean ALCnullBackend_reset(ALCnullBackend *self)
static ALCboolean ALCnullBackend_start(ALCnullBackend *self)
{
self->killNow = 0;
ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
if(althrd_create(&self->thread, ALCnullBackend_mixerProc, self) != althrd_success)
return ALC_FALSE;
return ALC_TRUE;
@@ -155,10 +155,8 @@ static void ALCnullBackend_stop(ALCnullBackend *self)
{
int res;
if(self->killNow)
if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
return;
self->killNow = 1;
althrd_join(self->thread, &res);
}
@@ -173,7 +171,7 @@ ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
static ALCboolean ALCnullBackendFactory_init(ALCnullBackendFactory *self);
static DECLARE_FORWARD(ALCnullBackendFactory, ALCbackendFactory, void, deinit)
static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory *self, ALCbackend_Type type);
static void ALCnullBackendFactory_probe(ALCnullBackendFactory *self, enum DevProbe type);
static void ALCnullBackendFactory_probe(ALCnullBackendFactory *self, enum DevProbe type, al_string *outnames);
static ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCnullBackendFactory);
@@ -197,14 +195,13 @@ static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory* UNUS
return ALC_FALSE;
}
static void ALCnullBackendFactory_probe(ALCnullBackendFactory* UNUSED(self), enum DevProbe type)
static void ALCnullBackendFactory_probe(ALCnullBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
switch(type)
{
case ALL_DEVICE_PROBE:
AppendAllDevicesList(nullDevice);
break;
case CAPTURE_DEVICE_PROBE:
alstr_append_range(outnames, nullDevice, nullDevice+sizeof(nullDevice));
break;
}
}
@@ -214,13 +211,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);
}
+841 -195
View File
File diff suppressed because it is too large Load Diff
+399 -162
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>
@@ -33,6 +35,8 @@
#include "alMain.h"
#include "alu.h"
#include "alconfig.h"
#include "ringbuffer.h"
#include "threads.h"
#include "compat.h"
@@ -51,11 +55,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 +237,6 @@ static int log2i(ALCuint x)
return y;
}
typedef struct ALCplaybackOSS {
DERIVE_FROM_TYPE(ALCbackend);
@@ -77,22 +245,21 @@ typedef struct ALCplaybackOSS {
ALubyte *mix_data;
int data_size;
volatile int killNow;
ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCplaybackOSS;
static int ALCplaybackOSS_mixerProc(void *ptr);
static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device);
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, Destruct)
static void ALCplaybackOSS_Destruct(ALCplaybackOSS *self);
static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name);
static void ALCplaybackOSS_close(ALCplaybackOSS *self);
static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self);
static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self);
static void ALCplaybackOSS_stop(ALCplaybackOSS *self);
static DECLARE_FORWARD2(ALCplaybackOSS, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ALCuint, availableSamples)
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ALint64, getLatency)
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCplaybackOSS)
@@ -103,42 +270,66 @@ 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(&self->killNow, almemory_order_acquire) &&
ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
{
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, "Failed waiting for playback buffer: %s", strerror(errno));
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, "Failed writing playback samples: %s",
strerror(errno));
break;
}
len -= wrote;
WritePtr += wrote;
to_write -= wrote;
write_ptr += wrote;
}
}
ALCplaybackOSS_unlock(self);
return 0;
}
@@ -148,37 +339,59 @@ static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCplaybackOSS, ALCbackend, self);
self->fd = -1;
ATOMIC_INIT(&self->killNow, AL_FALSE);
}
static void ALCplaybackOSS_Destruct(ALCplaybackOSS *self)
{
if(self->fd != -1)
close(self->fd);
self->fd = -1;
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
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;
}
static void ALCplaybackOSS_close(ALCplaybackOSS *self)
{
close(self->fd);
self->fd = -1;
}
static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
@@ -212,18 +425,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 +451,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 +467,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 +478,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 +498,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,28 +515,23 @@ 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;
static int ALCcaptureOSS_recordProc(void *ptr);
static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device);
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, Destruct)
static void ALCcaptureOSS_Destruct(ALCcaptureOSS *self);
static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name);
static void ALCcaptureOSS_close(ALCcaptureOSS *self);
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ALCboolean, reset)
static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self);
static void ALCcaptureOSS_stop(ALCcaptureOSS *self);
static ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples);
static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self);
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ALint64, getLatency)
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCcaptureOSS)
@@ -341,32 +542,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);
aluHandleDisconnect(device);
ALCcaptureOSS_unlock(self);
if(errno == EINTR)
continue;
ERR("select failed: %s\n", strerror(errno));
aluHandleDisconnect(device, "Failed to check capture samples: %s", strerror(errno));
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, "Failed reading capture samples: %s", strerror(errno));
ALCcaptureOSS_unlock(self);
break;
}
ll_ringbuffer_write_advance(self->ring, amt/frame_size);
}
}
return 0;
@@ -377,11 +601,27 @@ static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCcaptureOSS, ALCbackend, self);
self->fd = -1;
self->ring = NULL;
ATOMIC_INIT(&self->killNow, AL_FALSE);
}
static void ALCcaptureOSS_Destruct(ALCcaptureOSS *self)
{
if(self->fd != -1)
close(self->fd);
self->fd = -1;
ll_ringbuffer_free(self->ring);
self->ring = NULL;
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
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 +632,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 +681,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 +711,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 +729,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, frameSize, false);
if(!self->ring)
{
ERR("Ring buffer create failed\n");
@@ -481,60 +738,41 @@ 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);
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,9 +784,9 @@ 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 void ALCossBackendFactory_probe(ALCossBackendFactory *self, enum DevProbe type, al_string *outnames);
static ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCossBackendFactory);
@@ -562,12 +800,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)
@@ -575,29 +820,31 @@ ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory* UNUSED(self),
return ALC_FALSE;
}
void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProbe type)
void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
struct oss_device *cur = NULL;
switch(type)
{
case ALL_DEVICE_PROBE:
{
#ifdef HAVE_STAT
struct stat buf;
if(stat(oss_driver, &buf) == 0)
#endif
AppendAllDevicesList(oss_device);
}
break;
ALCossListFree(&oss_playback);
ALCossListPopulate(&oss_playback, DSP_CAP_OUTPUT);
cur = &oss_playback;
break;
case CAPTURE_DEVICE_PROBE:
{
ALCossListFree(&oss_capture);
ALCossListPopulate(&oss_capture, DSP_CAP_INPUT);
cur = &oss_capture;
break;
}
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;
alstr_append_range(outnames, cur->handle, cur->handle+strlen(cur->handle)+1);
cur = cur->next;
}
}
@@ -606,25 +853,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);
}
+255 -169
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
*/
@@ -26,8 +26,12 @@
#include "alMain.h"
#include "alu.h"
#include "alconfig.h"
#include "ringbuffer.h"
#include "compat.h"
#include "backends/base.h"
#include <portaudio.h>
@@ -122,149 +126,165 @@ 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 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;
PaError err = self->stream ? Pa_CloseStream(self->stream) : paNoError;
if(err != paNoError)
ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
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 ALCboolean ALCportPlayback_reset(ALCportPlayback *self)
{
pa_data *data = (pa_data*)device->ExtraData;
PaError err;
err = Pa_CloseStream(data->stream);
if(err != paNoError)
ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
free(data);
device->ExtraData = NULL;
}
static ALCboolean pa_reset_playback(ALCdevice *device)
{
pa_data *data = (pa_data*)device->ExtraData;
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 +292,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 +306,197 @@ 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 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;
self->ring = NULL;
}
static void ALCportCapture_Destruct(ALCportCapture *self)
{
PaError err = self->stream ? Pa_CloseStream(self->stream) : paNoError;
if(err != paNoError)
ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
self->stream = NULL;
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, false);
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 ALCboolean ALCportCapture_start(ALCportCapture *self)
{
pa_data *data = (pa_data*)device->ExtraData;
PaError err;
err = Pa_CloseStream(data->stream);
if(err != paNoError)
ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
DestroyRingBuffer(data->ring);
data->ring = NULL;
free(data);
device->ExtraData = NULL;
}
static void pa_start_capture(ALCdevice *device)
{
pa_data *data = device->ExtraData;
PaError err;
err = Pa_StartStream(data->stream);
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, al_string *outnames);
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,15 +510,46 @@ 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, al_string *outnames)
{
switch(type)
{
case ALL_DEVICE_PROBE:
AppendAllDevicesList(pa_device);
break;
case CAPTURE_DEVICE_PROBE:
AppendCaptureDeviceList(pa_device);
alstr_append_range(outnames, pa_device, pa_device+sizeof(pa_device));
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);
}
+398 -263
View File
File diff suppressed because it is too large Load Diff
+365 -458
View File
File diff suppressed because it is too large Load Diff
+288
View File
@@ -0,0 +1,288 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2018 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 <SDL2/SDL.h>
#include "alMain.h"
#include "alu.h"
#include "threads.h"
#include "compat.h"
#include "backends/base.h"
#ifdef _WIN32
#define DEVNAME_PREFIX "OpenAL Soft on "
#else
#define DEVNAME_PREFIX ""
#endif
typedef struct ALCsdl2Backend {
DERIVE_FROM_TYPE(ALCbackend);
SDL_AudioDeviceID deviceID;
ALsizei frameSize;
ALuint Frequency;
enum DevFmtChannels FmtChans;
enum DevFmtType FmtType;
ALuint UpdateSize;
} ALCsdl2Backend;
static void ALCsdl2Backend_Construct(ALCsdl2Backend *self, ALCdevice *device);
static void ALCsdl2Backend_Destruct(ALCsdl2Backend *self);
static ALCenum ALCsdl2Backend_open(ALCsdl2Backend *self, const ALCchar *name);
static ALCboolean ALCsdl2Backend_reset(ALCsdl2Backend *self);
static ALCboolean ALCsdl2Backend_start(ALCsdl2Backend *self);
static void ALCsdl2Backend_stop(ALCsdl2Backend *self);
static DECLARE_FORWARD2(ALCsdl2Backend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCsdl2Backend, ALCbackend, ALCuint, availableSamples)
static DECLARE_FORWARD(ALCsdl2Backend, ALCbackend, ClockLatency, getClockLatency)
static void ALCsdl2Backend_lock(ALCsdl2Backend *self);
static void ALCsdl2Backend_unlock(ALCsdl2Backend *self);
DECLARE_DEFAULT_ALLOCATORS(ALCsdl2Backend)
DEFINE_ALCBACKEND_VTABLE(ALCsdl2Backend);
static const ALCchar defaultDeviceName[] = DEVNAME_PREFIX "Default Device";
static void ALCsdl2Backend_Construct(ALCsdl2Backend *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCsdl2Backend, ALCbackend, self);
self->deviceID = 0;
self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
self->Frequency = device->Frequency;
self->FmtChans = device->FmtChans;
self->FmtType = device->FmtType;
self->UpdateSize = device->UpdateSize;
}
static void ALCsdl2Backend_Destruct(ALCsdl2Backend *self)
{
if(self->deviceID)
SDL_CloseAudioDevice(self->deviceID);
self->deviceID = 0;
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
static void ALCsdl2Backend_audioCallback(void *ptr, Uint8 *stream, int len)
{
ALCsdl2Backend *self = (ALCsdl2Backend*)ptr;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
assert((len % self->frameSize) == 0);
aluMixData(device, stream, len / self->frameSize);
}
static ALCenum ALCsdl2Backend_open(ALCsdl2Backend *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
SDL_AudioSpec want, have;
SDL_zero(want);
SDL_zero(have);
want.freq = device->Frequency;
switch(device->FmtType)
{
case DevFmtUByte: want.format = AUDIO_U8; break;
case DevFmtByte: want.format = AUDIO_S8; break;
case DevFmtUShort: want.format = AUDIO_U16SYS; break;
case DevFmtShort: want.format = AUDIO_S16SYS; break;
case DevFmtUInt: /* fall-through */
case DevFmtInt: want.format = AUDIO_S32SYS; break;
case DevFmtFloat: want.format = AUDIO_F32; break;
}
want.channels = (device->FmtChans == DevFmtMono) ? 1 : 2;
want.samples = device->UpdateSize;
want.callback = ALCsdl2Backend_audioCallback;
want.userdata = self;
/* Passing NULL to SDL_OpenAudioDevice opens a default, which isn't
* necessarily the first in the list.
*/
if(!name || strcmp(name, defaultDeviceName) == 0)
self->deviceID = SDL_OpenAudioDevice(NULL, SDL_FALSE, &want, &have,
SDL_AUDIO_ALLOW_ANY_CHANGE);
else
{
const size_t prefix_len = strlen(DEVNAME_PREFIX);
if(strncmp(name, DEVNAME_PREFIX, prefix_len) == 0)
self->deviceID = SDL_OpenAudioDevice(name+prefix_len, SDL_FALSE, &want, &have,
SDL_AUDIO_ALLOW_ANY_CHANGE);
else
self->deviceID = SDL_OpenAudioDevice(name, SDL_FALSE, &want, &have,
SDL_AUDIO_ALLOW_ANY_CHANGE);
}
if(self->deviceID == 0)
return ALC_INVALID_VALUE;
device->Frequency = have.freq;
if(have.channels == 1)
device->FmtChans = DevFmtMono;
else if(have.channels == 2)
device->FmtChans = DevFmtStereo;
else
{
ERR("Got unhandled SDL channel count: %d\n", (int)have.channels);
return ALC_INVALID_VALUE;
}
switch(have.format)
{
case AUDIO_U8: device->FmtType = DevFmtUByte; break;
case AUDIO_S8: device->FmtType = DevFmtByte; break;
case AUDIO_U16SYS: device->FmtType = DevFmtUShort; break;
case AUDIO_S16SYS: device->FmtType = DevFmtShort; break;
case AUDIO_S32SYS: device->FmtType = DevFmtInt; break;
case AUDIO_F32SYS: device->FmtType = DevFmtFloat; break;
default:
ERR("Got unsupported SDL format: 0x%04x\n", have.format);
return ALC_INVALID_VALUE;
}
device->UpdateSize = have.samples;
device->NumUpdates = 2; /* SDL always (tries to) use two periods. */
self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
self->Frequency = device->Frequency;
self->FmtChans = device->FmtChans;
self->FmtType = device->FmtType;
self->UpdateSize = device->UpdateSize;
alstr_copy_cstr(&device->DeviceName, name ? name : defaultDeviceName);
return ALC_NO_ERROR;
}
static ALCboolean ALCsdl2Backend_reset(ALCsdl2Backend *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
device->Frequency = self->Frequency;
device->FmtChans = self->FmtChans;
device->FmtType = self->FmtType;
device->UpdateSize = self->UpdateSize;
device->NumUpdates = 2;
SetDefaultWFXChannelOrder(device);
return ALC_TRUE;
}
static ALCboolean ALCsdl2Backend_start(ALCsdl2Backend *self)
{
SDL_PauseAudioDevice(self->deviceID, 0);
return ALC_TRUE;
}
static void ALCsdl2Backend_stop(ALCsdl2Backend *self)
{
SDL_PauseAudioDevice(self->deviceID, 1);
}
static void ALCsdl2Backend_lock(ALCsdl2Backend *self)
{
SDL_LockAudioDevice(self->deviceID);
}
static void ALCsdl2Backend_unlock(ALCsdl2Backend *self)
{
SDL_UnlockAudioDevice(self->deviceID);
}
typedef struct ALCsdl2BackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCsdl2BackendFactory;
#define ALCsdl2BACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCsdl2BackendFactory, ALCbackendFactory) } }
ALCbackendFactory *ALCsdl2BackendFactory_getFactory(void);
static ALCboolean ALCsdl2BackendFactory_init(ALCsdl2BackendFactory *self);
static void ALCsdl2BackendFactory_deinit(ALCsdl2BackendFactory *self);
static ALCboolean ALCsdl2BackendFactory_querySupport(ALCsdl2BackendFactory *self, ALCbackend_Type type);
static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory *self, enum DevProbe type, al_string *outnames);
static ALCbackend* ALCsdl2BackendFactory_createBackend(ALCsdl2BackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsdl2BackendFactory);
ALCbackendFactory *ALCsdl2BackendFactory_getFactory(void)
{
static ALCsdl2BackendFactory factory = ALCsdl2BACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
static ALCboolean ALCsdl2BackendFactory_init(ALCsdl2BackendFactory* UNUSED(self))
{
if(SDL_InitSubSystem(SDL_INIT_AUDIO) == 0)
return AL_TRUE;
return ALC_FALSE;
}
static void ALCsdl2BackendFactory_deinit(ALCsdl2BackendFactory* UNUSED(self))
{
SDL_QuitSubSystem(SDL_INIT_AUDIO);
}
static ALCboolean ALCsdl2BackendFactory_querySupport(ALCsdl2BackendFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
return ALC_TRUE;
return ALC_FALSE;
}
static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
int num_devices, i;
al_string name;
if(type != ALL_DEVICE_PROBE)
return;
AL_STRING_INIT(name);
num_devices = SDL_GetNumAudioDevices(SDL_FALSE);
alstr_append_range(outnames, defaultDeviceName, defaultDeviceName+sizeof(defaultDeviceName));
for(i = 0;i < num_devices;++i)
{
alstr_copy_cstr(&name, DEVNAME_PREFIX);
alstr_append_cstr(&name, SDL_GetAudioDeviceName(i, SDL_FALSE));
if(!alstr_empty(name))
alstr_append_range(outnames, VECTOR_BEGIN(name), VECTOR_END(name)+1);
}
alstr_reset(&name);
}
static ALCbackend* ALCsdl2BackendFactory_createBackend(ALCsdl2BackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCsdl2Backend *backend;
NEW_OBJ(backend, ALCsdl2Backend)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
+392 -87
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,6 +27,9 @@
#include "alMain.h"
#include "alu.h"
#include "threads.h"
#include "ringbuffer.h"
#include "backends/base.h"
#include <sndio.h>
@@ -34,49 +37,88 @@
static const ALCchar sndio_device[] = "SndIO Default";
static ALCboolean sndio_load(void)
{
return ALC_TRUE;
}
typedef struct SndioPlayback {
DERIVE_FROM_TYPE(ALCbackend);
typedef struct {
struct sio_hdl *sndHandle;
ALvoid *mix_data;
ALsizei data_size;
volatile int killNow;
ATOMIC(int) killNow;
althrd_t thread;
} sndio_data;
} SndioPlayback;
static int SndioPlayback_mixerProc(void *ptr);
static void SndioPlayback_Construct(SndioPlayback *self, ALCdevice *device);
static void SndioPlayback_Destruct(SndioPlayback *self);
static ALCenum SndioPlayback_open(SndioPlayback *self, const ALCchar *name);
static ALCboolean SndioPlayback_reset(SndioPlayback *self);
static ALCboolean SndioPlayback_start(SndioPlayback *self);
static void SndioPlayback_stop(SndioPlayback *self);
static DECLARE_FORWARD2(SndioPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(SndioPlayback, ALCbackend, ALCuint, availableSamples)
static DECLARE_FORWARD(SndioPlayback, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(SndioPlayback, ALCbackend, void, lock)
static DECLARE_FORWARD(SndioPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(SndioPlayback)
DEFINE_ALCBACKEND_VTABLE(SndioPlayback);
static int sndio_proc(void *ptr)
static void SndioPlayback_Construct(SndioPlayback *self, ALCdevice *device)
{
ALCdevice *device = ptr;
sndio_data *data = device->ExtraData;
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(SndioPlayback, ALCbackend, self);
self->sndHandle = NULL;
self->mix_data = NULL;
ATOMIC_INIT(&self->killNow, AL_TRUE);
}
static void SndioPlayback_Destruct(SndioPlayback *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 SndioPlayback_mixerProc(void *ptr)
{
SndioPlayback *self = (SndioPlayback*)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(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
{
ALsizei len = data->data_size;
ALubyte *WritePtr = data->mix_data;
ALsizei len = self->data_size;
ALubyte *WritePtr = self->mix_data;
SndioPlayback_lock(self);
aluMixData(device, WritePtr, len/frameSize);
while(len > 0 && !data->killNow)
SndioPlayback_unlock(self);
while(len > 0 && !ATOMIC_LOAD(&self->killNow, almemory_order_acquire))
{
wrote = sio_write(data->sndHandle, WritePtr, len);
wrote = sio_write(self->sndHandle, WritePtr, len);
if(wrote == 0)
{
ERR("sio_write failed\n");
ALCdevice_Lock(device);
aluHandleDisconnect(device);
aluHandleDisconnect(device, "Failed to write playback samples");
ALCdevice_Unlock(device);
break;
}
@@ -90,45 +132,30 @@ static int sndio_proc(void *ptr)
}
static ALCenum sndio_open_playback(ALCdevice *device, const ALCchar *deviceName)
static ALCenum SndioPlayback_open(SndioPlayback *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 ALCboolean SndioPlayback_reset(SndioPlayback *self)
{
sndio_data *data = device->ExtraData;
sio_close(data->sndHandle);
free(data);
device->ExtraData = NULL;
}
static ALCboolean sndio_reset_playback(ALCdevice *device)
{
sndio_data *data = device->ExtraData;
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
struct sio_par par;
sio_initpar(&par);
@@ -170,7 +197,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,85 +238,363 @@ static ALCboolean sndio_reset_playback(ALCdevice *device)
return ALC_TRUE;
}
static ALCboolean sndio_start_playback(ALCdevice *device)
static ALCboolean SndioPlayback_start(SndioPlayback *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)
ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
if(althrd_create(&self->thread, SndioPlayback_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 SndioPlayback_stop(SndioPlayback *self)
{
sndio_data *data = device->ExtraData;
int res;
if(data->killNow)
if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
return;
althrd_join(self->thread, &res);
data->killNow = 1;
althrd_join(data->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 SndioCapture {
DERIVE_FROM_TYPE(ALCbackend);
ALCboolean alc_sndio_init(BackendFuncs *func_list)
struct sio_hdl *sndHandle;
ll_ringbuffer_t *ring;
ATOMIC(int) killNow;
althrd_t thread;
} SndioCapture;
static int SndioCapture_recordProc(void *ptr);
static void SndioCapture_Construct(SndioCapture *self, ALCdevice *device);
static void SndioCapture_Destruct(SndioCapture *self);
static ALCenum SndioCapture_open(SndioCapture *self, const ALCchar *name);
static DECLARE_FORWARD(SndioCapture, ALCbackend, ALCboolean, reset)
static ALCboolean SndioCapture_start(SndioCapture *self);
static void SndioCapture_stop(SndioCapture *self);
static ALCenum SndioCapture_captureSamples(SndioCapture *self, void *buffer, ALCuint samples);
static ALCuint SndioCapture_availableSamples(SndioCapture *self);
static DECLARE_FORWARD(SndioCapture, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(SndioCapture, ALCbackend, void, lock)
static DECLARE_FORWARD(SndioCapture, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(SndioCapture)
DEFINE_ALCBACKEND_VTABLE(SndioCapture);
static void SndioCapture_Construct(SndioCapture *self, ALCdevice *device)
{
if(!sndio_load())
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(SndioCapture, ALCbackend, self);
self->sndHandle = NULL;
self->ring = NULL;
ATOMIC_INIT(&self->killNow, AL_TRUE);
}
static void SndioCapture_Destruct(SndioCapture *self)
{
if(self->sndHandle)
sio_close(self->sndHandle);
self->sndHandle = NULL;
ll_ringbuffer_free(self->ring);
self->ring = NULL;
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
static int SndioCapture_recordProc(void* ptr)
{
SndioCapture *self = (SndioCapture*)ptr;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
ALsizei frameSize;
SetRTPriority();
althrd_setname(althrd_current(), RECORD_THREAD_NAME);
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
{
ll_ringbuffer_data_t data[2];
size_t total, todo;
ll_ringbuffer_get_write_vector(self->ring, data);
todo = data[0].len + data[1].len;
if(todo == 0)
{
static char junk[4096];
sio_read(self->sndHandle, junk, minz(sizeof(junk)/frameSize, device->UpdateSize)*frameSize);
continue;
}
total = 0;
data[0].len *= frameSize;
data[1].len *= frameSize;
todo = minz(todo, device->UpdateSize) * frameSize;
while(total < todo)
{
size_t got;
if(!data[0].len)
data[0] = data[1];
got = sio_read(self->sndHandle, data[0].buf, minz(todo-total, data[0].len));
if(!got)
{
SndioCapture_lock(self);
aluHandleDisconnect(device, "Failed to read capture samples");
SndioCapture_unlock(self);
break;
}
data[0].buf += got;
data[0].len -= got;
total += got;
}
ll_ringbuffer_write_advance(self->ring, total / frameSize);
}
return 0;
}
static ALCenum SndioCapture_open(SndioCapture *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
struct sio_par par;
if(!name)
name = sndio_device;
else if(strcmp(name, sndio_device) != 0)
return ALC_INVALID_VALUE;
self->sndHandle = sio_open(NULL, SIO_REC, 0);
if(self->sndHandle == NULL)
{
ERR("Could not open device\n");
return ALC_INVALID_VALUE;
}
sio_initpar(&par);
switch(device->FmtType)
{
case DevFmtByte:
par.bps = 1;
par.sig = 1;
break;
case DevFmtUByte:
par.bps = 1;
par.sig = 0;
break;
case DevFmtShort:
par.bps = 2;
par.sig = 1;
break;
case DevFmtUShort:
par.bps = 2;
par.sig = 0;
break;
case DevFmtInt:
par.bps = 4;
par.sig = 1;
break;
case DevFmtUInt:
par.bps = 4;
par.sig = 0;
break;
case DevFmtFloat:
ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
return ALC_INVALID_VALUE;
}
par.bits = par.bps * 8;
par.le = SIO_LE_NATIVE;
par.msb = SIO_LE_NATIVE ? 0 : 1;
par.rchan = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
par.rate = device->Frequency;
par.appbufsz = maxu(device->UpdateSize*device->NumUpdates, (device->Frequency+9)/10);
par.round = clampu(par.appbufsz/device->NumUpdates, (device->Frequency+99)/100,
(device->Frequency+19)/20);
device->UpdateSize = par.round;
device->NumUpdates = maxu(par.appbufsz/par.round, 1);
if(!sio_setpar(self->sndHandle, &par) || !sio_getpar(self->sndHandle, &par))
{
ERR("Failed to set device parameters\n");
return ALC_INVALID_VALUE;
}
if(par.bits != par.bps*8)
{
ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8);
return ALC_INVALID_VALUE;
}
if(!((device->FmtType == DevFmtByte && par.bits == 8 && par.sig != 0) ||
(device->FmtType == DevFmtUByte && par.bits == 8 && par.sig == 0) ||
(device->FmtType == DevFmtShort && par.bits == 16 && par.sig != 0) ||
(device->FmtType == DevFmtUShort && par.bits == 16 && par.sig == 0) ||
(device->FmtType == DevFmtInt && par.bits == 32 && par.sig != 0) ||
(device->FmtType == DevFmtUInt && par.bits == 32 && par.sig == 0)) ||
ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != (ALsizei)par.rchan ||
device->Frequency != par.rate)
{
ERR("Failed to set format %s %s %uhz, got %c%u %u-channel %uhz instead\n",
DevFmtTypeString(device->FmtType), DevFmtChannelsString(device->FmtChans),
device->Frequency, par.sig?'s':'u', par.bits, par.rchan, par.rate);
return ALC_INVALID_VALUE;
}
self->ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates, par.bps*par.rchan, 0);
if(!self->ring)
{
ERR("Failed to allocate %u-byte ringbuffer\n",
device->UpdateSize*device->NumUpdates*par.bps*par.rchan);
return ALC_OUT_OF_MEMORY;
}
SetDefaultChannelOrder(device);
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
static ALCboolean SndioCapture_start(SndioCapture *self)
{
if(!sio_start(self->sndHandle))
{
ERR("Error starting playback\n");
return ALC_FALSE;
*func_list = sndio_funcs;
}
ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
if(althrd_create(&self->thread, SndioCapture_recordProc, self) != althrd_success)
{
sio_stop(self->sndHandle);
return ALC_FALSE;
}
return ALC_TRUE;
}
void alc_sndio_deinit(void)
static void SndioCapture_stop(SndioCapture *self)
{
int res;
if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
return;
althrd_join(self->thread, &res);
if(!sio_stop(self->sndHandle))
ERR("Error stopping device\n");
}
void alc_sndio_probe(enum DevProbe type)
static ALCenum SndioCapture_captureSamples(SndioCapture *self, void *buffer, ALCuint samples)
{
ll_ringbuffer_read(self->ring, buffer, samples);
return ALC_NO_ERROR;
}
static ALCuint SndioCapture_availableSamples(SndioCapture *self)
{
return ll_ringbuffer_read_space(self->ring);
}
typedef struct SndioBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} SndioBackendFactory;
#define SNDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(SndioBackendFactory, ALCbackendFactory) } }
ALCbackendFactory *SndioBackendFactory_getFactory(void);
static ALCboolean SndioBackendFactory_init(SndioBackendFactory *self);
static DECLARE_FORWARD(SndioBackendFactory, ALCbackendFactory, void, deinit)
static ALCboolean SndioBackendFactory_querySupport(SndioBackendFactory *self, ALCbackend_Type type);
static void SndioBackendFactory_probe(SndioBackendFactory *self, enum DevProbe type, al_string *outnames);
static ALCbackend* SndioBackendFactory_createBackend(SndioBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(SndioBackendFactory);
ALCbackendFactory *SndioBackendFactory_getFactory(void)
{
static SndioBackendFactory factory = SNDIOBACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
static ALCboolean SndioBackendFactory_init(SndioBackendFactory* UNUSED(self))
{
/* No dynamic loading */
return ALC_TRUE;
}
static ALCboolean SndioBackendFactory_querySupport(SndioBackendFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Playback || type == ALCbackend_Capture)
return ALC_TRUE;
return ALC_FALSE;
}
static void SndioBackendFactory_probe(SndioBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
switch(type)
{
case ALL_DEVICE_PROBE:
AppendAllDevicesList(sndio_device);
break;
case CAPTURE_DEVICE_PROBE:
alstr_append_range(outnames, sndio_device, sndio_device+sizeof(sndio_device));
break;
}
}
static ALCbackend* SndioBackendFactory_createBackend(SndioBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
SndioPlayback *backend;
NEW_OBJ(backend, SndioPlayback)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
if(type == ALCbackend_Capture)
{
SndioCapture *backend;
NEW_OBJ(backend, SndioCapture)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
+181 -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
*/
@@ -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>
@@ -33,112 +34,171 @@
#include "alMain.h"
#include "alu.h"
#include "alconfig.h"
#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 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;
self->mix_data = NULL;
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(&self->killNow, almemory_order_acquire) &&
ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
{
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, "Failed to wait for playback buffer: %s", strerror(errno));
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, "Failed to write playback samples: %s",
strerror(errno));
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 ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
{
solaris_data *data = (solaris_data*)device->ExtraData;
close(data->fd);
free(data);
device->ExtraData = NULL;
}
static ALCboolean solaris_reset_playback(ALCdevice *device)
{
solaris_data *data = (solaris_data*)device->ExtraData;
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
audio_info_t info;
ALuint frameSize;
int numChannels;
ALsizei frameSize;
ALsizei numChannels;
AUDIO_INITINFO(&info);
@@ -146,7 +206,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 +234,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 +261,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, al_string *outnames);
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, al_string *outnames)
{
switch(type)
{
@@ -278,7 +337,7 @@ void alc_solaris_probe(enum DevProbe type)
struct stat buf;
if(stat(solaris_driver, &buf) == 0)
#endif
AppendAllDevicesList(solaris_device);
alstr_append_range(outnames, solaris_device, solaris_device+sizeof(solaris_device));
}
break;
@@ -286,3 +345,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;
}
+513
View File
@@ -0,0 +1,513 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2018 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 <psp2/audioin.h>
#include <psp2/audioout.h>
#include <stdlib.h>
#include "alMain.h"
#include "alu.h"
#include "ringbuffer.h"
#include "threads.h"
#include "backends/base.h"
#define AUDIO_SAMPLE_ALIGN(s) (((s) + 63) & ~63)
static const ALCchar playbackDeviceName[] = "PS Vita Speakers/Headphones";
static const ALCchar captureDeviceName[] = "PS Vita Microphone";
extern unsigned int _oal_thread_priority __attribute__((weak));
extern unsigned int _oal_thread_affinity __attribute__((weak));
// -----------------------------------------------------------------------------
// Playback
// -----------------------------------------------------------------------------
typedef struct ALCvitaPlayback
{
DERIVE_FROM_TYPE(ALCbackend);
ATOMIC(int) killNow;
SceUID thread;
SceKernelLwMutexWork lock;
int portNumber;
ALsizei frameSize;
void* waveBuffer;
ALuint Frequency;
enum DevFmtChannels FmtChans;
enum DevFmtType FmtType;
ALuint UpdateSize;
} ALCvitaPlayback;
static void ALCvitaPlayback_Construct(ALCvitaPlayback *self, ALCdevice *device);
static void ALCvitaPlayback_Destruct(ALCvitaPlayback *self);
static ALCenum ALCvitaPlayback_open(ALCvitaPlayback *self, const ALCchar *name);
static ALCboolean ALCvitaPlayback_reset(ALCvitaPlayback *self);
static ALCboolean ALCvitaPlayback_start(ALCvitaPlayback *self);
static void ALCvitaPlayback_stop(ALCvitaPlayback *self);
static void ALCvitaPlayback_lock(ALCvitaPlayback *self);
static void ALCvitaPlayback_unlock(ALCvitaPlayback *self);
static DECLARE_FORWARD2(ALCvitaPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCvitaPlayback, ALCbackend, ALCuint, availableSamples)
static DECLARE_FORWARD(ALCvitaPlayback, ALCbackend, ClockLatency, getClockLatency)
DECLARE_DEFAULT_ALLOCATORS(ALCvitaPlayback)
DEFINE_ALCBACKEND_VTABLE(ALCvitaPlayback);
static void ALCvitaPlayback_Construct(ALCvitaPlayback *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCvitaPlayback, ALCbackend, self);
device->UpdateSize = AUDIO_SAMPLE_ALIGN(device->UpdateSize);
self->portNumber = 0;
self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
self->Frequency = device->Frequency;
self->FmtChans = device->FmtChans;
self->FmtType = device->FmtType;
self->UpdateSize = device->UpdateSize;
sceKernelCreateLwMutex(
&self->lock,
"OpenAL Vita playback mutex",
SCE_KERNEL_MUTEX_ATTR_RECURSIVE, // No SCE_KERNEL_LW_MUTEX_ATTR_RECURSIVE in VitaSDK, but it's the same
0,
NULL
);
}
static void ALCvitaPlayback_Destruct(ALCvitaPlayback *self)
{
if (self->portNumber)
{
sceAudioOutReleasePort(self->portNumber);
self->portNumber = 0;
}
if (self->waveBuffer)
{
free(self->waveBuffer);
self->waveBuffer = NULL;
}
sceKernelDeleteLwMutex(&self->lock);
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
static int ALCvitaPlayback_MixerProc(SceSize args, void *argp)
{
(void)args;
ALCvitaPlayback *self = *(ALCvitaPlayback **) argp;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
while (!ATOMIC_LOAD(&self->killNow, almemory_order_acquire))
{
ALCvitaPlayback_lock(self);
aluMixData(device, self->waveBuffer, device->UpdateSize);
ALCvitaPlayback_unlock(self);
sceAudioOutOutput(self->portNumber, self->waveBuffer);
}
return 0;
}
static ALCenum ALCvitaPlayback_open(ALCvitaPlayback *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
/* Only signed short output sample format is supported */
device->FmtType = DevFmtShort;
/* Only mono/stereo channel configurations are supported */
if (device->FmtChans != DevFmtMono && device->FmtChans != DevFmtStereo)
device->FmtChans = DevFmtStereo;
device->UpdateSize = AUDIO_SAMPLE_ALIGN(device->UpdateSize);
self->portNumber = sceAudioOutOpenPort(
SCE_AUDIO_OUT_PORT_TYPE_BGM,
device->UpdateSize,
device->Frequency,
device->FmtChans == DevFmtStereo ? SCE_AUDIO_OUT_MODE_STEREO : SCE_AUDIO_OUT_MODE_MONO
);
if (self->portNumber < 0)
return ALC_INVALID_VALUE;
self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
self->Frequency = device->Frequency;
self->FmtChans = device->FmtChans;
self->FmtType = device->FmtType;
self->UpdateSize = device->UpdateSize;
self->waveBuffer = calloc(device->UpdateSize * self->frameSize, 1);
alstr_copy_cstr(&device->DeviceName, name ? name : playbackDeviceName);
return ALC_NO_ERROR;
}
static ALCboolean ALCvitaPlayback_reset(ALCvitaPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
if (device->FmtChans != DevFmtMono && device->FmtChans != DevFmtStereo)
device->FmtChans = DevFmtStereo;
device->UpdateSize = AUDIO_SAMPLE_ALIGN(device->UpdateSize);
sceAudioOutSetConfig(
self->portNumber,
device->UpdateSize,
device->Frequency,
device->FmtChans == DevFmtStereo ? SCE_AUDIO_OUT_MODE_STEREO : SCE_AUDIO_OUT_MODE_MONO
);
self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
self->Frequency = device->Frequency;
self->FmtChans = device->FmtChans;
self->FmtType = device->FmtType;
self->UpdateSize = device->UpdateSize;
if (self->waveBuffer)
{
free(self->waveBuffer);
}
self->waveBuffer = calloc(device->UpdateSize * self->frameSize, 1);
SetDefaultWFXChannelOrder(device);
return ALC_TRUE;
}
static ALCboolean ALCvitaPlayback_start(ALCvitaPlayback *self)
{
ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
int priority = 128; // middle
int affinity = 0; // DEFAULT
int stack_size = 0x10000; // 64Kib
if (&_oal_thread_priority != NULL) {
priority = _oal_thread_priority;
} else {
SceKernelThreadInfo info;
info.size = sizeof(SceKernelThreadInfo);
if (sceKernelGetThreadInfo(sceKernelGetThreadId(), &info) == 0) {
priority = info.currentPriority - 1;
}
}
if (priority < 64 && priority != 0) priority = 64;
if (priority > 191) priority = 191;
if (&_oal_thread_affinity != NULL) {
affinity = _oal_thread_affinity;
}
self->thread = sceKernelCreateThread("OpenAL Vita playback thread", ALCvitaPlayback_MixerProc,
priority, stack_size, 0, affinity, NULL);
if (self->thread < 0)
return ALC_FALSE;
int ret = sceKernelStartThread(self->thread, 4, &self);
if (ret < 0)
return ALC_FALSE;
return ALC_TRUE;
}
static void ALCvitaPlayback_stop(ALCvitaPlayback *self)
{
if (ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
return;
sceKernelWaitThreadEnd(self->thread, NULL, NULL);
sceKernelDeleteThread(self->thread);
}
static void ALCvitaPlayback_lock(ALCvitaPlayback *self)
{
sceKernelLockLwMutex(&self->lock, 1, NULL);
}
static void ALCvitaPlayback_unlock(ALCvitaPlayback *self)
{
sceKernelUnlockLwMutex(&self->lock, 1);
}
// -----------------------------------------------------------------------------
// Capture
// -----------------------------------------------------------------------------
typedef struct ALCvitaCapture
{
DERIVE_FROM_TYPE(ALCbackend);
ATOMIC(int) killNow;
althrd_t thread;
SceKernelLwMutexWork lock;
int portNumber;
ALsizei frameSize;
ll_ringbuffer_t *ring;
ALuint Frequency;
enum DevFmtChannels FmtChans;
enum DevFmtType FmtType;
ALuint UpdateSize;
} ALCvitaCapture;
static void ALCvitaCapture_Construct(ALCvitaCapture *self, ALCdevice *device);
static void ALCvitaCapture_Destruct(ALCvitaCapture *self);
static ALCenum ALCvitaCapture_open(ALCvitaCapture *self, const ALCchar *name);
static ALCboolean ALCvitaCapture_reset(ALCvitaCapture *self);
static ALCboolean ALCvitaCapture_start(ALCvitaCapture *self);
static void ALCvitaCapture_stop(ALCvitaCapture *self);
static void ALCvitaCapture_lock(ALCvitaCapture *self);
static void ALCvitaCapture_unlock(ALCvitaCapture *self);
static ALCenum ALCvitaCapture_captureSamples(ALCvitaCapture *self, ALCvoid *buffer, ALCuint samples);
static ALCuint ALCvitaCapture_availableSamples(ALCvitaCapture *self);
static DECLARE_FORWARD(ALCvitaCapture, ALCbackend, ClockLatency, getClockLatency)
DECLARE_DEFAULT_ALLOCATORS(ALCvitaCapture)
DEFINE_ALCBACKEND_VTABLE(ALCvitaCapture);
static void ALCvitaCapture_Construct(ALCvitaCapture *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCvitaCapture, ALCbackend, self);
self->portNumber = 0;
self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
self->Frequency = device->Frequency;
self->FmtChans = device->FmtChans;
self->FmtType = device->FmtType;
self->UpdateSize = device->UpdateSize;
}
static void ALCvitaCapture_Destruct(ALCvitaCapture *self)
{
if (self->portNumber)
{
sceAudioOutReleasePort(self->portNumber);
self->portNumber = 0;
}
if (self->ring)
{
ll_ringbuffer_free(self->ring);
self->ring = NULL;
}
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
static int ALCvitaCapture_MixerProc(void *ptr)
{
ALCvitaCapture *self = (ALCvitaCapture*)ptr;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
void* buf = malloc(self->frameSize * device->UpdateSize);
while (!ATOMIC_LOAD(&self->killNow, almemory_order_acquire))
{
sceAudioInInput(self->portNumber, buf);
ll_ringbuffer_write(self->ring, buf, self->frameSize * device->UpdateSize);
}
free(buf);
return 0;
}
static ALCenum ALCvitaCapture_open(ALCvitaCapture *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
/* Only signed short output sample format is supported */
device->FmtType = DevFmtShort;
/* Only mono channel configuration is supported */
if (device->FmtChans != DevFmtMono)
device->FmtChans = DevFmtMono;
/* TODO: Validate samplerate and update size */
self->portNumber = sceAudioInOpenPort(
SCE_AUDIO_IN_PORT_TYPE_RAW,
device->UpdateSize,
device->Frequency,
SCE_AUDIO_IN_PARAM_FORMAT_S16_MONO
);
if (self->portNumber < 0)
{
return ALC_INVALID_VALUE;
}
self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
self->Frequency = device->Frequency;
self->FmtChans = device->FmtChans;
self->FmtType = device->FmtType;
self->UpdateSize = device->UpdateSize;
self->ring = ll_ringbuffer_create(device->UpdateSize * device->NumUpdates, self->frameSize, false);
if (self->ring == NULL)
return ALC_INVALID_VALUE;
alstr_copy_cstr(&device->DeviceName, name ? name : captureDeviceName);
return ALC_NO_ERROR;
}
static ALCboolean ALCvitaCapture_reset(ALCvitaCapture *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
SetDefaultWFXChannelOrder(device);
return ALC_TRUE;
}
static ALCboolean ALCvitaCapture_start(ALCvitaCapture *self)
{
ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
if (althrd_create(&self->thread, ALCvitaCapture_MixerProc, self) != althrd_success)
return ALC_FALSE;
return ALC_TRUE;
}
static void ALCvitaCapture_stop(ALCvitaCapture *self)
{
int res;
if (ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
return;
althrd_join(self->thread, &res);
}
static void ALCvitaCapture_lock(ALCvitaCapture *self)
{
sceKernelLockLwMutex(&self->lock, 1, NULL);
}
static void ALCvitaCapture_unlock(ALCvitaCapture *self)
{
sceKernelUnlockLwMutex(&self->lock, 1);
}
static ALCuint ALCvitaCapture_availableSamples(ALCvitaCapture *self)
{
return ll_ringbuffer_read_space(self->ring);
}
static ALCenum ALCvitaCapture_captureSamples(ALCvitaCapture *self, ALCvoid *buffer, ALCuint samples)
{
ll_ringbuffer_read(self->ring, buffer, samples);
return ALC_NO_ERROR;
}
// -----------------------------------------------------------------------------
// Backends
// -----------------------------------------------------------------------------
typedef struct ALCvitaBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCvitaBackendFactory;
#define ALCvitaBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCvitaBackendFactory, ALCbackendFactory) } }
ALCbackendFactory *ALCvitaBackendFactory_getFactory(void);
static ALCboolean ALCvitaBackendFactory_init(ALCvitaBackendFactory *self);
static void ALCvitaBackendFactory_deinit(ALCvitaBackendFactory *self);
static ALCboolean ALCvitaBackendFactory_querySupport(ALCvitaBackendFactory *self, ALCbackend_Type type);
static void ALCvitaBackendFactory_probe(ALCvitaBackendFactory *self, enum DevProbe type, al_string *outnames);
static ALCbackend* ALCvitaBackendFactory_createBackend(ALCvitaBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCvitaBackendFactory);
ALCbackendFactory *ALCvitaBackendFactory_getFactory(void)
{
static ALCvitaBackendFactory factory = ALCvitaBACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
static ALCboolean ALCvitaBackendFactory_init(ALCvitaBackendFactory* UNUSED(self))
{
return AL_TRUE;
}
static void ALCvitaBackendFactory_deinit(ALCvitaBackendFactory* UNUSED(self))
{
}
static ALCboolean ALCvitaBackendFactory_querySupport(ALCvitaBackendFactory* UNUSED(self), ALCbackend_Type type)
{
if (type == ALCbackend_Playback || type == ALCbackend_Capture)
return ALC_TRUE;
return ALC_FALSE;
}
static void ALCvitaBackendFactory_probe(ALCvitaBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
if (type == ALL_DEVICE_PROBE)
alstr_append_range(outnames, playbackDeviceName, playbackDeviceName+sizeof(playbackDeviceName));
else if (type == CAPTURE_DEVICE_PROBE)
alstr_append_range(outnames, captureDeviceName, playbackDeviceName+sizeof(captureDeviceName));
}
static ALCbackend* ALCvitaBackendFactory_createBackend(ALCvitaBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
{
if (type == ALCbackend_Playback)
{
ALCvitaPlayback *backend;
NEW_OBJ(backend, ALCvitaPlayback)(device);
if (!backend)
return NULL;
return STATIC_CAST(ALCbackend, backend);
}
if (type == ALCbackend_Capture)
{
ALCvitaCapture *backend;
NEW_OBJ(backend, ALCvitaCapture)(device);
if (!backend)
return NULL;
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
File diff suppressed because it is too large Load Diff
+229 -153
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,14 @@
#include <stdio.h>
#include <memory.h>
#include <errno.h>
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#include "alMain.h"
#include "alu.h"
#include "alconfig.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 +45,87 @@ 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;
ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCwaveBackend;
static int ALCwaveBackend_mixerProc(void *ptr);
static void ALCwaveBackend_Construct(ALCwaveBackend *self, ALCdevice *device);
static void ALCwaveBackend_Destruct(ALCwaveBackend *self);
static ALCenum ALCwaveBackend_open(ALCwaveBackend *self, const ALCchar *name);
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;
ATOMIC_INIT(&self->killNow, AL_TRUE);
}
static void ALCwaveBackend_Destruct(ALCwaveBackend *self)
{
if(self->mFile)
fclose(self->mFile);
self->mFile = NULL;
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
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 +135,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 +143,8 @@ static int WaveProc(void *ptr)
ERR("Failed to get starting time\n");
return 1;
}
while(!data->killNow && device->Connected)
while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
{
if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC)
{
@@ -125,45 +163,49 @@ 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);
aluHandleDisconnect(device);
aluHandleDisconnect(device, "Failed to write playback samples");
ALCdevice_Unlock(device);
break;
}
@@ -173,52 +215,48 @@ 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 ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self)
{
wave_data *data = (wave_data*)device->ExtraData;
fclose(data->f);
free(data);
device->ExtraData = NULL;
}
static ALCboolean wave_reset_playback(ALCdevice *device)
{
wave_data *data = (wave_data*)device->ExtraData;
ALuint channels=0, bits=0;
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,141 +275,179 @@ 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)
ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
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(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
return;
althrd_join(self->thread, &res);
data->killNow = 1;
althrd_join(data->thread, &res);
free(self->mBuffer);
self->mBuffer = NULL;
free(data->buffer);
data->buffer = 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, al_string *outnames);
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, al_string *outnames)
{
if(!ConfigValueExists("wave", "file"))
return;
switch(type)
{
case ALL_DEVICE_PROBE:
AppendAllDevicesList(waveDevice);
alstr_append_range(outnames, waveDevice, waveDevice+sizeof(waveDevice));
break;
case CAPTURE_DEVICE_PROBE:
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;
}
+422 -352
View File
File diff suppressed because it is too large Load Diff
+492
View File
@@ -0,0 +1,492 @@
#include "config.h"
#include "bformatdec.h"
#include "ambdec.h"
#include "filters/splitter.h"
#include "alu.h"
#include "bool.h"
#include "threads.h"
#include "almalloc.h"
/* NOTE: These are scale factors as applied to Ambisonics content. Decoder
* coefficients should be divided by these values to get proper N3D scalings.
*/
const ALfloat N3D2N3DScale[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
};
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) */
};
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) */
};
#define HF_BAND 0
#define LF_BAND 1
#define NUM_BANDS 2
/* 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][MAX_AMBI_COEFFS] = {
{ 0.125f, 0.125f, 0.125f, 0.125f },
{ 0.125f, -0.125f, 0.125f, 0.125f },
{ 0.125f, 0.125f, 0.125f, -0.125f },
{ 0.125f, -0.125f, 0.125f, -0.125f },
{ 0.125f, 0.125f, -0.125f, 0.125f },
{ 0.125f, -0.125f, -0.125f, 0.125f },
{ 0.125f, 0.125f, -0.125f, -0.125f },
{ 0.125f, -0.125f, -0.125f, -0.125f },
};
static const ALfloat Ambi3DDecoderHFScale[MAX_AMBI_COEFFS] = {
2.0f,
1.15470054f, 1.15470054f, 1.15470054f
};
/* NOTE: BandSplitter filters are unused with single-band decoding */
typedef struct BFormatDec {
ALuint Enabled; /* Bitfield of enabled channels. */
union {
alignas(16) ALfloat Dual[MAX_OUTPUT_CHANNELS][NUM_BANDS][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[NUM_BANDS];
} UpSampler[4];
ALsizei NumChannels;
ALboolean DualBand;
} BFormatDec;
BFormatDec *bformatdec_alloc()
{
return al_calloc(16, sizeof(BFormatDec));
}
void bformatdec_free(BFormatDec **dec)
{
if(dec && *dec)
{
al_free((*dec)->Samples);
(*dec)->Samples = NULL;
(*dec)->SamplesHF = NULL;
(*dec)->SamplesLF = NULL;
al_free(*dec);
*dec = NULL;
}
}
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 = N3D2N3DScale;
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;
dec->Enabled = 0;
for(i = 0;i < conf->NumSpeakers;i++)
dec->Enabled |= 1 << chanmap[i];
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[HF_BAND] = (conf->ChanMask > 0x1ff) ? W_SCALE_3H3P :
(conf->ChanMask > 0xf) ? W_SCALE_2H2P : 1.0f;
dec->UpSampler[0].Gains[LF_BAND] = 1.0f;
for(i = 1;i < 4;i++)
{
dec->UpSampler[i].Gains[HF_BAND] = (conf->ChanMask > 0x1ff) ? XYZ_SCALE_3H3P :
(conf->ChanMask > 0xf) ? XYZ_SCALE_2H2P : 1.0f;
dec->UpSampler[i].Gains[LF_BAND] = 1.0f;
}
}
else
{
periphonic = false;
dec->UpSampler[0].Gains[HF_BAND] = (conf->ChanMask > 0x1ff) ? W_SCALE_3H0P :
(conf->ChanMask > 0xf) ? W_SCALE_2H0P : 1.0f;
dec->UpSampler[0].Gains[LF_BAND] = 1.0f;
for(i = 1;i < 3;i++)
{
dec->UpSampler[i].Gains[HF_BAND] = (conf->ChanMask > 0x1ff) ? XYZ_SCALE_3H0P :
(conf->ChanMask > 0xf) ? XYZ_SCALE_2H0P : 1.0f;
dec->UpSampler[i].Gains[LF_BAND] = 1.0f;
}
dec->UpSampler[3].Gains[HF_BAND] = 0.0f;
dec->UpSampler[3].Gains[LF_BAND] = 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][HF_BAND][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][LF_BAND][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][HF_BAND][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][LF_BAND][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&(1<<chan)))
continue;
memset(dec->ChannelMix, 0, SamplesToDo*sizeof(ALfloat));
MixRowSamples(dec->ChannelMix, dec->Matrix.Dual[chan][HF_BAND],
dec->SamplesHF, dec->NumChannels, 0, SamplesToDo
);
MixRowSamples(dec->ChannelMix, dec->Matrix.Dual[chan][LF_BAND],
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&(1<<chan)))
continue;
memset(dec->ChannelMix, 0, SamplesToDo*sizeof(ALfloat));
MixRowSamples(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[HF_BAND], dec->Samples[LF_BAND],
InSamples[i], SamplesToDo
);
/* Now write each band to the output. */
MixRowSamples(OutBuffer[i], dec->UpSampler[i].Gains,
dec->Samples, NUM_BANDS, 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[NUM_BANDS][BUFFERSIZE];
BandSplitter XOver[4];
ALfloat Gains[4][MAX_OUTPUT_CHANNELS][NUM_BANDS];
} AmbiUpsampler;
AmbiUpsampler *ambiup_alloc()
{
return al_calloc(16, sizeof(AmbiUpsampler));
}
void ambiup_free(struct AmbiUpsampler **ambiup)
{
if(ambiup)
{
al_free(*ambiup);
*ambiup = NULL;
}
}
void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device, ALfloat w_scale, ALfloat xyz_scale)
{
ALfloat ratio;
ALsizei 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(k = 0;k < COUNTOF(Ambi3DPoints);k++)
{
ALfloat coeffs[MAX_AMBI_COEFFS] = { 0.0f };
CalcDirectionCoeffs(Ambi3DPoints[k], 0.0f, coeffs);
ComputePanGains(&device->Dry, coeffs, 1.0f, encgains[k]);
}
/* 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++)
{
ALdouble gain = 0.0;
for(k = 0;k < COUNTOF(Ambi3DDecoder);k++)
gain += (ALdouble)Ambi3DDecoder[k][i] * encgains[k][j];
ambiup->Gains[i][j][HF_BAND] = (ALfloat)(gain * Ambi3DDecoderHFScale[i]);
ambiup->Gains[i][j][LF_BAND] = (ALfloat)gain;
}
}
}
else
{
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][HF_BAND] = scale * ((i==0) ? w_scale : xyz_scale);
ambiup->Gains[i][index][LF_BAND] = 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[HF_BAND], ambiup->Samples[LF_BAND],
InSamples[i], SamplesToDo
);
for(j = 0;j < OutChannels;j++)
MixRowSamples(OutBuffer[j], ambiup->Gains[i][j],
ambiup->Samples, NUM_BANDS, 0, SamplesToDo
);
}
}
+57
View File
@@ -0,0 +1,57 @@
#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_SCALE_2H0P 1.224744871f /* sqrt(1.5) */
#define XYZ_SCALE_2H0P 1.0f
#define W_SCALE_3H0P 1.414213562f /* sqrt(2) */
#define XYZ_SCALE_3H0P 1.082392196f
/* These are the necessary scales for first-order HF responses to play over
* higher-order 3D (periphonic) decoders.
*/
#define W_SCALE_2H2P 1.341640787f /* sqrt(1.8) */
#define XYZ_SCALE_2H2P 1.0f
#define W_SCALE_3H3P 1.695486018f
#define XYZ_SCALE_3H3P 1.136697713f
/* NOTE: These are scale factors as applied to Ambisonics content. Decoder
* coefficients should be divided by these values to get proper N3D scalings.
*/
extern const ALfloat N3D2N3DScale[MAX_AMBI_COEFFS];
extern const ALfloat SN3D2N3DScale[MAX_AMBI_COEFFS];
extern const ALfloat FuMa2N3DScale[MAX_AMBI_COEFFS];
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. Assumes a periphonic (4-channel) input mix!
*/
struct AmbiUpsampler *ambiup_alloc();
void ambiup_free(struct AmbiUpsampler **ambiup);
void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device, ALfloat w_scale, ALfloat xyz_scale);
void ambiup_process(struct AmbiUpsampler *ambiup, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo);
#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 */
+25
View File
@@ -1,6 +1,12 @@
#ifndef AL_COMPAT_H
#define AL_COMPAT_H
#include "alstring.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
@@ -23,10 +29,29 @@ 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);
void GetProcBinary(al_string *path, al_string *fname);
#ifdef HAVE_DYNLOAD
void *LoadLib(const char *name);
void CloseLib(void *handle);
void *GetSymbol(void *handle, const char *name);
#endif
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* AL_COMPAT_H */
+468
View File
@@ -0,0 +1,468 @@
#include "config.h"
#include "converter.h"
#include "fpu_modes.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 = (ALsizei)mind(((ALdouble)srcRate/dstRate*FRACTIONONE) + 0.5,
MAX_PITCH * FRACTIONONE);
converter->mIncrement = maxi(step, 1);
if(converter->mIncrement == FRACTIONONE)
converter->mResample = Resample_copy_C;
else
{
/* TODO: Allow other resamplers. */
BsincPrepare(converter->mIncrement, &converter->mState.bsinc, &bsinc12);
converter->mResample = SelectResampler(BSinc12Resampler);
}
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_RESAMPLE_PADDING*2 &&
MAX_RESAMPLE_PADDING*2 - prepcount >= srcframes)
{
/* Not enough input samples to generate an output sample. */
return 0;
}
DataSize64 = prepcount;
DataSize64 += srcframes;
DataSize64 -= MAX_RESAMPLE_PADDING*2;
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_RESAMPLE_PADDING*2);
if(prepcount < MAX_RESAMPLE_PADDING*2 &&
MAX_RESAMPLE_PADDING*2 - 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_RESAMPLE_PADDING*2;
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_RESAMPLE_PADDING*2, 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_RESAMPLE_PADDING, 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(prepcount + toread - (DataPosFrac>>FRACTIONBITS),
MAX_RESAMPLE_PADDING*2);
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_RESAMPLE_PADDING*2];
} 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 */
+15
View File
@@ -0,0 +1,15 @@
#ifndef CPU_CAPS_H
#define CPU_CAPS_H
extern int CPUCapFlags;
enum {
CPU_CAP_SSE = 1<<0,
CPU_CAP_SSE2 = 1<<1,
CPU_CAP_SSE3 = 1<<2,
CPU_CAP_SSE4_1 = 1<<3,
CPU_CAP_NEON = 1<<4,
};
void FillCPUCaps(int capfilter);
#endif /* CPU_CAPS_H */
+204 -155
View File
@@ -1,6 +1,6 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2013 by Anis A. Hireche, Nasca Octavian Paul
* Copyright (C) 2018 by Raul Herraiz.
* 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
@@ -13,188 +13,220 @@
*
* 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 <math.h>
#include <stdlib.h>
#include "config.h"
#include "alu.h"
#include "alFilter.h"
#include "alError.h"
#include "alMain.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
#include "filters/defs.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.
*/
#define MIN_FREQ 20.0f
#define MAX_FREQ 2500.0f
#define Q_FACTOR 5.0f
typedef struct ALautowahState {
DERIVE_FROM_TYPE(ALeffectState);
/* Effect gains for each channel */
ALfloat Gain[MaxChannels];
/* Effect parameters */
ALfloat AttackRate;
ALfloat ReleaseRate;
ALfloat Resonance;
ALfloat ResonanceGain;
ALfloat PeakGain;
ALfloat GainCtrl;
ALfloat Frequency;
ALfloat FreqMinNorm;
ALfloat BandwidthNorm;
ALfloat env_delay;
/* Samples processing */
ALfilterState LowPass;
/* Filter components derived from the envelope. */
struct {
ALfloat cos_w0;
ALfloat alpha;
} Env[BUFFERSIZE];
struct {
/* Effect filters' history. */
struct {
ALfloat z1, z2;
} Filter;
/* Effect gains for each output channel */
ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
} Chans[MAX_EFFECT_CHANNELS];
/* Effects buffers */
alignas(16) ALfloat BufferOut[BUFFERSIZE];
} 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;
}
}
static ALvoid ALautowahState_Destruct(ALautowahState *state);
static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *device);
static ALvoid ALautowahState_update(ALautowahState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
static ALvoid ALautowahState_process(ALautowahState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
DECLARE_DEFAULT_ALLOCATORS(ALautowahState)
DEFINE_ALEFFECTSTATE_VTABLE(ALautowahState);
static void ALautowahState_Construct(ALautowahState *state)
{
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
SET_VTABLE2(ALautowahState, ALeffectState, state);
}
typedef struct ALautowahStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALautowahStateFactory;
static ALvoid ALautowahState_Destruct(ALautowahState *state)
{
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
static ALeffectState *ALautowahStateFactory_create(ALautowahStateFactory *UNUSED(factory))
static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *UNUSED(device))
{
/* (Re-)initializing parameters and clear the buffers. */
ALsizei i, j;
state->AttackRate = 1.0f;
state->ReleaseRate = 1.0f;
state->ResonanceGain = 10.0f;
state->PeakGain = 4.5f;
state->FreqMinNorm = 4.5e-4f;
state->BandwidthNorm = 0.05f;
state->env_delay = 0.0f;
memset(state->Env, 0, sizeof(state->Env));
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
{
for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
state->Chans[i].CurrentGains[j] = 0.0f;
state->Chans[i].Filter.z1 = 0.0f;
state->Chans[i].Filter.z2 = 0.0f;
}
return AL_TRUE;
}
static ALvoid ALautowahState_update(ALautowahState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
{
const ALCdevice *device = context->Device;
ALfloat ReleaseTime;
ALsizei i;
ReleaseTime = clampf(props->Autowah.ReleaseTime, 0.001f, 1.0f);
state->AttackRate = expf(-1.0f / (props->Autowah.AttackTime*device->Frequency));
state->ReleaseRate = expf(-1.0f / (ReleaseTime*device->Frequency));
/* 0-20dB Resonance Peak gain */
state->ResonanceGain = sqrtf(log10f(props->Autowah.Resonance)*10.0f / 3.0f);
state->PeakGain = 1.0f - log10f(props->Autowah.PeakGain/AL_AUTOWAH_MAX_PEAK_GAIN);
state->FreqMinNorm = MIN_FREQ / device->Frequency;
state->BandwidthNorm = (MAX_FREQ-MIN_FREQ) / device->Frequency;
STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain,
state->Chans[i].TargetGains);
}
static ALvoid ALautowahState_process(ALautowahState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
const ALfloat attack_rate = state->AttackRate;
const ALfloat release_rate = state->ReleaseRate;
const ALfloat res_gain = state->ResonanceGain;
const ALfloat peak_gain = state->PeakGain;
const ALfloat freq_min = state->FreqMinNorm;
const ALfloat bandwidth = state->BandwidthNorm;
ALfloat env_delay;
ALsizei c, i;
env_delay = state->env_delay;
for(i = 0;i < SamplesToDo;i++)
{
ALfloat w0, sample, a;
/* Envelope follower described on the book: Audio Effects, Theory,
* Implementation and Application.
*/
sample = peak_gain * fabsf(SamplesIn[0][i]);
a = (sample > env_delay) ? attack_rate : release_rate;
env_delay = lerp(sample, env_delay, a);
/* Calculate the cos and alpha components for this sample's filter. */
w0 = minf((bandwidth*env_delay + freq_min), 0.46f) * F_TAU;
state->Env[i].cos_w0 = cosf(w0);
state->Env[i].alpha = sinf(w0)/(2.0f * Q_FACTOR);
}
state->env_delay = env_delay;
for(c = 0;c < MAX_EFFECT_CHANNELS; c++)
{
/* This effectively inlines BiquadFilter_setParams for a peaking
* filter and BiquadFilter_processC. The alpha and cosine components
* for the filter coefficients were previously calculated with the
* envelope. Because the filter changes for each sample, the
* coefficients are transient and don't need to be held.
*/
ALfloat z1 = state->Chans[c].Filter.z1;
ALfloat z2 = state->Chans[c].Filter.z2;
for(i = 0;i < SamplesToDo;i++)
{
const ALfloat alpha = state->Env[i].alpha;
const ALfloat cos_w0 = state->Env[i].cos_w0;
ALfloat input, output;
ALfloat a[3], b[3];
b[0] = 1.0f + alpha*res_gain;
b[1] = -2.0f * cos_w0;
b[2] = 1.0f - alpha*res_gain;
a[0] = 1.0f + alpha/res_gain;
a[1] = -2.0f * cos_w0;
a[2] = 1.0f - alpha/res_gain;
input = SamplesIn[c][i];
output = input*(b[0]/a[0]) + z1;
z1 = input*(b[1]/a[0]) - output*(a[1]/a[0]) + z2;
z2 = input*(b[2]/a[0]) - output*(a[2]/a[0]);
state->BufferOut[i] = output;
}
state->Chans[c].Filter.z1 = z1;
state->Chans[c].Filter.z2 = z2;
/* Now, mix the processed sound data to the output. */
MixSamples(state->BufferOut, NumChannels, SamplesOut, state->Chans[c].CurrentGains,
state->Chans[c].TargetGains, SamplesToDo, 0, SamplesToDo);
}
}
typedef struct AutowahStateFactory {
DERIVE_FROM_TYPE(EffectStateFactory);
} AutowahStateFactory;
static ALeffectState *AutowahStateFactory_create(AutowahStateFactory *UNUSED(factory))
{
ALautowahState *state;
state = ALautowahState_New(sizeof(*state));
NEW_OBJ0(state, ALautowahState)();
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);
DEFINE_EFFECTSTATEFACTORY_VTABLE(AutowahStateFactory);
ALeffectStateFactory *ALautowahStateFactory_getFactory(void)
EffectStateFactory *AutowahStateFactory_getFactory(void)
{
static ALautowahStateFactory AutowahFactory = { { GET_VTABLE2(ALautowahStateFactory, ALeffectStateFactory) } };
static AutowahStateFactory AutowahFactory = { { GET_VTABLE2(AutowahStateFactory, EffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &AutowahFactory);
return STATIC_CAST(EffectStateFactory, &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;
@@ -202,45 +234,60 @@ void ALautowah_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, AL
{
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);
SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah attack time out of range");
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);
SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah release time out of range");
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);
SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah resonance out of range");
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);
SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah peak gain out of range");
props->Autowah.PeakGain = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
alSetError(context, AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param);
}
}
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)
void ALautowah_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
{
ALautowah_getParami(effect, context, param, vals);
alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param);
}
void ALautowah_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
{
alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", param);
}
void ALautowah_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
{
alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param);
}
void ALautowah_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
{
alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", param);
}
void ALautowah_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
@@ -261,9 +308,11 @@ void ALautowah_getParamf(const ALeffect *effect, ALCcontext *context, ALenum par
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
alSetError(context, AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param);
}
}
void ALautowah_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALautowah_getParamf(effect, context, param, vals);
+395 -237
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,259 +24,289 @@
#include <stdlib.h>
#include "alMain.h"
#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
#include "filters/defs.h"
enum ChorusWaveForm {
CWF_Triangle = AL_CHORUS_WAVEFORM_TRIANGLE,
CWF_Sinusoid = AL_CHORUS_WAVEFORM_SINUSOID
static_assert(AL_CHORUS_WAVEFORM_SINUSOID == AL_FLANGER_WAVEFORM_SINUSOID, "Chorus/Flanger waveform value mismatch");
static_assert(AL_CHORUS_WAVEFORM_TRIANGLE == AL_FLANGER_WAVEFORM_TRIANGLE, "Chorus/Flanger waveform value mismatch");
enum WaveForm {
WF_Sinusoid,
WF_Triangle
};
typedef struct ALchorusState {
DERIVE_FROM_TYPE(ALeffectState);
ALfloat *SampleBuffer[2];
ALuint BufferLength;
ALuint offset;
ALuint lfo_range;
ALfloat *SampleBuffer;
ALsizei BufferLength;
ALsizei offset;
ALsizei lfo_offset;
ALsizei lfo_range;
ALfloat lfo_scale;
ALint lfo_disp;
/* Gains for left and right sides */
ALfloat Gain[2][MaxChannels];
struct {
ALfloat Current[MAX_OUTPUT_CHANNELS];
ALfloat Target[MAX_OUTPUT_CHANNELS];
} Gains[2];
/* effect parameters */
enum ChorusWaveForm waveform;
enum WaveForm waveform;
ALint delay;
ALfloat depth;
ALfloat feedback;
} ALchorusState;
static ALvoid ALchorusState_Destruct(ALchorusState *state)
{
free(state->SampleBuffer[0]);
state->SampleBuffer[0] = NULL;
state->SampleBuffer[1] = NULL;
}
static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device)
{
ALuint maxlen;
ALuint it;
maxlen = fastf2u(AL_CHORUS_MAX_DELAY * 3.0f * Device->Frequency) + 1;
maxlen = NextPowerOf2(maxlen);
if(maxlen != state->BufferLength)
{
void *temp;
temp = realloc(state->SampleBuffer[0], maxlen * sizeof(ALfloat) * 2);
if(!temp) return AL_FALSE;
state->SampleBuffer[0] = temp;
state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen;
state->BufferLength = maxlen;
}
for(it = 0;it < state->BufferLength;it++)
{
state->SampleBuffer[0][it] = 0.0f;
state->SampleBuffer[1][it] = 0.0f;
}
return AL_TRUE;
}
static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, const ALeffectslot *Slot)
{
ALfloat frequency = (ALfloat)Device->Frequency;
ALfloat rate;
ALint phase;
switch(Slot->EffectProps.Chorus.Waveform)
{
case AL_CHORUS_WAVEFORM_TRIANGLE:
state->waveform = CWF_Triangle;
break;
case AL_CHORUS_WAVEFORM_SINUSOID:
state->waveform = CWF_Sinusoid;
break;
}
state->depth = Slot->EffectProps.Chorus.Depth;
state->feedback = Slot->EffectProps.Chorus.Feedback;
state->delay = fastf2i(Slot->EffectProps.Chorus.Delay * frequency);
/* Gains for left and right sides */
ComputeAngleGains(Device, atan2f(-1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[0]);
ComputeAngleGains(Device, atan2f(+1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[1]);
phase = Slot->EffectProps.Chorus.Phase;
rate = Slot->EffectProps.Chorus.Rate;
if(!(rate > 0.0f))
{
state->lfo_scale = 0.0f;
state->lfo_range = 1;
state->lfo_disp = 0;
}
else
{
/* Calculate LFO coefficient */
state->lfo_range = fastf2u(frequency/rate + 0.5f);
switch(state->waveform)
{
case CWF_Triangle:
state->lfo_scale = 4.0f / state->lfo_range;
break;
case CWF_Sinusoid:
state->lfo_scale = F_2PI / state->lfo_range;
break;
}
/* Calculate lfo phase displacement */
state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f));
}
}
static inline void Triangle(ALint *delay_left, ALint *delay_right, ALuint offset, const ALchorusState *state)
{
ALfloat lfo_value;
lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
lfo_value *= state->depth * state->delay;
*delay_left = fastf2i(lfo_value) + state->delay;
offset += state->lfo_disp;
lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
lfo_value *= state->depth * state->delay;
*delay_right = fastf2i(lfo_value) + state->delay;
}
static inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALuint offset, const ALchorusState *state)
{
ALfloat lfo_value;
lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
lfo_value *= state->depth * state->delay;
*delay_left = fastf2i(lfo_value) + state->delay;
offset += state->lfo_disp;
lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
lfo_value *= state->depth * state->delay;
*delay_right = fastf2i(lfo_value) + state->delay;
}
#define DECL_TEMPLATE(Func) \
static void Process##Func(ALchorusState *state, const ALuint SamplesToDo, \
const ALfloat *restrict SamplesIn, ALfloat (*restrict out)[2]) \
{ \
const ALuint bufmask = state->BufferLength-1; \
ALfloat *restrict leftbuf = state->SampleBuffer[0]; \
ALfloat *restrict rightbuf = state->SampleBuffer[1]; \
ALuint offset = state->offset; \
const ALfloat feedback = state->feedback; \
ALuint it; \
\
for(it = 0;it < SamplesToDo;it++) \
{ \
ALint delay_left, delay_right; \
Func(&delay_left, &delay_right, offset, state); \
\
out[it][0] = leftbuf[(offset-delay_left)&bufmask]; \
leftbuf[offset&bufmask] = (out[it][0]+SamplesIn[it]) * feedback; \
\
out[it][1] = rightbuf[(offset-delay_right)&bufmask]; \
rightbuf[offset&bufmask] = (out[it][1]+SamplesIn[it]) * feedback; \
\
offset++; \
} \
state->offset = offset; \
}
DECL_TEMPLATE(Triangle)
DECL_TEMPLATE(Sinusoid)
#undef DECL_TEMPLATE
static ALvoid ALchorusState_process(ALchorusState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
{
ALuint it, kt;
ALuint base;
for(base = 0;base < SamplesToDo;)
{
ALfloat temps[64][2];
ALuint td = minu(SamplesToDo-base, 64);
switch(state->waveform)
{
case CWF_Triangle:
ProcessTriangle(state, td, SamplesIn+base, temps);
break;
case CWF_Sinusoid:
ProcessSinusoid(state, td, SamplesIn+base, temps);
break;
}
for(kt = 0;kt < MaxChannels;kt++)
{
ALfloat gain = state->Gain[0][kt];
if(gain > GAIN_SILENCE_THRESHOLD)
{
for(it = 0;it < td;it++)
SamplesOut[kt][it+base] += temps[it][0] * gain;
}
gain = state->Gain[1][kt];
if(gain > GAIN_SILENCE_THRESHOLD)
{
for(it = 0;it < td;it++)
SamplesOut[kt][it+base] += temps[it][1] * gain;
}
}
base += td;
}
}
static ALvoid ALchorusState_Destruct(ALchorusState *state);
static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device);
static ALvoid ALchorusState_update(ALchorusState *state, const ALCcontext *Context, 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);
typedef struct ALchorusStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALchorusStateFactory;
static ALeffectState *ALchorusStateFactory_create(ALchorusStateFactory *UNUSED(factory))
static void ALchorusState_Construct(ALchorusState *state)
{
ALchorusState *state;
state = ALchorusState_New(sizeof(*state));
if(!state) return NULL;
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
SET_VTABLE2(ALchorusState, ALeffectState, state);
state->BufferLength = 0;
state->SampleBuffer[0] = NULL;
state->SampleBuffer[1] = NULL;
state->SampleBuffer = NULL;
state->offset = 0;
state->lfo_offset = 0;
state->lfo_range = 1;
state->waveform = CWF_Triangle;
state->waveform = WF_Triangle;
}
static ALvoid ALchorusState_Destruct(ALchorusState *state)
{
al_free(state->SampleBuffer);
state->SampleBuffer = NULL;
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device)
{
const ALfloat max_delay = maxf(AL_CHORUS_MAX_DELAY, AL_FLANGER_MAX_DELAY);
ALsizei maxlen;
maxlen = NextPowerOf2(float2int(max_delay*2.0f*Device->Frequency) + 1u);
if(maxlen <= 0) return AL_FALSE;
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;
}
memset(state->SampleBuffer, 0, state->BufferLength*sizeof(ALfloat));
memset(state->Gains, 0, sizeof(state->Gains));
return AL_TRUE;
}
static ALvoid ALchorusState_update(ALchorusState *state, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props)
{
const ALsizei mindelay = MAX_RESAMPLE_PADDING << FRACTIONBITS;
const ALCdevice *device = Context->Device;
ALfloat frequency = (ALfloat)device->Frequency;
ALfloat coeffs[MAX_AMBI_COEFFS];
ALfloat rate;
ALint phase;
switch(props->Chorus.Waveform)
{
case AL_CHORUS_WAVEFORM_TRIANGLE:
state->waveform = WF_Triangle;
break;
case AL_CHORUS_WAVEFORM_SINUSOID:
state->waveform = WF_Sinusoid;
break;
}
/* The LFO depth is scaled to be relative to the sample delay. Clamp the
* delay and depth to allow enough padding for resampling.
*/
state->delay = maxi(float2int(props->Chorus.Delay*frequency*FRACTIONONE + 0.5f),
mindelay);
state->depth = minf(props->Chorus.Depth * state->delay,
(ALfloat)(state->delay - mindelay));
state->feedback = props->Chorus.Feedback;
/* Gains for left and right sides */
CalcAngleCoeffs(-F_PI_2, 0.0f, 0.0f, coeffs);
ComputePanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[0].Target);
CalcAngleCoeffs( F_PI_2, 0.0f, 0.0f, coeffs);
ComputePanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[1].Target);
phase = props->Chorus.Phase;
rate = props->Chorus.Rate;
if(!(rate > 0.0f))
{
state->lfo_offset = 0;
state->lfo_range = 1;
state->lfo_scale = 0.0f;
state->lfo_disp = 0;
}
else
{
/* Calculate LFO coefficient (number of samples per cycle). Limit the
* max range to avoid overflow when calculating the displacement.
*/
ALsizei lfo_range = float2int(minf(frequency/rate + 0.5f, (ALfloat)(INT_MAX/360 - 180)));
state->lfo_offset = float2int((ALfloat)state->lfo_offset/state->lfo_range*
lfo_range + 0.5f) % lfo_range;
state->lfo_range = lfo_range;
switch(state->waveform)
{
case WF_Triangle:
state->lfo_scale = 4.0f / state->lfo_range;
break;
case WF_Sinusoid:
state->lfo_scale = F_TAU / state->lfo_range;
break;
}
/* Calculate lfo phase displacement */
if(phase < 0) phase = 360 + phase;
state->lfo_disp = (state->lfo_range*phase + 180) / 360;
}
}
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)
{
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 void GetSinusoidDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
const ALsizei todo)
{
ALsizei i;
for(i = 0;i < todo;i++)
{
delays[i] = fastf2i(sinf(lfo_scale*offset) * depth) + delay;
offset = (offset+1)%lfo_range;
}
}
static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
const ALsizei bufmask = state->BufferLength-1;
const ALfloat feedback = state->feedback;
const ALsizei avgdelay = (state->delay + (FRACTIONONE>>1)) >> FRACTIONBITS;
ALfloat *restrict delaybuf = state->SampleBuffer;
ALsizei offset = state->offset;
ALsizei i, c;
ALsizei base;
for(base = 0;base < SamplesToDo;)
{
const ALsizei todo = mini(256, SamplesToDo-base);
ALint moddelays[2][256];
alignas(16) ALfloat temps[2][256];
if(state->waveform == WF_Sinusoid)
{
GetSinusoidDelays(moddelays[0], state->lfo_offset, state->lfo_range, state->lfo_scale,
state->depth, state->delay, todo);
GetSinusoidDelays(moddelays[1], (state->lfo_offset+state->lfo_disp)%state->lfo_range,
state->lfo_range, state->lfo_scale, state->depth, state->delay,
todo);
}
else /*if(state->waveform == WF_Triangle)*/
{
GetTriangleDelays(moddelays[0], state->lfo_offset, state->lfo_range, state->lfo_scale,
state->depth, state->delay, todo);
GetTriangleDelays(moddelays[1], (state->lfo_offset+state->lfo_disp)%state->lfo_range,
state->lfo_range, state->lfo_scale, state->depth, state->delay,
todo);
}
state->lfo_offset = (state->lfo_offset+todo) % state->lfo_range;
for(i = 0;i < todo;i++)
{
ALint delay;
ALfloat mu;
// Feed the buffer's input first (necessary for delays < 1).
delaybuf[offset&bufmask] = SamplesIn[0][base+i];
// Tap for the left output.
delay = offset - (moddelays[0][i]>>FRACTIONBITS);
mu = (moddelays[0][i]&FRACTIONMASK) * (1.0f/FRACTIONONE);
temps[0][i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay ) & bufmask],
delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask],
mu);
// Tap for the right output.
delay = offset - (moddelays[1][i]>>FRACTIONBITS);
mu = (moddelays[1][i]&FRACTIONMASK) * (1.0f/FRACTIONONE);
temps[1][i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay ) & bufmask],
delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask],
mu);
// Accumulate feedback from the average delay of the taps.
delaybuf[offset&bufmask] += delaybuf[(offset-avgdelay) & bufmask] * feedback;
offset++;
}
for(c = 0;c < 2;c++)
MixSamples(temps[c], NumChannels, SamplesOut, state->Gains[c].Current,
state->Gains[c].Target, SamplesToDo-base, base, todo);
base += todo;
}
state->offset = offset;
}
typedef struct ChorusStateFactory {
DERIVE_FROM_TYPE(EffectStateFactory);
} ChorusStateFactory;
static ALeffectState *ChorusStateFactory_create(ChorusStateFactory *UNUSED(factory))
{
ALchorusState *state;
NEW_OBJ0(state, ALchorusState)();
if(!state) return NULL;
return STATIC_CAST(ALeffectState, state);
}
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALchorusStateFactory);
DEFINE_EFFECTSTATEFACTORY_VTABLE(ChorusStateFactory);
ALeffectStateFactory *ALchorusStateFactory_getFactory(void)
EffectStateFactory *ChorusStateFactory_getFactory(void)
{
static ALchorusStateFactory ChorusFactory = { { GET_VTABLE2(ALchorusStateFactory, ALeffectStateFactory) } };
static ChorusStateFactory ChorusFactory = { { GET_VTABLE2(ChorusStateFactory, EffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &ChorusFactory);
return STATIC_CAST(EffectStateFactory, &ChorusFactory);
}
@@ -287,24 +317,22 @@ void ALchorus_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALi
{
case AL_CHORUS_WAVEFORM:
if(!(val >= AL_CHORUS_MIN_WAVEFORM && val <= AL_CHORUS_MAX_WAVEFORM))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid chorus waveform");
props->Chorus.Waveform = val;
break;
case AL_CHORUS_PHASE:
if(!(val >= AL_CHORUS_MIN_PHASE && val <= AL_CHORUS_MAX_PHASE))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus phase out of range");
props->Chorus.Phase = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
alSetError(context, AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param);
}
}
void ALchorus_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALchorus_setParami(effect, context, param, vals[0]);
}
{ ALchorus_setParami(effect, context, param, vals[0]); }
void ALchorus_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
@@ -312,36 +340,34 @@ void ALchorus_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALf
{
case AL_CHORUS_RATE:
if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus rate out of range");
props->Chorus.Rate = val;
break;
case AL_CHORUS_DEPTH:
if(!(val >= AL_CHORUS_MIN_DEPTH && val <= AL_CHORUS_MAX_DEPTH))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus depth out of range");
props->Chorus.Depth = val;
break;
case AL_CHORUS_FEEDBACK:
if(!(val >= AL_CHORUS_MIN_FEEDBACK && val <= AL_CHORUS_MAX_FEEDBACK))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus feedback out of range");
props->Chorus.Feedback = val;
break;
case AL_CHORUS_DELAY:
if(!(val >= AL_CHORUS_MIN_DELAY && val <= AL_CHORUS_MAX_DELAY))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus delay out of range");
props->Chorus.Delay = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
alSetError(context, AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param);
}
}
void ALchorus_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALchorus_setParamf(effect, context, param, vals[0]);
}
{ ALchorus_setParamf(effect, context, param, vals[0]); }
void ALchorus_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
{
@@ -357,13 +383,11 @@ void ALchorus_getParami(const ALeffect *effect, ALCcontext *context, ALenum para
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
alSetError(context, AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param);
}
}
void ALchorus_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALchorus_getParami(effect, context, param, vals);
}
{ ALchorus_getParami(effect, context, param, vals); }
void ALchorus_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
@@ -386,12 +410,146 @@ void ALchorus_getParamf(const ALeffect *effect, ALCcontext *context, ALenum para
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
alSetError(context, AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param);
}
}
void ALchorus_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALchorus_getParamf(effect, context, param, vals);
}
{ ALchorus_getParamf(effect, context, param, vals); }
DEFINE_ALEFFECT_VTABLE(ALchorus);
/* Flanger is basically a chorus with a really short delay. They can both use
* the same processing functions, so piggyback flanger on the chorus functions.
*/
typedef struct FlangerStateFactory {
DERIVE_FROM_TYPE(EffectStateFactory);
} FlangerStateFactory;
ALeffectState *FlangerStateFactory_create(FlangerStateFactory *UNUSED(factory))
{
ALchorusState *state;
NEW_OBJ0(state, ALchorusState)();
if(!state) return NULL;
return STATIC_CAST(ALeffectState, state);
}
DEFINE_EFFECTSTATEFACTORY_VTABLE(FlangerStateFactory);
EffectStateFactory *FlangerStateFactory_getFactory(void)
{
static FlangerStateFactory FlangerFactory = { { GET_VTABLE2(FlangerStateFactory, EffectStateFactory) } };
return STATIC_CAST(EffectStateFactory, &FlangerFactory);
}
void ALflanger_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_FLANGER_WAVEFORM:
if(!(val >= AL_FLANGER_MIN_WAVEFORM && val <= AL_FLANGER_MAX_WAVEFORM))
SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid flanger waveform");
props->Chorus.Waveform = val;
break;
case AL_FLANGER_PHASE:
if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE))
SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger phase out of range");
props->Chorus.Phase = val;
break;
default:
alSetError(context, AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param);
}
}
void ALflanger_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{ ALflanger_setParami(effect, context, param, vals[0]); }
void ALflanger_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_FLANGER_RATE:
if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE))
SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger rate out of range");
props->Chorus.Rate = val;
break;
case AL_FLANGER_DEPTH:
if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH))
SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger depth out of range");
props->Chorus.Depth = val;
break;
case AL_FLANGER_FEEDBACK:
if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK))
SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger feedback out of range");
props->Chorus.Feedback = val;
break;
case AL_FLANGER_DELAY:
if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY))
SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger delay out of range");
props->Chorus.Delay = val;
break;
default:
alSetError(context, AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param);
}
}
void ALflanger_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{ ALflanger_setParamf(effect, context, param, vals[0]); }
void ALflanger_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_FLANGER_WAVEFORM:
*val = props->Chorus.Waveform;
break;
case AL_FLANGER_PHASE:
*val = props->Chorus.Phase;
break;
default:
alSetError(context, AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param);
}
}
void ALflanger_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{ ALflanger_getParami(effect, context, param, vals); }
void ALflanger_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_FLANGER_RATE:
*val = props->Chorus.Rate;
break;
case AL_FLANGER_DEPTH:
*val = props->Chorus.Depth;
break;
case AL_FLANGER_FEEDBACK:
*val = props->Chorus.Feedback;
break;
case AL_FLANGER_DELAY:
*val = props->Chorus.Delay;
break;
default:
alSetError(context, AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param);
}
}
void ALflanger_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{ ALflanger_getParamf(effect, context, param, vals); }
DEFINE_ALEFFECT_VTABLE(ALflanger);
+122 -99
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,144 +27,172 @@
#include "alu.h"
#define AMP_ENVELOPE_MIN 0.5f
#define AMP_ENVELOPE_MAX 2.0f
#define ATTACK_TIME 0.1f /* 100ms to rise from min to max */
#define RELEASE_TIME 0.2f /* 200ms to drop from max to min */
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;
ALfloat AttackRate;
ALfloat ReleaseRate;
ALfloat GainCtrl;
ALfloat AttackMult;
ALfloat ReleaseMult;
ALfloat EnvFollower;
} 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 ALCcontext *context, 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->AttackMult = 1.0f;
state->ReleaseMult = 1.0f;
state->EnvFollower = 1.0f;
}
static ALvoid ALcompressorState_Destruct(ALcompressorState *state)
{
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device)
{
const ALfloat attackTime = device->Frequency * 0.2f; /* 200ms Attack */
const ALfloat releaseTime = device->Frequency * 0.4f; /* 400ms Release */
/* Number of samples to do a full attack and release (non-integer sample
* counts are okay).
*/
const ALfloat attackCount = (ALfloat)device->Frequency * ATTACK_TIME;
const ALfloat releaseCount = (ALfloat)device->Frequency * RELEASE_TIME;
state->AttackRate = 1.0f / attackTime;
state->ReleaseRate = 1.0f / releaseTime;
/* Calculate per-sample multipliers to attack and release at the desired
* rates.
*/
state->AttackMult = powf(AMP_ENVELOPE_MAX/AMP_ENVELOPE_MIN, 1.0f/attackCount);
state->ReleaseMult = powf(AMP_ENVELOPE_MIN/AMP_ENVELOPE_MAX, 1.0f/releaseCount);
return AL_TRUE;
}
static ALvoid ALcompressorState_update(ALcompressorState *state, ALCdevice *Device, const ALeffectslot *Slot)
static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
{
ALfloat gain;
const ALCdevice *device = context->Device;
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++)
ComputePanGains(&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 gains[256];
ALsizei td = mini(256, SamplesToDo-base);
ALfloat env = state->EnvFollower;
/* Generate the per-sample gains from the signal envelope. */
if(state->Enabled)
{
ALfloat output, smp, amplitude;
ALfloat gain = state->GainCtrl;
for(it = 0;it < td;it++)
for(i = 0;i < td;++i)
{
smp = SamplesIn[it+base];
/* Clamp the absolute amplitude to the defined envelope limits,
* then attack or release the envelope to reach it.
*/
ALfloat amplitude = clampf(fabsf(SamplesIn[0][base+i]),
AMP_ENVELOPE_MIN, AMP_ENVELOPE_MAX);
if(amplitude > env)
env = minf(env*state->AttackMult, amplitude);
else if(amplitude < env)
env = maxf(env*state->ReleaseMult, amplitude);
amplitude = fabsf(smp);
if(amplitude > gain)
gain = minf(gain+state->AttackRate, amplitude);
else if(amplitude < gain)
gain = maxf(gain-state->ReleaseRate, amplitude);
output = 1.0f / clampf(gain, 0.5f, 2.0f);
temps[it] = smp * output;
/* Apply the reciprocal of the envelope to normalize the volume
* (compress the dynamic range).
*/
gains[i] = 1.0f / env;
}
state->GainCtrl = gain;
}
else
{
ALfloat output, smp, amplitude;
ALfloat gain = state->GainCtrl;
for(it = 0;it < td;it++)
/* Same as above, except the amplitude is forced to 1. This helps
* ensure smooth gain changes when the compressor is turned on and
* off.
*/
for(i = 0;i < td;++i)
{
smp = SamplesIn[it+base];
ALfloat amplitude = 1.0f;
if(amplitude > env)
env = minf(env*state->AttackMult, amplitude);
else if(amplitude < env)
env = maxf(env*state->ReleaseMult, amplitude);
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;
gains[i] = 1.0f / env;
}
state->GainCtrl = gain;
}
state->EnvFollower = env;
for(kt = 0;kt < MaxChannels;kt++)
/* Now compress the signal amplitude to output. */
for(j = 0;j < MAX_EFFECT_CHANNELS;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] += SamplesIn[j][base+i] * gains[i] * gain;
}
}
base += td;
}
}
DECLARE_DEFAULT_ALLOCATORS(ALcompressorState)
DEFINE_ALEFFECTSTATE_VTABLE(ALcompressorState);
typedef struct CompressorStateFactory {
DERIVE_FROM_TYPE(EffectStateFactory);
} CompressorStateFactory;
typedef struct ALcompressorStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALcompressorStateFactory;
static ALeffectState *ALcompressorStateFactory_create(ALcompressorStateFactory *UNUSED(factory))
static ALeffectState *CompressorStateFactory_create(CompressorStateFactory *UNUSED(factory))
{
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);
}
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALcompressorStateFactory);
DEFINE_EFFECTSTATEFACTORY_VTABLE(CompressorStateFactory);
ALeffectStateFactory *ALcompressorStateFactory_getFactory(void)
EffectStateFactory *CompressorStateFactory_getFactory(void)
{
static ALcompressorStateFactory CompressorFactory = { { GET_VTABLE2(ALcompressorStateFactory, ALeffectStateFactory) } };
static CompressorStateFactory CompressorFactory = { { GET_VTABLE2(CompressorStateFactory, EffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &CompressorFactory);
return STATIC_CAST(EffectStateFactory, &CompressorFactory);
}
@@ -175,24 +203,21 @@ void ALcompressor_setParami(ALeffect *effect, ALCcontext *context, ALenum param,
{
case AL_COMPRESSOR_ONOFF:
if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Compressor state out of range");
props->Compressor.OnOff = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
default:
alSetError(context, AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
param);
}
}
void ALcompressor_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALcompressor_setParami(effect, context, param, vals[0]);
}
void ALcompressor_setParamf(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALfloat UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALcompressor_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALcompressor_setParamf(effect, context, param, vals[0]);
}
{ ALcompressor_setParami(effect, context, param, vals[0]); }
void ALcompressor_setParamf(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat UNUSED(val))
{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param); }
void ALcompressor_setParamfv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALfloat *UNUSED(vals))
{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", param); }
void ALcompressor_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
{
@@ -202,19 +227,17 @@ void ALcompressor_getParami(const ALeffect *effect, ALCcontext *context, ALenum
case AL_COMPRESSOR_ONOFF:
*val = props->Compressor.OnOff;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
alSetError(context, AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
param);
}
}
void ALcompressor_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALcompressor_getParami(effect, context, param, vals);
}
void ALcompressor_getParamf(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALfloat *UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALcompressor_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALcompressor_getParamf(effect, context, param, vals);
}
{ ALcompressor_getParami(effect, context, param, vals); }
void ALcompressor_getParamf(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat *UNUSED(val))
{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param); }
void ALcompressor_getParamfv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat *UNUSED(vals))
{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", param); }
DEFINE_ALEFFECT_VTABLE(ALcompressor);
+100 -80
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
*/
@@ -23,100 +23,126 @@
#include <stdlib.h>
#include "alMain.h"
#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
#include "filters/defs.h"
typedef struct ALdedicatedState {
DERIVE_FROM_TYPE(ALeffectState);
ALfloat gains[MaxChannels];
ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
} ALdedicatedState;
static ALvoid ALdedicatedState_Destruct(ALdedicatedState *UNUSED(state))
{
}
static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *UNUSED(state), ALCdevice *UNUSED(device))
{
return AL_TRUE;
}
static ALvoid ALdedicatedState_update(ALdedicatedState *state, ALCdevice *device, const ALeffectslot *Slot)
{
ALfloat Gain;
ALsizei s;
Gain = Slot->Gain * Slot->EffectProps.Dedicated.Gain;
if(Slot->EffectType == AL_EFFECT_DEDICATED_DIALOGUE)
ComputeAngleGains(device, atan2f(0.0f, 1.0f), 0.0f, Gain, state->gains);
else if(Slot->EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
{
for(s = 0;s < MaxChannels;s++)
state->gains[s] = 0.0f;
state->gains[LFE] = Gain;
}
}
static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
{
const ALfloat *gains = state->gains;
ALuint i, c;
for(c = 0;c < MaxChannels;c++)
{
if(!(gains[c] > GAIN_SILENCE_THRESHOLD))
continue;
for(i = 0;i < SamplesToDo;i++)
SamplesOut[c][i] = SamplesIn[i] * gains[c];
}
}
static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state);
static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *state, ALCdevice *device);
static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCcontext *context, 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)
DEFINE_ALEFFECTSTATE_VTABLE(ALdedicatedState);
typedef struct ALdedicatedStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALdedicatedStateFactory;
static void ALdedicatedState_Construct(ALdedicatedState *state)
{
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
SET_VTABLE2(ALdedicatedState, ALeffectState, state);
}
ALeffectState *ALdedicatedStateFactory_create(ALdedicatedStateFactory *UNUSED(factory))
static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state)
{
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *state, ALCdevice *UNUSED(device))
{
ALsizei i;
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
state->CurrentGains[i] = 0.0f;
return AL_TRUE;
}
static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
{
const ALCdevice *device = context->Device;
ALfloat Gain;
ALsizei i;
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
state->TargetGains[i] = 0.0f;
Gain = slot->Params.Gain * props->Dedicated.Gain;
if(slot->Params.EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
{
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->TargetGains[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->TargetGains[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;
ComputePanGains(&device->Dry, coeffs, Gain, state->TargetGains);
}
}
}
static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
MixSamples(SamplesIn[0], NumChannels, SamplesOut, state->CurrentGains,
state->TargetGains, SamplesToDo, 0, SamplesToDo);
}
typedef struct DedicatedStateFactory {
DERIVE_FROM_TYPE(EffectStateFactory);
} DedicatedStateFactory;
ALeffectState *DedicatedStateFactory_create(DedicatedStateFactory *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);
}
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALdedicatedStateFactory);
DEFINE_EFFECTSTATEFACTORY_VTABLE(DedicatedStateFactory);
ALeffectStateFactory *ALdedicatedStateFactory_getFactory(void)
EffectStateFactory *DedicatedStateFactory_getFactory(void)
{
static ALdedicatedStateFactory DedicatedFactory = { { GET_VTABLE2(ALdedicatedStateFactory, ALeffectStateFactory) } };
static DedicatedStateFactory DedicatedFactory = { { GET_VTABLE2(DedicatedStateFactory, EffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &DedicatedFactory);
return STATIC_CAST(EffectStateFactory, &DedicatedFactory);
}
void ALdedicated_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALdedicated_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALdedicated_setParami(effect, context, param, vals[0]);
}
void ALdedicated_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param); }
void ALdedicated_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", param); }
void ALdedicated_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
@@ -124,25 +150,21 @@ void ALdedicated_setParamf(ALeffect *effect, ALCcontext *context, ALenum param,
{
case AL_DEDICATED_GAIN:
if(!(val >= 0.0f && isfinite(val)))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Dedicated gain out of range");
props->Dedicated.Gain = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
alSetError(context, AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param);
}
}
void ALdedicated_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALdedicated_setParamf(effect, context, param, vals[0]);
}
{ ALdedicated_setParamf(effect, context, param, vals[0]); }
void ALdedicated_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALdedicated_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALdedicated_getParami(effect, context, param, vals);
}
void ALdedicated_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param); }
void ALdedicated_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", param); }
void ALdedicated_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
@@ -153,12 +175,10 @@ void ALdedicated_getParamf(const ALeffect *effect, ALCcontext *context, ALenum p
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
alSetError(context, AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param);
}
}
void ALdedicated_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALdedicated_getParamf(effect, context, param, vals);
}
{ ALdedicated_getParamf(effect, context, param, vals); }
DEFINE_ALEFFECT_VTABLE(ALdedicated);
+160 -171
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,192 +24,185 @@
#include <stdlib.h>
#include "alMain.h"
#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
#include "filters/defs.h"
typedef struct ALdistortionState {
DERIVE_FROM_TYPE(ALeffectState);
/* Effect gains for each channel */
ALfloat Gain[MaxChannels];
ALfloat Gain[MAX_OUTPUT_CHANNELS];
/* Effect parameters */
ALfilterState lowpass;
ALfilterState bandpass;
BiquadFilter lowpass;
BiquadFilter bandpass;
ALfloat attenuation;
ALfloat edge_coeff;
ALfloat Buffer[2][BUFFERSIZE];
} ALdistortionState;
static ALvoid ALdistortionState_Destruct(ALdistortionState *UNUSED(state))
{
}
static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *UNUSED(state), ALCdevice *UNUSED(device))
{
return AL_TRUE;
}
static ALvoid ALdistortionState_update(ALdistortionState *state, ALCdevice *Device, const ALeffectslot *Slot)
{
ALfloat frequency = (ALfloat)Device->Frequency;
ALfloat bandwidth;
ALfloat cutoff;
ALfloat edge;
ALfloat gain;
/* Store distorted signal attenuation settings */
state->attenuation = Slot->EffectProps.Distortion.Gain;
/* Store waveshaper edge settings */
edge = sinf(Slot->EffectProps.Distortion.Edge * (F_PI_2));
edge = minf(edge, 0.99f);
state->edge_coeff = 2.0f * edge / (1.0f-edge);
/* Lowpass filter */
cutoff = Slot->EffectProps.Distortion.LowpassCutoff;
/* Bandwidth value is constant in octaves */
bandwidth = (cutoff / 2.0f) / (cutoff * 0.67f);
ALfilterState_setParams(&state->lowpass, ALfilterType_LowPass, 1.0f,
cutoff / (frequency*4.0f), bandwidth);
/* Bandpass filter */
cutoff = Slot->EffectProps.Distortion.EQCenter;
/* Convert bandwidth in Hz to octaves */
bandwidth = Slot->EffectProps.Distortion.EQBandwidth / (cutoff * 0.67f);
ALfilterState_setParams(&state->bandpass, ALfilterType_BandPass, 1.0f,
cutoff / (frequency*4.0f), bandwidth);
gain = sqrtf(1.0f / Device->NumChan) * Slot->Gain;
SetGains(Device, gain, state->Gain);
}
static ALvoid ALdistortionState_process(ALdistortionState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
{
const ALfloat fc = state->edge_coeff;
float oversample_buffer[64][4];
ALuint base;
ALuint it;
ALuint ot;
ALuint kt;
for(base = 0;base < SamplesToDo;)
{
ALfloat temps[64];
ALuint td = minu(SamplesToDo-base, 64);
/* Perform 4x oversampling to avoid aliasing. */
/* Oversampling greatly improves distortion */
/* quality and allows to implement lowpass and */
/* bandpass filters using high frequencies, at */
/* which classic IIR filters became unstable. */
/* Fill oversample buffer using zero stuffing */
for(it = 0;it < td;it++)
{
oversample_buffer[it][0] = SamplesIn[it+base];
oversample_buffer[it][1] = 0.0f;
oversample_buffer[it][2] = 0.0f;
oversample_buffer[it][3] = 0.0f;
}
/* First step, do lowpass filtering of original signal, */
/* additionally perform buffer interpolation and lowpass */
/* cutoff for oversampling (which is fortunately first */
/* step of distortion). So combine three operations into */
/* the one. */
for(it = 0;it < td;it++)
{
for(ot = 0;ot < 4;ot++)
{
ALfloat smp;
smp = ALfilterState_processSingle(&state->lowpass, oversample_buffer[it][ot]);
/* Restore signal power by multiplying sample by amount of oversampling */
oversample_buffer[it][ot] = smp * 4.0f;
}
}
for(it = 0;it < td;it++)
{
/* Second step, do distortion using waveshaper function */
/* to emulate signal processing during tube overdriving. */
/* Three steps of waveshaping are intended to modify */
/* waveform without boost/clipping/attenuation process. */
for(ot = 0;ot < 4;ot++)
{
ALfloat smp = oversample_buffer[it][ot];
smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp)) * -1.0f;
smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
/* Third step, do bandpass filtering of distorted signal */
smp = ALfilterState_processSingle(&state->bandpass, smp);
oversample_buffer[it][ot] = smp;
}
/* Fourth step, final, do attenuation and perform decimation, */
/* store only one sample out of 4. */
temps[it] = oversample_buffer[it][0] * state->attenuation;
}
for(kt = 0;kt < MaxChannels;kt++)
{
ALfloat gain = state->Gain[kt];
if(!(gain > GAIN_SILENCE_THRESHOLD))
continue;
for(it = 0;it < td;it++)
SamplesOut[kt][base+it] += gain * temps[it];
}
base += td;
}
}
static ALvoid ALdistortionState_Destruct(ALdistortionState *state);
static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *state, ALCdevice *device);
static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCcontext *context, 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);
typedef struct ALdistortionStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALdistortionStateFactory;
static void ALdistortionState_Construct(ALdistortionState *state)
{
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
SET_VTABLE2(ALdistortionState, ALeffectState, state);
}
static ALeffectState *ALdistortionStateFactory_create(ALdistortionStateFactory *UNUSED(factory))
static ALvoid ALdistortionState_Destruct(ALdistortionState *state)
{
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *state, ALCdevice *UNUSED(device))
{
BiquadFilter_clear(&state->lowpass);
BiquadFilter_clear(&state->bandpass);
return AL_TRUE;
}
static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
{
const ALCdevice *device = context->Device;
ALfloat frequency = (ALfloat)device->Frequency;
ALfloat coeffs[MAX_AMBI_COEFFS];
ALfloat bandwidth;
ALfloat cutoff;
ALfloat edge;
/* 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);
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.
*/
BiquadFilter_setParams(&state->lowpass, BiquadType_LowPass, 1.0f,
cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
);
cutoff = props->Distortion.EQCenter;
/* Convert bandwidth in Hz to octaves. */
bandwidth = props->Distortion.EQBandwidth / (cutoff * 0.67f);
BiquadFilter_setParams(&state->bandpass, BiquadType_BandPass, 1.0f,
cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
);
CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
ComputePanGains(&device->Dry, coeffs, slot->Params.Gain*props->Distortion.Gain, state->Gain);
}
static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
ALfloat (*restrict buffer)[BUFFERSIZE] = state->Buffer;
const ALfloat fc = state->edge_coeff;
ALsizei base;
ALsizei i, k;
for(base = 0;base < SamplesToDo;)
{
/* 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.
*/
ALsizei todo = mini(BUFFERSIZE, (SamplesToDo-base) * 4);
/* Fill oversample buffer using zero stuffing. Multiply the sample by
* the amount of oversampling to maintain the signal's power.
*/
for(i = 0;i < todo;i++)
buffer[0][i] = !(i&3) ? SamplesIn[0][(i>>2)+base] * 4.0f : 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.
*/
BiquadFilter_process(&state->lowpass, buffer[1], buffer[0], todo);
/* 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(i = 0;i < todo;i++)
{
ALfloat smp = buffer[1][i];
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][i] = smp;
}
/* Third step, do bandpass filtering of distorted signal. */
BiquadFilter_process(&state->bandpass, buffer[1], buffer[0], todo);
todo >>= 2;
for(k = 0;k < NumChannels;k++)
{
/* Fourth step, final, do attenuation and perform decimation,
* storing only one sample out of four.
*/
ALfloat gain = state->Gain[k];
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
continue;
for(i = 0;i < todo;i++)
SamplesOut[k][base+i] += gain * buffer[1][i*4];
}
base += todo;
}
}
typedef struct DistortionStateFactory {
DERIVE_FROM_TYPE(EffectStateFactory);
} DistortionStateFactory;
static ALeffectState *DistortionStateFactory_create(DistortionStateFactory *UNUSED(factory))
{
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);
}
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALdistortionStateFactory);
DEFINE_EFFECTSTATEFACTORY_VTABLE(DistortionStateFactory);
ALeffectStateFactory *ALdistortionStateFactory_getFactory(void)
EffectStateFactory *DistortionStateFactory_getFactory(void)
{
static ALdistortionStateFactory DistortionFactory = { { GET_VTABLE2(ALdistortionStateFactory, ALeffectStateFactory) } };
static DistortionStateFactory DistortionFactory = { { GET_VTABLE2(DistortionStateFactory, EffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &DistortionFactory);
return STATIC_CAST(EffectStateFactory, &DistortionFactory);
}
void ALdistortion_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALdistortion_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALdistortion_setParami(effect, context, param, vals[0]);
}
void ALdistortion_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param); }
void ALdistortion_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param); }
void ALdistortion_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
@@ -217,49 +210,46 @@ void ALdistortion_setParamf(ALeffect *effect, ALCcontext *context, ALenum param,
{
case AL_DISTORTION_EDGE:
if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion edge out of range");
props->Distortion.Edge = val;
break;
case AL_DISTORTION_GAIN:
if(!(val >= AL_DISTORTION_MIN_GAIN && val <= AL_DISTORTION_MAX_GAIN))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion gain out of range");
props->Distortion.Gain = val;
break;
case AL_DISTORTION_LOWPASS_CUTOFF:
if(!(val >= AL_DISTORTION_MIN_LOWPASS_CUTOFF && val <= AL_DISTORTION_MAX_LOWPASS_CUTOFF))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion low-pass cutoff out of range");
props->Distortion.LowpassCutoff = val;
break;
case AL_DISTORTION_EQCENTER:
if(!(val >= AL_DISTORTION_MIN_EQCENTER && val <= AL_DISTORTION_MAX_EQCENTER))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion EQ center out of range");
props->Distortion.EQCenter = val;
break;
case AL_DISTORTION_EQBANDWIDTH:
if(!(val >= AL_DISTORTION_MIN_EQBANDWIDTH && val <= AL_DISTORTION_MAX_EQBANDWIDTH))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion EQ bandwidth out of range");
props->Distortion.EQBandwidth = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
alSetError(context, AL_INVALID_ENUM, "Invalid distortion float property 0x%04x",
param);
}
}
void ALdistortion_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALdistortion_setParamf(effect, context, param, vals[0]);
}
{ ALdistortion_setParamf(effect, context, param, vals[0]); }
void ALdistortion_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALdistortion_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALdistortion_getParami(effect, context, param, vals);
}
void ALdistortion_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param); }
void ALdistortion_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param); }
void ALdistortion_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
@@ -286,12 +276,11 @@ void ALdistortion_getParamf(const ALeffect *effect, ALCcontext *context, ALenum
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
alSetError(context, AL_INVALID_ENUM, "Invalid distortion float property 0x%04x",
param);
}
}
void ALdistortion_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALdistortion_getParamf(effect, context, param, vals);
}
{ ALdistortion_getParamf(effect, context, param, vals); }
DEFINE_ALEFFECT_VTABLE(ALdistortion);
+174 -157
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,154 +28,45 @@
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
#include "filters/defs.h"
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];
struct {
ALfloat Current[MAX_OUTPUT_CHANNELS];
ALfloat Target[MAX_OUTPUT_CHANNELS];
} Gains[2];
ALfloat FeedGain;
ALfilterState Filter;
BiquadFilter 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 ALCcontext *context, 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;
@@ -185,27 +76,159 @@ ALeffectState *ALechoStateFactory_create(ALechoStateFactory *UNUSED(factory))
state->Tap[1].delay = 0;
state->Offset = 0;
ALfilterState_clear(&state->Filter);
BiquadFilter_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;
// 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 = float2int(AL_ECHO_MAX_DELAY*Device->Frequency + 0.5f) +
float2int(AL_ECHO_MAX_LRDELAY*Device->Frequency + 0.5f);
maxlen = NextPowerOf2(maxlen);
if(maxlen <= 0) return AL_FALSE;
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;
}
memset(state->SampleBuffer, 0, state->BufferLength*sizeof(ALfloat));
memset(state->Gains, 0, sizeof(state->Gains));
return AL_TRUE;
}
static ALvoid ALechoState_update(ALechoState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
{
const ALCdevice *device = context->Device;
ALuint frequency = device->Frequency;
ALfloat coeffs[MAX_AMBI_COEFFS];
ALfloat gainhf, lrpan, spread;
state->Tap[0].delay = maxi(float2int(props->Echo.Delay*frequency + 0.5f), 1);
state->Tap[1].delay = float2int(props->Echo.LRDelay*frequency + 0.5f);
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;
gainhf = maxf(1.0f - props->Echo.Damping, 0.0625f); /* Limit -24dB */
BiquadFilter_setParams(&state->Filter, BiquadType_HighShelf,
gainhf, LOWPASSFREQREF/frequency, calc_rcpQ_from_slope(gainhf, 1.0f)
);
/* First tap panning */
CalcAngleCoeffs(-F_PI_2*lrpan, 0.0f, spread, coeffs);
ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[0].Target);
/* Second tap panning */
CalcAngleCoeffs( F_PI_2*lrpan, 0.0f, spread, coeffs);
ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[1].Target);
}
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;
ALfloat *restrict delaybuf = state->SampleBuffer;
ALsizei offset = state->Offset;
ALfloat z1, z2, in, out;
ALsizei base;
ALsizei c, i;
z1 = state->Filter.z1;
z2 = state->Filter.z2;
for(base = 0;base < SamplesToDo;)
{
alignas(16) ALfloat temps[2][128];
ALsizei td = mini(128, SamplesToDo-base);
for(i = 0;i < td;i++)
{
/* Feed the delay buffer's input first. */
delaybuf[offset&mask] = SamplesIn[0][i+base];
/* First tap */
temps[0][i] = delaybuf[(offset-tap1) & mask];
/* Second tap */
temps[1][i] = delaybuf[(offset-tap2) & mask];
/* Apply damping to the second tap, then add it to the buffer with
* feedback attenuation.
*/
in = temps[1][i];
out = in*state->Filter.b0 + z1;
z1 = in*state->Filter.b1 - out*state->Filter.a1 + z2;
z2 = in*state->Filter.b2 - out*state->Filter.a2;
delaybuf[offset&mask] += out * state->FeedGain;
offset++;
}
for(c = 0;c < 2;c++)
MixSamples(temps[c], NumChannels, SamplesOut, state->Gains[c].Current,
state->Gains[c].Target, SamplesToDo-base, base, td);
base += td;
}
state->Filter.z1 = z1;
state->Filter.z2 = z2;
state->Offset = offset;
}
typedef struct EchoStateFactory {
DERIVE_FROM_TYPE(EffectStateFactory);
} EchoStateFactory;
ALeffectState *EchoStateFactory_create(EchoStateFactory *UNUSED(factory))
{
ALechoState *state;
NEW_OBJ0(state, ALechoState)();
if(!state) return NULL;
return STATIC_CAST(ALeffectState, state);
}
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALechoStateFactory);
DEFINE_EFFECTSTATEFACTORY_VTABLE(EchoStateFactory);
ALeffectStateFactory *ALechoStateFactory_getFactory(void)
EffectStateFactory *EchoStateFactory_getFactory(void)
{
static ALechoStateFactory EchoFactory = { { GET_VTABLE2(ALechoStateFactory, ALeffectStateFactory) } };
static EchoStateFactory EchoFactory = { { GET_VTABLE2(EchoStateFactory, EffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &EchoFactory);
return STATIC_CAST(EffectStateFactory, &EchoFactory);
}
void ALecho_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALecho_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALecho_setParami(effect, context, param, vals[0]);
}
void ALecho_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param); }
void ALecho_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param); }
void ALecho_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
@@ -213,49 +236,45 @@ void ALecho_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALflo
{
case AL_ECHO_DELAY:
if(!(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo delay out of range");
props->Echo.Delay = val;
break;
case AL_ECHO_LRDELAY:
if(!(val >= AL_ECHO_MIN_LRDELAY && val <= AL_ECHO_MAX_LRDELAY))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo LR delay out of range");
props->Echo.LRDelay = val;
break;
case AL_ECHO_DAMPING:
if(!(val >= AL_ECHO_MIN_DAMPING && val <= AL_ECHO_MAX_DAMPING))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo damping out of range");
props->Echo.Damping = val;
break;
case AL_ECHO_FEEDBACK:
if(!(val >= AL_ECHO_MIN_FEEDBACK && val <= AL_ECHO_MAX_FEEDBACK))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo feedback out of range");
props->Echo.Feedback = val;
break;
case AL_ECHO_SPREAD:
if(!(val >= AL_ECHO_MIN_SPREAD && val <= AL_ECHO_MAX_SPREAD))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo spread out of range");
props->Echo.Spread = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
alSetError(context, AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param);
}
}
void ALecho_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALecho_setParamf(effect, context, param, vals[0]);
}
{ ALecho_setParamf(effect, context, param, vals[0]); }
void ALecho_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALecho_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALecho_getParami(effect, context, param, vals);
}
void ALecho_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param); }
void ALecho_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param); }
void ALecho_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
@@ -282,12 +301,10 @@ void ALecho_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param,
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
alSetError(context, AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param);
}
}
void ALecho_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALecho_getParamf(effect, context, param, vals);
}
{ ALecho_getParamf(effect, context, param, vals); }
DEFINE_ALEFFECT_VTABLE(ALecho);
+148 -127
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,10 +24,10 @@
#include <stdlib.h>
#include "alMain.h"
#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
#include "filters/defs.h"
/* The document "Effects Extension Guide.pdf" says that low and high *
@@ -71,132 +71,159 @@
* filter coefficients" by Robert Bristow-Johnson *
* http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt */
typedef struct ALequalizerState {
DERIVE_FROM_TYPE(ALeffectState);
/* Effect gains for each channel */
ALfloat Gain[MaxChannels];
struct {
/* Effect parameters */
BiquadFilter filter[4];
/* Effect parameters */
ALfilterState filter[4];
/* Effect gains for each channel */
ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
} Chans[MAX_EFFECT_CHANNELS];
ALfloat SampleBuffer[MAX_EFFECT_CHANNELS][BUFFERSIZE];
} ALequalizerState;
static ALvoid ALequalizerState_Destruct(ALequalizerState *UNUSED(state))
{
}
static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *UNUSED(state), ALCdevice *UNUSED(device))
{
return AL_TRUE;
}
static ALvoid ALequalizerState_update(ALequalizerState *state, ALCdevice *device, const ALeffectslot *slot)
{
ALfloat frequency = (ALfloat)device->Frequency;
ALfloat gain = sqrtf(1.0f / device->NumChan) * slot->Gain;
SetGains(device, gain, state->Gain);
/* Calculate coefficients for the each type of filter */
ALfilterState_setParams(&state->filter[0], ALfilterType_LowShelf,
sqrtf(slot->EffectProps.Equalizer.LowGain),
slot->EffectProps.Equalizer.LowCutoff/frequency,
0.0f);
ALfilterState_setParams(&state->filter[1], ALfilterType_Peaking,
sqrtf(slot->EffectProps.Equalizer.Mid1Gain),
slot->EffectProps.Equalizer.Mid1Center/frequency,
slot->EffectProps.Equalizer.Mid1Width);
ALfilterState_setParams(&state->filter[2], ALfilterType_Peaking,
sqrtf(slot->EffectProps.Equalizer.Mid2Gain),
slot->EffectProps.Equalizer.Mid2Center/frequency,
slot->EffectProps.Equalizer.Mid2Width);
ALfilterState_setParams(&state->filter[3], ALfilterType_HighShelf,
sqrtf(slot->EffectProps.Equalizer.HighGain),
slot->EffectProps.Equalizer.HighCutoff/frequency,
0.0f);
}
static ALvoid ALequalizerState_process(ALequalizerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
{
ALuint base;
ALuint it;
ALuint kt;
ALuint ft;
for(base = 0;base < SamplesToDo;)
{
ALfloat temps[64];
ALuint td = minu(SamplesToDo-base, 64);
for(it = 0;it < td;it++)
{
ALfloat smp = SamplesIn[base+it];
for(ft = 0;ft < 4;ft++)
smp = ALfilterState_processSingle(&state->filter[ft], smp);
temps[it] = smp;
}
for(kt = 0;kt < MaxChannels;kt++)
{
ALfloat gain = state->Gain[kt];
if(!(gain > GAIN_SILENCE_THRESHOLD))
continue;
for(it = 0;it < td;it++)
SamplesOut[kt][base+it] += gain * temps[it];
}
base += td;
}
}
static ALvoid ALequalizerState_Destruct(ALequalizerState *state);
static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *state, ALCdevice *device);
static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCcontext *context, 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);
typedef struct ALequalizerStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALequalizerStateFactory;
static void ALequalizerState_Construct(ALequalizerState *state)
{
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
SET_VTABLE2(ALequalizerState, ALeffectState, state);
}
ALeffectState *ALequalizerStateFactory_create(ALequalizerStateFactory *UNUSED(factory))
static ALvoid ALequalizerState_Destruct(ALequalizerState *state)
{
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *state, ALCdevice *UNUSED(device))
{
ALsizei i, j;
for(i = 0; i < MAX_EFFECT_CHANNELS;i++)
{
for(j = 0;j < 4;j++)
BiquadFilter_clear(&state->Chans[i].filter[j]);
for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
state->Chans[i].CurrentGains[j] = 0.0f;
}
return AL_TRUE;
}
static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
{
const ALCdevice *device = context->Device;
ALfloat frequency = (ALfloat)device->Frequency;
ALfloat gain, f0norm;
ALuint i;
/* Calculate coefficients for the each type of filter. Note that the shelf
* filters' gain is for the reference frequency, which is the centerpoint
* of the transition band.
*/
gain = maxf(sqrtf(props->Equalizer.LowGain), 0.0625f); /* Limit -24dB */
f0norm = props->Equalizer.LowCutoff/frequency;
BiquadFilter_setParams(&state->Chans[0].filter[0], BiquadType_LowShelf,
gain, f0norm, calc_rcpQ_from_slope(gain, 0.75f)
);
gain = maxf(props->Equalizer.Mid1Gain, 0.0625f);
f0norm = props->Equalizer.Mid1Center/frequency;
BiquadFilter_setParams(&state->Chans[0].filter[1], BiquadType_Peaking,
gain, f0norm, calc_rcpQ_from_bandwidth(
f0norm, props->Equalizer.Mid1Width
)
);
gain = maxf(props->Equalizer.Mid2Gain, 0.0625f);
f0norm = props->Equalizer.Mid2Center/frequency;
BiquadFilter_setParams(&state->Chans[0].filter[2], BiquadType_Peaking,
gain, f0norm, calc_rcpQ_from_bandwidth(
f0norm, props->Equalizer.Mid2Width
)
);
gain = maxf(sqrtf(props->Equalizer.HighGain), 0.0625f);
f0norm = props->Equalizer.HighCutoff/frequency;
BiquadFilter_setParams(&state->Chans[0].filter[3], BiquadType_HighShelf,
gain, f0norm, calc_rcpQ_from_slope(gain, 0.75f)
);
/* Copy the filter coefficients for the other input channels. */
for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
{
BiquadFilter_copyParams(&state->Chans[i].filter[0], &state->Chans[0].filter[0]);
BiquadFilter_copyParams(&state->Chans[i].filter[1], &state->Chans[0].filter[1]);
BiquadFilter_copyParams(&state->Chans[i].filter[2], &state->Chans[0].filter[2]);
BiquadFilter_copyParams(&state->Chans[i].filter[3], &state->Chans[0].filter[3]);
}
STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain,
state->Chans[i].TargetGains);
}
static ALvoid ALequalizerState_process(ALequalizerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
ALfloat (*restrict temps)[BUFFERSIZE] = state->SampleBuffer;
ALsizei c;
for(c = 0;c < MAX_EFFECT_CHANNELS;c++)
{
BiquadFilter_process(&state->Chans[c].filter[0], temps[0], SamplesIn[c], SamplesToDo);
BiquadFilter_process(&state->Chans[c].filter[1], temps[1], temps[0], SamplesToDo);
BiquadFilter_process(&state->Chans[c].filter[2], temps[2], temps[1], SamplesToDo);
BiquadFilter_process(&state->Chans[c].filter[3], temps[3], temps[2], SamplesToDo);
MixSamples(temps[3], NumChannels, SamplesOut,
state->Chans[c].CurrentGains, state->Chans[c].TargetGains,
SamplesToDo, 0, SamplesToDo
);
}
}
typedef struct EqualizerStateFactory {
DERIVE_FROM_TYPE(EffectStateFactory);
} EqualizerStateFactory;
ALeffectState *EqualizerStateFactory_create(EqualizerStateFactory *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);
}
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALequalizerStateFactory);
DEFINE_EFFECTSTATEFACTORY_VTABLE(EqualizerStateFactory);
ALeffectStateFactory *ALequalizerStateFactory_getFactory(void)
EffectStateFactory *EqualizerStateFactory_getFactory(void)
{
static ALequalizerStateFactory EqualizerFactory = { { GET_VTABLE2(ALequalizerStateFactory, ALeffectStateFactory) } };
static EqualizerStateFactory EqualizerFactory = { { GET_VTABLE2(EqualizerStateFactory, EffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &EqualizerFactory);
return STATIC_CAST(EffectStateFactory, &EqualizerFactory);
}
void ALequalizer_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALequalizer_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALequalizer_setParami(effect, context, param, vals[0]);
}
void ALequalizer_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param); }
void ALequalizer_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", param); }
void ALequalizer_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
@@ -204,79 +231,75 @@ void ALequalizer_setParamf(ALeffect *effect, ALCcontext *context, ALenum param,
{
case AL_EQUALIZER_LOW_GAIN:
if(!(val >= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_LOW_GAIN))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer low-band gain out of range");
props->Equalizer.LowGain = val;
break;
case AL_EQUALIZER_LOW_CUTOFF:
if(!(val >= AL_EQUALIZER_MIN_LOW_CUTOFF && val <= AL_EQUALIZER_MAX_LOW_CUTOFF))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer low-band cutoff out of range");
props->Equalizer.LowCutoff = val;
break;
case AL_EQUALIZER_MID1_GAIN:
if(!(val >= AL_EQUALIZER_MIN_MID1_GAIN && val <= AL_EQUALIZER_MAX_MID1_GAIN))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid1-band gain out of range");
props->Equalizer.Mid1Gain = val;
break;
case AL_EQUALIZER_MID1_CENTER:
if(!(val >= AL_EQUALIZER_MIN_MID1_CENTER && val <= AL_EQUALIZER_MAX_MID1_CENTER))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid1-band center out of range");
props->Equalizer.Mid1Center = val;
break;
case AL_EQUALIZER_MID1_WIDTH:
if(!(val >= AL_EQUALIZER_MIN_MID1_WIDTH && val <= AL_EQUALIZER_MAX_MID1_WIDTH))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid1-band width out of range");
props->Equalizer.Mid1Width = val;
break;
case AL_EQUALIZER_MID2_GAIN:
if(!(val >= AL_EQUALIZER_MIN_MID2_GAIN && val <= AL_EQUALIZER_MAX_MID2_GAIN))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid2-band gain out of range");
props->Equalizer.Mid2Gain = val;
break;
case AL_EQUALIZER_MID2_CENTER:
if(!(val >= AL_EQUALIZER_MIN_MID2_CENTER && val <= AL_EQUALIZER_MAX_MID2_CENTER))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid2-band center out of range");
props->Equalizer.Mid2Center = val;
break;
case AL_EQUALIZER_MID2_WIDTH:
if(!(val >= AL_EQUALIZER_MIN_MID2_WIDTH && val <= AL_EQUALIZER_MAX_MID2_WIDTH))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid2-band width out of range");
props->Equalizer.Mid2Width = val;
break;
case AL_EQUALIZER_HIGH_GAIN:
if(!(val >= AL_EQUALIZER_MIN_HIGH_GAIN && val <= AL_EQUALIZER_MAX_HIGH_GAIN))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer high-band gain out of range");
props->Equalizer.HighGain = val;
break;
case AL_EQUALIZER_HIGH_CUTOFF:
if(!(val >= AL_EQUALIZER_MIN_HIGH_CUTOFF && val <= AL_EQUALIZER_MAX_HIGH_CUTOFF))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer high-band cutoff out of range");
props->Equalizer.HighCutoff = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
alSetError(context, AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param);
}
}
void ALequalizer_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALequalizer_setParamf(effect, context, param, vals[0]);
}
{ ALequalizer_setParamf(effect, context, param, vals[0]); }
void ALequalizer_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALequalizer_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALequalizer_getParami(effect, context, param, vals);
}
void ALequalizer_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param); }
void ALequalizer_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", param); }
void ALequalizer_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
@@ -323,12 +346,10 @@ void ALequalizer_getParamf(const ALeffect *effect, ALCcontext *context, ALenum p
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
alSetError(context, AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param);
}
}
void ALequalizer_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALequalizer_getParamf(effect, context, param, vals);
}
{ ALequalizer_getParamf(effect, context, param, vals); }
DEFINE_ALEFFECT_VTABLE(ALequalizer);
-396
View File
@@ -1,396 +0,0 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2013 by Mike Gorchak
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <math.h>
#include <stdlib.h>
#include "alMain.h"
#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
enum FlangerWaveForm {
FWF_Triangle = AL_FLANGER_WAVEFORM_TRIANGLE,
FWF_Sinusoid = AL_FLANGER_WAVEFORM_SINUSOID
};
typedef struct ALflangerState {
DERIVE_FROM_TYPE(ALeffectState);
ALfloat *SampleBuffer[2];
ALuint BufferLength;
ALuint offset;
ALuint lfo_range;
ALfloat lfo_scale;
ALint lfo_disp;
/* Gains for left and right sides */
ALfloat Gain[2][MaxChannels];
/* effect parameters */
enum FlangerWaveForm waveform;
ALint delay;
ALfloat depth;
ALfloat feedback;
} ALflangerState;
static ALvoid ALflangerState_Destruct(ALflangerState *state)
{
free(state->SampleBuffer[0]);
state->SampleBuffer[0] = NULL;
state->SampleBuffer[1] = NULL;
}
static ALboolean ALflangerState_deviceUpdate(ALflangerState *state, ALCdevice *Device)
{
ALuint maxlen;
ALuint it;
maxlen = fastf2u(AL_FLANGER_MAX_DELAY * 3.0f * Device->Frequency) + 1;
maxlen = NextPowerOf2(maxlen);
if(maxlen != state->BufferLength)
{
void *temp;
temp = realloc(state->SampleBuffer[0], maxlen * sizeof(ALfloat) * 2);
if(!temp) return AL_FALSE;
state->SampleBuffer[0] = temp;
state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen;
state->BufferLength = maxlen;
}
for(it = 0;it < state->BufferLength;it++)
{
state->SampleBuffer[0][it] = 0.0f;
state->SampleBuffer[1][it] = 0.0f;
}
return AL_TRUE;
}
static ALvoid ALflangerState_update(ALflangerState *state, ALCdevice *Device, const ALeffectslot *Slot)
{
ALfloat frequency = (ALfloat)Device->Frequency;
ALfloat rate;
ALint phase;
switch(Slot->EffectProps.Flanger.Waveform)
{
case AL_FLANGER_WAVEFORM_TRIANGLE:
state->waveform = FWF_Triangle;
break;
case AL_FLANGER_WAVEFORM_SINUSOID:
state->waveform = FWF_Sinusoid;
break;
}
state->depth = Slot->EffectProps.Flanger.Depth;
state->feedback = Slot->EffectProps.Flanger.Feedback;
state->delay = fastf2i(Slot->EffectProps.Flanger.Delay * frequency);
/* Gains for left and right sides */
ComputeAngleGains(Device, atan2f(-1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[0]);
ComputeAngleGains(Device, atan2f(+1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[1]);
phase = Slot->EffectProps.Flanger.Phase;
rate = Slot->EffectProps.Flanger.Rate;
if(!(rate > 0.0f))
{
state->lfo_scale = 0.0f;
state->lfo_range = 1;
state->lfo_disp = 0;
}
else
{
/* Calculate LFO coefficient */
state->lfo_range = fastf2u(frequency/rate + 0.5f);
switch(state->waveform)
{
case FWF_Triangle:
state->lfo_scale = 4.0f / state->lfo_range;
break;
case FWF_Sinusoid:
state->lfo_scale = F_2PI / state->lfo_range;
break;
}
/* Calculate lfo phase displacement */
state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f));
}
}
static inline void Triangle(ALint *delay_left, ALint *delay_right, ALuint offset, const ALflangerState *state)
{
ALfloat lfo_value;
lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
lfo_value *= state->depth * state->delay;
*delay_left = fastf2i(lfo_value) + state->delay;
offset += state->lfo_disp;
lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
lfo_value *= state->depth * state->delay;
*delay_right = fastf2i(lfo_value) + state->delay;
}
static inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALuint offset, const ALflangerState *state)
{
ALfloat lfo_value;
lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
lfo_value *= state->depth * state->delay;
*delay_left = fastf2i(lfo_value) + state->delay;
offset += state->lfo_disp;
lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
lfo_value *= state->depth * state->delay;
*delay_right = fastf2i(lfo_value) + state->delay;
}
#define DECL_TEMPLATE(Func) \
static void Process##Func(ALflangerState *state, const ALuint SamplesToDo, \
const ALfloat *restrict SamplesIn, ALfloat (*restrict out)[2]) \
{ \
const ALuint bufmask = state->BufferLength-1; \
ALfloat *restrict leftbuf = state->SampleBuffer[0]; \
ALfloat *restrict rightbuf = state->SampleBuffer[1]; \
ALuint offset = state->offset; \
const ALfloat feedback = state->feedback; \
ALuint it; \
\
for(it = 0;it < SamplesToDo;it++) \
{ \
ALint delay_left, delay_right; \
Func(&delay_left, &delay_right, offset, state); \
\
out[it][0] = leftbuf[(offset-delay_left)&bufmask]; \
leftbuf[offset&bufmask] = (out[it][0]+SamplesIn[it]) * feedback; \
\
out[it][1] = rightbuf[(offset-delay_right)&bufmask]; \
rightbuf[offset&bufmask] = (out[it][1]+SamplesIn[it]) * feedback; \
\
offset++; \
} \
state->offset = offset; \
}
DECL_TEMPLATE(Triangle)
DECL_TEMPLATE(Sinusoid)
#undef DECL_TEMPLATE
static ALvoid ALflangerState_process(ALflangerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
{
ALuint it, kt;
ALuint base;
for(base = 0;base < SamplesToDo;)
{
ALfloat temps[64][2];
ALuint td = minu(SamplesToDo-base, 64);
switch(state->waveform)
{
case FWF_Triangle:
ProcessTriangle(state, td, SamplesIn+base, temps);
break;
case FWF_Sinusoid:
ProcessSinusoid(state, td, SamplesIn+base, temps);
break;
}
for(kt = 0;kt < MaxChannels;kt++)
{
ALfloat gain = state->Gain[0][kt];
if(gain > GAIN_SILENCE_THRESHOLD)
{
for(it = 0;it < td;it++)
SamplesOut[kt][it+base] += temps[it][0] * gain;
}
gain = state->Gain[1][kt];
if(gain > GAIN_SILENCE_THRESHOLD)
{
for(it = 0;it < td;it++)
SamplesOut[kt][it+base] += temps[it][1] * gain;
}
}
base += td;
}
}
DECLARE_DEFAULT_ALLOCATORS(ALflangerState)
DEFINE_ALEFFECTSTATE_VTABLE(ALflangerState);
typedef struct ALflangerStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALflangerStateFactory;
ALeffectState *ALflangerStateFactory_create(ALflangerStateFactory *UNUSED(factory))
{
ALflangerState *state;
state = ALflangerState_New(sizeof(*state));
if(!state) return NULL;
SET_VTABLE2(ALflangerState, ALeffectState, state);
state->BufferLength = 0;
state->SampleBuffer[0] = NULL;
state->SampleBuffer[1] = NULL;
state->offset = 0;
state->lfo_range = 1;
state->waveform = FWF_Triangle;
return STATIC_CAST(ALeffectState, state);
}
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALflangerStateFactory);
ALeffectStateFactory *ALflangerStateFactory_getFactory(void)
{
static ALflangerStateFactory FlangerFactory = { { GET_VTABLE2(ALflangerStateFactory, ALeffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &FlangerFactory);
}
void ALflanger_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_FLANGER_WAVEFORM:
if(!(val >= AL_FLANGER_MIN_WAVEFORM && val <= AL_FLANGER_MAX_WAVEFORM))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Flanger.Waveform = val;
break;
case AL_FLANGER_PHASE:
if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Flanger.Phase = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALflanger_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALflanger_setParami(effect, context, param, vals[0]);
}
void ALflanger_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_FLANGER_RATE:
if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Flanger.Rate = val;
break;
case AL_FLANGER_DEPTH:
if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Flanger.Depth = val;
break;
case AL_FLANGER_FEEDBACK:
if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Flanger.Feedback = val;
break;
case AL_FLANGER_DELAY:
if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Flanger.Delay = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALflanger_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALflanger_setParamf(effect, context, param, vals[0]);
}
void ALflanger_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_FLANGER_WAVEFORM:
*val = props->Flanger.Waveform;
break;
case AL_FLANGER_PHASE:
*val = props->Flanger.Phase;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALflanger_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALflanger_getParami(effect, context, param, vals);
}
void ALflanger_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_FLANGER_RATE:
*val = props->Flanger.Rate;
break;
case AL_FLANGER_DEPTH:
*val = props->Flanger.Depth;
break;
case AL_FLANGER_FEEDBACK:
*val = props->Flanger.Feedback;
break;
case AL_FLANGER_DELAY:
*val = props->Flanger.Delay;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALflanger_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALflanger_getParamf(effect, context, param, vals);
}
DEFINE_ALEFFECT_VTABLE(ALflanger);
+329
View File
@@ -0,0 +1,329 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2018 by Raul Herraiz.
* 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 <math.h>
#include <stdlib.h>
#include "alMain.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
#include "filters/defs.h"
#include "alcomplex.h"
#define HIL_SIZE 1024
#define OVERSAMP (1<<2)
#define HIL_STEP (HIL_SIZE / OVERSAMP)
#define FIFO_LATENCY (HIL_STEP * (OVERSAMP-1))
typedef struct ALfshifterState {
DERIVE_FROM_TYPE(ALeffectState);
/* Effect parameters */
ALsizei count;
ALsizei PhaseStep;
ALsizei Phase;
ALdouble ld_sign;
/*Effects buffers*/
ALfloat InFIFO[HIL_SIZE];
ALcomplex OutFIFO[HIL_SIZE];
ALcomplex OutputAccum[HIL_SIZE];
ALcomplex Analytic[HIL_SIZE];
ALcomplex Outdata[BUFFERSIZE];
alignas(16) ALfloat BufferOut[BUFFERSIZE];
/* Effect gains for each output channel */
ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
} ALfshifterState;
static ALvoid ALfshifterState_Destruct(ALfshifterState *state);
static ALboolean ALfshifterState_deviceUpdate(ALfshifterState *state, ALCdevice *device);
static ALvoid ALfshifterState_update(ALfshifterState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
static ALvoid ALfshifterState_process(ALfshifterState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
DECLARE_DEFAULT_ALLOCATORS(ALfshifterState)
DEFINE_ALEFFECTSTATE_VTABLE(ALfshifterState);
/* Define a Hann window, used to filter the HIL input and output. */
alignas(16) static ALdouble HannWindow[HIL_SIZE];
static void InitHannWindow(void)
{
ALsizei i;
/* Create lookup table of the Hann window for the desired size, i.e. HIL_SIZE */
for(i = 0;i < HIL_SIZE>>1;i++)
{
ALdouble val = sin(M_PI * (ALdouble)i / (ALdouble)(HIL_SIZE-1));
HannWindow[i] = HannWindow[HIL_SIZE-1-i] = val * val;
}
}
static alonce_flag HannInitOnce = AL_ONCE_FLAG_INIT;
static void ALfshifterState_Construct(ALfshifterState *state)
{
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
SET_VTABLE2(ALfshifterState, ALeffectState, state);
alcall_once(&HannInitOnce, InitHannWindow);
}
static ALvoid ALfshifterState_Destruct(ALfshifterState *state)
{
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
static ALboolean ALfshifterState_deviceUpdate(ALfshifterState *state, ALCdevice *UNUSED(device))
{
/* (Re-)initializing parameters and clear the buffers. */
state->count = FIFO_LATENCY;
state->PhaseStep = 0;
state->Phase = 0;
state->ld_sign = 1.0;
memset(state->InFIFO, 0, sizeof(state->InFIFO));
memset(state->OutFIFO, 0, sizeof(state->OutFIFO));
memset(state->OutputAccum, 0, sizeof(state->OutputAccum));
memset(state->Analytic, 0, sizeof(state->Analytic));
memset(state->CurrentGains, 0, sizeof(state->CurrentGains));
memset(state->TargetGains, 0, sizeof(state->TargetGains));
return AL_TRUE;
}
static ALvoid ALfshifterState_update(ALfshifterState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
{
const ALCdevice *device = context->Device;
ALfloat coeffs[MAX_AMBI_COEFFS];
ALfloat step;
step = props->Fshifter.Frequency / (ALfloat)device->Frequency;
state->PhaseStep = fastf2i(minf(step, 0.5f) * FRACTIONONE);
switch(props->Fshifter.LeftDirection)
{
case AL_FREQUENCY_SHIFTER_DIRECTION_DOWN:
state->ld_sign = -1.0;
break;
case AL_FREQUENCY_SHIFTER_DIRECTION_UP:
state->ld_sign = 1.0;
break;
case AL_FREQUENCY_SHIFTER_DIRECTION_OFF:
state->Phase = 0;
state->PhaseStep = 0;
break;
}
CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->TargetGains);
}
static ALvoid ALfshifterState_process(ALfshifterState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
static const ALcomplex complex_zero = { 0.0, 0.0 };
ALfloat *restrict BufferOut = state->BufferOut;
ALsizei j, k, base;
for(base = 0;base < SamplesToDo;)
{
ALsizei todo = mini(HIL_SIZE-state->count, SamplesToDo-base);
ASSUME(todo > 0);
/* Fill FIFO buffer with samples data */
k = state->count;
for(j = 0;j < todo;j++,k++)
{
state->InFIFO[k] = SamplesIn[0][base+j];
state->Outdata[base+j] = state->OutFIFO[k-FIFO_LATENCY];
}
state->count += todo;
base += todo;
/* Check whether FIFO buffer is filled */
if(state->count < HIL_SIZE) continue;
state->count = FIFO_LATENCY;
/* Real signal windowing and store in Analytic buffer */
for(k = 0;k < HIL_SIZE;k++)
{
state->Analytic[k].Real = state->InFIFO[k] * HannWindow[k];
state->Analytic[k].Imag = 0.0;
}
/* Processing signal by Discrete Hilbert Transform (analytical signal). */
complex_hilbert(state->Analytic, HIL_SIZE);
/* Windowing and add to output accumulator */
for(k = 0;k < HIL_SIZE;k++)
{
state->OutputAccum[k].Real += 2.0/OVERSAMP*HannWindow[k]*state->Analytic[k].Real;
state->OutputAccum[k].Imag += 2.0/OVERSAMP*HannWindow[k]*state->Analytic[k].Imag;
}
/* Shift accumulator, input & output FIFO */
for(k = 0;k < HIL_STEP;k++) state->OutFIFO[k] = state->OutputAccum[k];
for(j = 0;k < HIL_SIZE;k++,j++) state->OutputAccum[j] = state->OutputAccum[k];
for(;j < HIL_SIZE;j++) state->OutputAccum[j] = complex_zero;
for(k = 0;k < FIFO_LATENCY;k++)
state->InFIFO[k] = state->InFIFO[k+HIL_STEP];
}
/* Process frequency shifter using the analytic signal obtained. */
for(k = 0;k < SamplesToDo;k++)
{
ALdouble phase = state->Phase * ((1.0/FRACTIONONE) * 2.0*M_PI);
BufferOut[k] = (ALfloat)(state->Outdata[k].Real*cos(phase) +
state->Outdata[k].Imag*sin(phase)*state->ld_sign);
state->Phase += state->PhaseStep;
state->Phase &= FRACTIONMASK;
}
/* Now, mix the processed sound data to the output. */
MixSamples(BufferOut, NumChannels, SamplesOut, state->CurrentGains, state->TargetGains,
maxi(SamplesToDo, 512), 0, SamplesToDo);
}
typedef struct FshifterStateFactory {
DERIVE_FROM_TYPE(EffectStateFactory);
} FshifterStateFactory;
static ALeffectState *FshifterStateFactory_create(FshifterStateFactory *UNUSED(factory))
{
ALfshifterState *state;
NEW_OBJ0(state, ALfshifterState)();
if(!state) return NULL;
return STATIC_CAST(ALeffectState, state);
}
DEFINE_EFFECTSTATEFACTORY_VTABLE(FshifterStateFactory);
EffectStateFactory *FshifterStateFactory_getFactory(void)
{
static FshifterStateFactory FshifterFactory = { { GET_VTABLE2(FshifterStateFactory, EffectStateFactory) } };
return STATIC_CAST(EffectStateFactory, &FshifterFactory);
}
void ALfshifter_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_FREQUENCY_SHIFTER_FREQUENCY:
if(!(val >= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY && val <= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY))
SETERR_RETURN(context, AL_INVALID_VALUE,,"Frequency shifter frequency out of range");
props->Fshifter.Frequency = val;
break;
default:
alSetError(context, AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x", param);
}
}
void ALfshifter_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALfshifter_setParamf(effect, context, param, vals[0]);
}
void ALfshifter_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
if(!(val >= AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION && val <= AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION))
SETERR_RETURN(context, AL_INVALID_VALUE,,"Frequency shifter left direction out of range");
props->Fshifter.LeftDirection = val;
break;
case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
if(!(val >= AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION && val <= AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION))
SETERR_RETURN(context, AL_INVALID_VALUE,,"Frequency shifter right direction out of range");
props->Fshifter.RightDirection = val;
break;
default:
alSetError(context, AL_INVALID_ENUM, "Invalid frequency shifter integer property 0x%04x", param);
}
}
void ALfshifter_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALfshifter_setParami(effect, context, param, vals[0]);
}
void ALfshifter_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
*val = props->Fshifter.LeftDirection;
break;
case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
*val = props->Fshifter.RightDirection;
break;
default:
alSetError(context, AL_INVALID_ENUM, "Invalid frequency shifter integer property 0x%04x", param);
}
}
void ALfshifter_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALfshifter_getParami(effect, context, param, vals);
}
void ALfshifter_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_FREQUENCY_SHIFTER_FREQUENCY:
*val = props->Fshifter.Frequency;
break;
default:
alSetError(context, AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x", param);
}
}
void ALfshifter_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALfshifter_getParamf(effect, context, param, vals);
}
DEFINE_ALEFFECT_VTABLE(ALfshifter);
+171 -167
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,185 +24,197 @@
#include <stdlib.h>
#include "alMain.h"
#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
#include "filters/defs.h"
#define MAX_UPDATE_SAMPLES 128
typedef struct ALmodulatorState {
DERIVE_FROM_TYPE(ALeffectState);
enum {
SINUSOID,
SAWTOOTH,
SQUARE
} Waveform;
void (*GetSamples)(ALfloat*, ALsizei, const ALsizei, ALsizei);
ALuint index;
ALuint step;
ALsizei index;
ALsizei step;
ALfloat Gain[MaxChannels];
struct {
BiquadFilter Filter;
ALfilterState Filter;
ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
} Chans[MAX_EFFECT_CHANNELS];
} ALmodulatorState;
#define WAVEFORM_FRACBITS 24
#define WAVEFORM_FRACONE (1<<WAVEFORM_FRACBITS)
#define WAVEFORM_FRACMASK (WAVEFORM_FRACONE-1)
static inline ALfloat Sin(ALuint index)
{
return sinf(index*(F_2PI/WAVEFORM_FRACONE) - F_PI)*0.5f + 0.5f;
}
static inline ALfloat Saw(ALuint index)
{
return (ALfloat)index / WAVEFORM_FRACONE;
}
static inline ALfloat Square(ALuint index)
{
return (ALfloat)((index >> (WAVEFORM_FRACBITS - 1)) & 1);
}
#define DECL_TEMPLATE(func) \
static void Process##func(ALmodulatorState *state, ALuint SamplesToDo, \
const ALfloat *restrict SamplesIn, \
ALfloat (*restrict SamplesOut)[BUFFERSIZE]) \
{ \
const ALuint step = state->step; \
ALuint index = state->index; \
ALuint base; \
\
for(base = 0;base < SamplesToDo;) \
{ \
ALfloat temps[64]; \
ALuint td = minu(SamplesToDo-base, 64); \
ALuint i, k; \
\
for(i = 0;i < td;i++) \
{ \
ALfloat samp; \
samp = SamplesIn[base+i]; \
samp = ALfilterState_processSingle(&state->Filter, samp); \
\
index += step; \
index &= WAVEFORM_FRACMASK; \
temps[i] = samp * func(index); \
} \
\
for(k = 0;k < MaxChannels;k++) \
{ \
ALfloat gain = state->Gain[k]; \
if(!(gain > GAIN_SILENCE_THRESHOLD)) \
continue; \
\
for(i = 0;i < td;i++) \
SamplesOut[k][base+i] += gain * temps[i]; \
} \
\
base += td; \
} \
state->index = index; \
}
DECL_TEMPLATE(Sin)
DECL_TEMPLATE(Saw)
DECL_TEMPLATE(Square)
#undef DECL_TEMPLATE
static ALvoid ALmodulatorState_Destruct(ALmodulatorState *UNUSED(state))
{
}
static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *UNUSED(state), ALCdevice *UNUSED(device))
{
return AL_TRUE;
}
static ALvoid ALmodulatorState_update(ALmodulatorState *state, ALCdevice *Device, const ALeffectslot *Slot)
{
ALfloat gain, cw, a;
if(Slot->EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SINUSOID)
state->Waveform = SINUSOID;
else if(Slot->EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SAWTOOTH)
state->Waveform = SAWTOOTH;
else if(Slot->EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SQUARE)
state->Waveform = SQUARE;
state->step = fastf2u(Slot->EffectProps.Modulator.Frequency*WAVEFORM_FRACONE /
Device->Frequency);
if(state->step == 0) state->step = 1;
/* Custom filter coeffs, which match the old version instead of a low-shelf. */
cw = cosf(F_2PI * Slot->EffectProps.Modulator.HighPassCutoff / Device->Frequency);
a = (2.0f-cw) - sqrtf(powf(2.0f-cw, 2.0f) - 1.0f);
state->Filter.b[0] = a;
state->Filter.b[1] = -a;
state->Filter.b[2] = 0.0f;
state->Filter.a[0] = 1.0f;
state->Filter.a[1] = -a;
state->Filter.a[2] = 0.0f;
gain = sqrtf(1.0f/Device->NumChan) * Slot->Gain;
SetGains(Device, gain, state->Gain);
}
static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
{
switch(state->Waveform)
{
case SINUSOID:
ProcessSin(state, SamplesToDo, SamplesIn, SamplesOut);
break;
case SAWTOOTH:
ProcessSaw(state, SamplesToDo, SamplesIn, SamplesOut);
break;
case SQUARE:
ProcessSquare(state, SamplesToDo, SamplesIn, SamplesOut);
break;
}
}
static ALvoid ALmodulatorState_Destruct(ALmodulatorState *state);
static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *state, ALCdevice *device);
static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCcontext *context, 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);
typedef struct ALmodulatorStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALmodulatorStateFactory;
#define WAVEFORM_FRACBITS 24
#define WAVEFORM_FRACONE (1<<WAVEFORM_FRACBITS)
#define WAVEFORM_FRACMASK (WAVEFORM_FRACONE-1)
static ALeffectState *ALmodulatorStateFactory_create(ALmodulatorStateFactory *UNUSED(factory))
static inline ALfloat Sin(ALsizei index)
{
ALmodulatorState *state;
return sinf((ALfloat)index * (F_TAU / WAVEFORM_FRACONE));
}
state = ALmodulatorState_New(sizeof(*state));
if(!state) return NULL;
static inline ALfloat Saw(ALsizei index)
{
return (ALfloat)index*(2.0f/WAVEFORM_FRACONE) - 1.0f;
}
static inline ALfloat Square(ALsizei index)
{
return (ALfloat)(((index>>(WAVEFORM_FRACBITS-2))&2) - 1);
}
static inline ALfloat One(ALsizei UNUSED(index))
{
return 1.0f;
}
#define DECL_TEMPLATE(func) \
static void Modulate##func(ALfloat *restrict dst, ALsizei index, \
const ALsizei step, ALsizei todo) \
{ \
ALsizei i; \
for(i = 0;i < todo;i++) \
{ \
index += step; \
index &= WAVEFORM_FRACMASK; \
dst[i] = func(index); \
} \
}
DECL_TEMPLATE(Sin)
DECL_TEMPLATE(Saw)
DECL_TEMPLATE(Square)
DECL_TEMPLATE(One)
#undef DECL_TEMPLATE
static void ALmodulatorState_Construct(ALmodulatorState *state)
{
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
SET_VTABLE2(ALmodulatorState, ALeffectState, state);
state->index = 0;
state->step = 1;
}
ALfilterState_clear(&state->Filter);
static ALvoid ALmodulatorState_Destruct(ALmodulatorState *state)
{
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *state, ALCdevice *UNUSED(device))
{
ALsizei i, j;
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
{
BiquadFilter_clear(&state->Chans[i].Filter);
for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
state->Chans[i].CurrentGains[j] = 0.0f;
}
return AL_TRUE;
}
static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
{
const ALCdevice *device = context->Device;
ALfloat f0norm;
ALsizei i;
state->step = fastf2i(props->Modulator.Frequency / (ALfloat)device->Frequency *
WAVEFORM_FRACONE);
state->step = clampi(state->step, 0, WAVEFORM_FRACONE-1);
if(state->step == 0)
state->GetSamples = ModulateOne;
else if(props->Modulator.Waveform == AL_RING_MODULATOR_SINUSOID)
state->GetSamples = ModulateSin;
else if(props->Modulator.Waveform == AL_RING_MODULATOR_SAWTOOTH)
state->GetSamples = ModulateSaw;
else /*if(Slot->Params.EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SQUARE)*/
state->GetSamples = ModulateSquare;
f0norm = props->Modulator.HighPassCutoff / (ALfloat)device->Frequency;
f0norm = clampf(f0norm, 1.0f/512.0f, 0.49f);
/* Bandwidth value is constant in octaves. */
BiquadFilter_setParams(&state->Chans[0].Filter, BiquadType_HighPass, 1.0f,
f0norm, calc_rcpQ_from_bandwidth(f0norm, 0.75f));
for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
BiquadFilter_copyParams(&state->Chans[i].Filter, &state->Chans[0].Filter);
STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain,
state->Chans[i].TargetGains);
}
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 base;
for(base = 0;base < SamplesToDo;)
{
alignas(16) ALfloat modsamples[MAX_UPDATE_SAMPLES];
ALsizei td = mini(MAX_UPDATE_SAMPLES, SamplesToDo-base);
ALsizei c, i;
state->GetSamples(modsamples, state->index, step, td);
state->index += (step*td) & WAVEFORM_FRACMASK;
state->index &= WAVEFORM_FRACMASK;
for(c = 0;c < MAX_EFFECT_CHANNELS;c++)
{
alignas(16) ALfloat temps[MAX_UPDATE_SAMPLES];
BiquadFilter_process(&state->Chans[c].Filter, temps, &SamplesIn[c][base], td);
for(i = 0;i < td;i++)
temps[i] *= modsamples[i];
MixSamples(temps, NumChannels, SamplesOut, state->Chans[c].CurrentGains,
state->Chans[c].TargetGains, SamplesToDo-base, base, td);
}
base += td;
}
}
typedef struct ModulatorStateFactory {
DERIVE_FROM_TYPE(EffectStateFactory);
} ModulatorStateFactory;
static ALeffectState *ModulatorStateFactory_create(ModulatorStateFactory *UNUSED(factory))
{
ALmodulatorState *state;
NEW_OBJ0(state, ALmodulatorState)();
if(!state) return NULL;
return STATIC_CAST(ALeffectState, state);
}
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALmodulatorStateFactory);
DEFINE_EFFECTSTATEFACTORY_VTABLE(ModulatorStateFactory);
ALeffectStateFactory *ALmodulatorStateFactory_getFactory(void)
EffectStateFactory *ModulatorStateFactory_getFactory(void)
{
static ALmodulatorStateFactory ModulatorFactory = { { GET_VTABLE2(ALmodulatorStateFactory, ALeffectStateFactory) } };
static ModulatorStateFactory ModulatorFactory = { { GET_VTABLE2(ModulatorStateFactory, EffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &ModulatorFactory);
return STATIC_CAST(EffectStateFactory, &ModulatorFactory);
}
@@ -213,24 +225,22 @@ void ALmodulator_setParamf(ALeffect *effect, ALCcontext *context, ALenum param,
{
case AL_RING_MODULATOR_FREQUENCY:
if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Modulator frequency out of range");
props->Modulator.Frequency = val;
break;
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Modulator high-pass cutoff out of range");
props->Modulator.HighPassCutoff = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
alSetError(context, AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param);
}
}
void ALmodulator_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALmodulator_setParamf(effect, context, param, vals[0]);
}
{ ALmodulator_setParamf(effect, context, param, vals[0]); }
void ALmodulator_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
{
ALeffectProps *props = &effect->Props;
@@ -243,18 +253,16 @@ void ALmodulator_setParami(ALeffect *effect, ALCcontext *context, ALenum param,
case AL_RING_MODULATOR_WAVEFORM:
if(!(val >= AL_RING_MODULATOR_MIN_WAVEFORM && val <= AL_RING_MODULATOR_MAX_WAVEFORM))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid modulator waveform");
props->Modulator.Waveform = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
alSetError(context, AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x", param);
}
}
void ALmodulator_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALmodulator_setParami(effect, context, param, vals[0]);
}
{ ALmodulator_setParami(effect, context, param, vals[0]); }
void ALmodulator_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
{
@@ -272,13 +280,11 @@ void ALmodulator_getParami(const ALeffect *effect, ALCcontext *context, ALenum p
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
alSetError(context, AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x", param);
}
}
void ALmodulator_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALmodulator_getParami(effect, context, param, vals);
}
{ ALmodulator_getParami(effect, context, param, vals); }
void ALmodulator_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
@@ -292,12 +298,10 @@ void ALmodulator_getParamf(const ALeffect *effect, ALCcontext *context, ALenum p
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
alSetError(context, AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param);
}
}
void ALmodulator_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALmodulator_getParamf(effect, context, param, vals);
}
{ ALmodulator_getParamf(effect, context, param, vals); }
DEFINE_ALEFFECT_VTABLE(ALmodulator);
+65 -51
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 ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
static ALvoid ALnullState_process(ALnullState *state, ALsizei samplesToDo, const ALfloat (*restrict samplesIn)[BUFFERSIZE], ALfloat (*restrict samplesOut)[BUFFERSIZE], ALsizei mumChannels);
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 ALCcontext* UNUSED(context), const ALeffectslot* UNUSED(slot), const ALeffectProps* UNUSED(props))
{
}
@@ -41,124 +64,115 @@ 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);
} ALnullStateFactory;
typedef struct NullStateFactory {
DERIVE_FROM_TYPE(EffectStateFactory);
} NullStateFactory;
/* Creates ALeffectState objects of the appropriate type. */
ALeffectState *ALnullStateFactory_create(ALnullStateFactory *UNUSED(factory))
ALeffectState *NullStateFactory_create(NullStateFactory *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);
}
/* Define the ALeffectStateFactory vtable for this type. */
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALnullStateFactory);
/* Define the EffectStateFactory vtable for this type. */
DEFINE_EFFECTSTATEFACTORY_VTABLE(NullStateFactory);
ALeffectStateFactory *ALnullStateFactory_getFactory(void)
EffectStateFactory *NullStateFactory_getFactory(void)
{
static ALnullStateFactory NullFactory = { { GET_VTABLE2(ALnullStateFactory, ALeffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &NullFactory);
static NullStateFactory NullFactory = { { GET_VTABLE2(NullStateFactory, EffectStateFactory) } };
return STATIC_CAST(EffectStateFactory, &NullFactory);
}
void ALnull_setParami(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
void ALnull_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
{
switch(param)
{
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
default:
alSetError(context, AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", param);
}
}
void ALnull_setParamiv(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, const ALint* UNUSED(vals))
void ALnull_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint* UNUSED(vals))
{
switch(param)
{
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
default:
alSetError(context, AL_INVALID_ENUM, "Invalid null effect integer-vector property 0x%04x", param);
}
}
void ALnull_setParamf(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat UNUSED(val))
void ALnull_setParamf(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat UNUSED(val))
{
switch(param)
{
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
default:
alSetError(context, AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", param);
}
}
void ALnull_setParamfv(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, const ALfloat* UNUSED(vals))
void ALnull_setParamfv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALfloat* UNUSED(vals))
{
switch(param)
{
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
default:
alSetError(context, AL_INVALID_ENUM, "Invalid null effect float-vector property 0x%04x", param);
}
}
void ALnull_getParami(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(val))
void ALnull_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(val))
{
switch(param)
{
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
default:
alSetError(context, AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", param);
}
}
void ALnull_getParamiv(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(vals))
void ALnull_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(vals))
{
switch(param)
{
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
default:
alSetError(context, AL_INVALID_ENUM, "Invalid null effect integer-vector property 0x%04x", param);
}
}
void ALnull_getParamf(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(val))
void ALnull_getParamf(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(val))
{
switch(param)
{
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
default:
alSetError(context, AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", param);
}
}
void ALnull_getParamfv(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(vals))
void ALnull_getParamfv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(vals))
{
switch(param)
{
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
default:
alSetError(context, AL_INVALID_ENUM, "Invalid null effect float-vector property 0x%04x", param);
}
}
+441
View File
@@ -0,0 +1,441 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2018 by Raul Herraiz.
* 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 <math.h>
#include <stdlib.h>
#include "alMain.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
#include "filters/defs.h"
#include "alcomplex.h"
#define STFT_SIZE 1024
#define STFT_HALF_SIZE (STFT_SIZE>>1)
#define OVERSAMP (1<<2)
#define STFT_STEP (STFT_SIZE / OVERSAMP)
#define FIFO_LATENCY (STFT_STEP * (OVERSAMP-1))
typedef struct ALphasor {
ALdouble Amplitude;
ALdouble Phase;
} ALphasor;
typedef struct ALFrequencyDomain {
ALdouble Amplitude;
ALdouble Frequency;
} ALfrequencyDomain;
typedef struct ALpshifterState {
DERIVE_FROM_TYPE(ALeffectState);
/* Effect parameters */
ALsizei count;
ALsizei PitchShiftI;
ALfloat PitchShift;
ALfloat FreqPerBin;
/*Effects buffers*/
ALfloat InFIFO[STFT_SIZE];
ALfloat OutFIFO[STFT_STEP];
ALdouble LastPhase[STFT_HALF_SIZE+1];
ALdouble SumPhase[STFT_HALF_SIZE+1];
ALdouble OutputAccum[STFT_SIZE];
ALcomplex FFTbuffer[STFT_SIZE];
ALfrequencyDomain Analysis_buffer[STFT_HALF_SIZE+1];
ALfrequencyDomain Syntesis_buffer[STFT_HALF_SIZE+1];
alignas(16) ALfloat BufferOut[BUFFERSIZE];
/* Effect gains for each output channel */
ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
} ALpshifterState;
static ALvoid ALpshifterState_Destruct(ALpshifterState *state);
static ALboolean ALpshifterState_deviceUpdate(ALpshifterState *state, ALCdevice *device);
static ALvoid ALpshifterState_update(ALpshifterState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
DECLARE_DEFAULT_ALLOCATORS(ALpshifterState)
DEFINE_ALEFFECTSTATE_VTABLE(ALpshifterState);
/* Define a Hann window, used to filter the STFT input and output. */
alignas(16) static ALdouble HannWindow[STFT_SIZE];
static void InitHannWindow(void)
{
ALsizei i;
/* Create lookup table of the Hann window for the desired size, i.e. STFT_SIZE */
for(i = 0;i < STFT_SIZE>>1;i++)
{
ALdouble val = sin(M_PI * (ALdouble)i / (ALdouble)(STFT_SIZE-1));
HannWindow[i] = HannWindow[STFT_SIZE-1-i] = val * val;
}
}
static alonce_flag HannInitOnce = AL_ONCE_FLAG_INIT;
static inline ALint double2int(ALdouble d)
{
#if ((defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) && \
!defined(__SSE2_MATH__)) || (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP < 2)
ALint sign, shift;
ALint64 mant;
union {
ALdouble d;
ALint64 i64;
} conv;
conv.d = d;
sign = (conv.i64>>63) | 1;
shift = ((conv.i64>>52)&0x7ff) - (1023+52);
/* Over/underflow */
if(UNLIKELY(shift >= 63 || shift < -52))
return 0;
mant = (conv.i64&I64(0xfffffffffffff)) | I64(0x10000000000000);
if(LIKELY(shift < 0))
return (ALint)(mant >> -shift) * sign;
return (ALint)(mant << shift) * sign;
#else
return (ALint)d;
#endif
}
/* Converts ALcomplex to ALphasor */
static inline ALphasor rect2polar(ALcomplex number)
{
ALphasor polar;
polar.Amplitude = sqrt(number.Real*number.Real + number.Imag*number.Imag);
polar.Phase = atan2(number.Imag, number.Real);
return polar;
}
/* Converts ALphasor to ALcomplex */
static inline ALcomplex polar2rect(ALphasor number)
{
ALcomplex cartesian;
cartesian.Real = number.Amplitude * cos(number.Phase);
cartesian.Imag = number.Amplitude * sin(number.Phase);
return cartesian;
}
static void ALpshifterState_Construct(ALpshifterState *state)
{
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
SET_VTABLE2(ALpshifterState, ALeffectState, state);
alcall_once(&HannInitOnce, InitHannWindow);
}
static ALvoid ALpshifterState_Destruct(ALpshifterState *state)
{
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
static ALboolean ALpshifterState_deviceUpdate(ALpshifterState *state, ALCdevice *device)
{
/* (Re-)initializing parameters and clear the buffers. */
state->count = FIFO_LATENCY;
state->PitchShiftI = FRACTIONONE;
state->PitchShift = 1.0f;
state->FreqPerBin = device->Frequency / (ALfloat)STFT_SIZE;
memset(state->InFIFO, 0, sizeof(state->InFIFO));
memset(state->OutFIFO, 0, sizeof(state->OutFIFO));
memset(state->FFTbuffer, 0, sizeof(state->FFTbuffer));
memset(state->LastPhase, 0, sizeof(state->LastPhase));
memset(state->SumPhase, 0, sizeof(state->SumPhase));
memset(state->OutputAccum, 0, sizeof(state->OutputAccum));
memset(state->Analysis_buffer, 0, sizeof(state->Analysis_buffer));
memset(state->Syntesis_buffer, 0, sizeof(state->Syntesis_buffer));
memset(state->CurrentGains, 0, sizeof(state->CurrentGains));
memset(state->TargetGains, 0, sizeof(state->TargetGains));
return AL_TRUE;
}
static ALvoid ALpshifterState_update(ALpshifterState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
{
const ALCdevice *device = context->Device;
ALfloat coeffs[MAX_AMBI_COEFFS];
float pitch;
pitch = powf(2.0f,
(ALfloat)(props->Pshifter.CoarseTune*100 + props->Pshifter.FineTune) / 1200.0f
);
state->PitchShiftI = fastf2i(pitch*FRACTIONONE);
state->PitchShift = state->PitchShiftI * (1.0f/FRACTIONONE);
CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->TargetGains);
}
static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
/* Pitch shifter engine based on the work of Stephan Bernsee.
* http://blogs.zynaptiq.com/bernsee/pitch-shifting-using-the-ft/
*/
static const ALdouble expected = M_PI*2.0 / OVERSAMP;
const ALdouble freq_per_bin = state->FreqPerBin;
ALfloat *restrict bufferOut = state->BufferOut;
ALsizei count = state->count;
ALsizei i, j, k;
for(i = 0;i < SamplesToDo;)
{
do {
/* Fill FIFO buffer with samples data */
state->InFIFO[count] = SamplesIn[0][i];
bufferOut[i] = state->OutFIFO[count - FIFO_LATENCY];
count++;
} while(++i < SamplesToDo && count < STFT_SIZE);
/* Check whether FIFO buffer is filled */
if(count < STFT_SIZE) break;
count = FIFO_LATENCY;
/* Real signal windowing and store in FFTbuffer */
for(k = 0;k < STFT_SIZE;k++)
{
state->FFTbuffer[k].Real = state->InFIFO[k] * HannWindow[k];
state->FFTbuffer[k].Imag = 0.0;
}
/* ANALYSIS */
/* Apply FFT to FFTbuffer data */
complex_fft(state->FFTbuffer, STFT_SIZE, -1.0);
/* Analyze the obtained data. Since the real FFT is symmetric, only
* STFT_HALF_SIZE+1 samples are needed.
*/
for(k = 0;k < STFT_HALF_SIZE+1;k++)
{
ALphasor component;
ALdouble tmp;
ALint qpd;
/* Compute amplitude and phase */
component = rect2polar(state->FFTbuffer[k]);
/* Compute phase difference and subtract expected phase difference */
tmp = (component.Phase - state->LastPhase[k]) - k*expected;
/* Map delta phase into +/- Pi interval */
qpd = double2int(tmp / M_PI);
tmp -= M_PI * (qpd + (qpd%2));
/* Get deviation from bin frequency from the +/- Pi interval */
tmp /= expected;
/* Compute the k-th partials' true frequency, twice the amplitude
* for maintain the gain (because half of bins are used) and store
* amplitude and true frequency in analysis buffer.
*/
state->Analysis_buffer[k].Amplitude = 2.0 * component.Amplitude;
state->Analysis_buffer[k].Frequency = (k + tmp) * freq_per_bin;
/* Store actual phase[k] for the calculations in the next frame*/
state->LastPhase[k] = component.Phase;
}
/* PROCESSING */
/* pitch shifting */
for(k = 0;k < STFT_HALF_SIZE+1;k++)
{
state->Syntesis_buffer[k].Amplitude = 0.0;
state->Syntesis_buffer[k].Frequency = 0.0;
}
for(k = 0;k < STFT_HALF_SIZE+1;k++)
{
j = (k*state->PitchShiftI) >> FRACTIONBITS;
if(j >= STFT_HALF_SIZE+1) break;
state->Syntesis_buffer[j].Amplitude += state->Analysis_buffer[k].Amplitude;
state->Syntesis_buffer[j].Frequency = state->Analysis_buffer[k].Frequency *
state->PitchShift;
}
/* SYNTHESIS */
/* Synthesis the processing data */
for(k = 0;k < STFT_HALF_SIZE+1;k++)
{
ALphasor component;
ALdouble tmp;
/* Compute bin deviation from scaled freq */
tmp = state->Syntesis_buffer[k].Frequency/freq_per_bin - k;
/* Calculate actual delta phase and accumulate it to get bin phase */
state->SumPhase[k] += (k + tmp) * expected;
component.Amplitude = state->Syntesis_buffer[k].Amplitude;
component.Phase = state->SumPhase[k];
/* Compute phasor component to cartesian complex number and storage it into FFTbuffer*/
state->FFTbuffer[k] = polar2rect(component);
}
/* zero negative frequencies for recontruct a real signal */
for(k = STFT_HALF_SIZE+1;k < STFT_SIZE;k++)
{
state->FFTbuffer[k].Real = 0.0;
state->FFTbuffer[k].Imag = 0.0;
}
/* Apply iFFT to buffer data */
complex_fft(state->FFTbuffer, STFT_SIZE, 1.0);
/* Windowing and add to output */
for(k = 0;k < STFT_SIZE;k++)
state->OutputAccum[k] += HannWindow[k] * state->FFTbuffer[k].Real /
(0.5 * STFT_HALF_SIZE * OVERSAMP);
/* Shift accumulator, input & output FIFO */
for(k = 0;k < STFT_STEP;k++) state->OutFIFO[k] = (ALfloat)state->OutputAccum[k];
for(j = 0;k < STFT_SIZE;k++,j++) state->OutputAccum[j] = state->OutputAccum[k];
for(;j < STFT_SIZE;j++) state->OutputAccum[j] = 0.0;
for(k = 0;k < FIFO_LATENCY;k++)
state->InFIFO[k] = state->InFIFO[k+STFT_STEP];
}
state->count = count;
/* Now, mix the processed sound data to the output. */
MixSamples(bufferOut, NumChannels, SamplesOut, state->CurrentGains, state->TargetGains,
maxi(SamplesToDo, 512), 0, SamplesToDo);
}
typedef struct PshifterStateFactory {
DERIVE_FROM_TYPE(EffectStateFactory);
} PshifterStateFactory;
static ALeffectState *PshifterStateFactory_create(PshifterStateFactory *UNUSED(factory))
{
ALpshifterState *state;
NEW_OBJ0(state, ALpshifterState)();
if(!state) return NULL;
return STATIC_CAST(ALeffectState, state);
}
DEFINE_EFFECTSTATEFACTORY_VTABLE(PshifterStateFactory);
EffectStateFactory *PshifterStateFactory_getFactory(void)
{
static PshifterStateFactory PshifterFactory = { { GET_VTABLE2(PshifterStateFactory, EffectStateFactory) } };
return STATIC_CAST(EffectStateFactory, &PshifterFactory);
}
void ALpshifter_setParamf(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat UNUSED(val))
{
alSetError( context, AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param );
}
void ALpshifter_setParamfv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALfloat *UNUSED(vals))
{
alSetError( context, AL_INVALID_ENUM, "Invalid pitch shifter float-vector property 0x%04x", param );
}
void ALpshifter_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_PITCH_SHIFTER_COARSE_TUNE:
if(!(val >= AL_PITCH_SHIFTER_MIN_COARSE_TUNE && val <= AL_PITCH_SHIFTER_MAX_COARSE_TUNE))
SETERR_RETURN(context, AL_INVALID_VALUE,,"Pitch shifter coarse tune out of range");
props->Pshifter.CoarseTune = val;
break;
case AL_PITCH_SHIFTER_FINE_TUNE:
if(!(val >= AL_PITCH_SHIFTER_MIN_FINE_TUNE && val <= AL_PITCH_SHIFTER_MAX_FINE_TUNE))
SETERR_RETURN(context, AL_INVALID_VALUE,,"Pitch shifter fine tune out of range");
props->Pshifter.FineTune = val;
break;
default:
alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x", param);
}
}
void ALpshifter_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALpshifter_setParami(effect, context, param, vals[0]);
}
void ALpshifter_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_PITCH_SHIFTER_COARSE_TUNE:
*val = (ALint)props->Pshifter.CoarseTune;
break;
case AL_PITCH_SHIFTER_FINE_TUNE:
*val = (ALint)props->Pshifter.FineTune;
break;
default:
alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x", param);
}
}
void ALpshifter_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALpshifter_getParami(effect, context, param, vals);
}
void ALpshifter_getParamf(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat *UNUSED(val))
{
alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param);
}
void ALpshifter_getParamfv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat *UNUSED(vals))
{
alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter float vector-property 0x%04x", param);
}
DEFINE_ALEFFECT_VTABLE(ALpshifter);
+1423 -1112
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 */
+112
View File
@@ -0,0 +1,112 @@
#ifndef ALC_FILTER_H
#define ALC_FILTER_H
#include "AL/al.h"
#include "math_defs.h"
/* 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 BiquadType {
/** EFX-style low-pass filter, specifying a gain and reference frequency. */
BiquadType_HighShelf,
/** EFX-style high-pass filter, specifying a gain and reference frequency. */
BiquadType_LowShelf,
/** Peaking filter, specifying a gain and reference frequency. */
BiquadType_Peaking,
/** Low-pass cut-off filter, specifying a cut-off frequency. */
BiquadType_LowPass,
/** High-pass cut-off filter, specifying a cut-off frequency. */
BiquadType_HighPass,
/** Band-pass filter, specifying a center frequency. */
BiquadType_BandPass,
} BiquadType;
typedef struct BiquadFilter {
ALfloat z1, z2; /* Last two delayed components for direct form II. */
ALfloat b0, b1, b2; /* Transfer function coefficients "b" (numerator) */
ALfloat a1, a2; /* Transfer function coefficients "a" (denominator; a0 is
* pre-applied). */
} BiquadFilter;
/* Currently only a C-based filter process method is implemented. */
#define BiquadFilter_process BiquadFilter_processC
/**
* Calculates the rcpQ (i.e. 1/Q) coefficient for shelving filters, using the
* reference gain and shelf slope parameter.
* \param gain 0 < gain
* \param slope 0 < slope <= 1
*/
inline ALfloat calc_rcpQ_from_slope(ALfloat gain, ALfloat slope)
{
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 normalized
* reference frequency and bandwidth.
* \param f0norm 0 < f0norm < 0.5.
* \param bandwidth 0 < bandwidth
*/
inline ALfloat calc_rcpQ_from_bandwidth(ALfloat f0norm, ALfloat bandwidth)
{
ALfloat w0 = F_TAU * f0norm;
return 2.0f*sinhf(logf(2.0f)/2.0f*bandwidth*w0/sinf(w0));
}
inline void BiquadFilter_clear(BiquadFilter *filter)
{
filter->z1 = 0.0f;
filter->z2 = 0.0f;
}
/**
* Sets up the filter state for the specified filter type and its parameters.
*
* \param filter The filter object to prepare.
* \param type The type of filter for the object to apply.
* \param gain The gain for the reference frequency response. Only used by the
* Shelf and Peaking filter types.
* \param f0norm The normalized reference frequency (ref_freq / sample_rate).
* This is the center point for the Shelf, Peaking, and BandPass
* filter types, or the cutoff frequency for the LowPass and
* HighPass filter types.
* \param rcpQ The reciprocal of the Q coefficient for the filter's transition
* band. Can be generated from calc_rcpQ_from_slope or
* calc_rcpQ_from_bandwidth depending on the available data.
*/
void BiquadFilter_setParams(BiquadFilter *filter, BiquadType type, ALfloat gain, ALfloat f0norm, ALfloat rcpQ);
inline void BiquadFilter_copyParams(BiquadFilter *restrict dst, const BiquadFilter *restrict src)
{
dst->b0 = src->b0;
dst->b1 = src->b1;
dst->b2 = src->b2;
dst->a1 = src->a1;
dst->a2 = src->a2;
}
void BiquadFilter_processC(BiquadFilter *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALsizei numsamples);
inline void BiquadFilter_passthru(BiquadFilter *filter, ALsizei numsamples)
{
if(LIKELY(numsamples >= 2))
{
filter->z1 = 0.0f;
filter->z2 = 0.0f;
}
else if(numsamples == 1)
{
filter->z1 = filter->z2;
filter->z2 = 0.0f;
}
}
#endif /* ALC_FILTER_H */
+129
View File
@@ -0,0 +1,129 @@
#include "config.h"
#include "AL/alc.h"
#include "AL/al.h"
#include "alMain.h"
#include "defs.h"
extern inline void BiquadFilter_clear(BiquadFilter *filter);
extern inline void BiquadFilter_copyParams(BiquadFilter *restrict dst, const BiquadFilter *restrict src);
extern inline void BiquadFilter_passthru(BiquadFilter *filter, ALsizei numsamples);
extern inline ALfloat calc_rcpQ_from_slope(ALfloat gain, ALfloat slope);
extern inline ALfloat calc_rcpQ_from_bandwidth(ALfloat f0norm, ALfloat bandwidth);
void BiquadFilter_setParams(BiquadFilter *filter, BiquadType type, ALfloat gain, ALfloat f0norm, ALfloat rcpQ)
{
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
assert(gain > 0.00001f);
w0 = F_TAU * f0norm;
sin_w0 = sinf(w0);
cos_w0 = cosf(w0);
alpha = sin_w0/2.0f * rcpQ;
/* Calculate filter coefficients depending on filter type */
switch(type)
{
case BiquadType_HighShelf:
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 BiquadType_LowShelf:
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 BiquadType_Peaking:
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 BiquadType_LowPass:
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 BiquadType_HighPass:
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 BiquadType_BandPass:
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->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];
}
void BiquadFilter_processC(BiquadFilter *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALsizei numsamples)
{
const ALfloat a1 = filter->a1;
const ALfloat a2 = filter->a2;
const ALfloat b0 = filter->b0;
const ALfloat b1 = filter->b1;
const ALfloat b2 = filter->b2;
ALfloat z1 = filter->z1;
ALfloat z2 = filter->z2;
ALsizei i;
ASSUME(numsamples > 0);
/* Processing loop is Transposed Direct Form II. This requires less storage
* compared to Direct Form I (only two delay components, instead of a four-
* sample history; the last two inputs and outputs), and works better for
* floating-point which favors summing similarly-sized values while being
* less bothered by overflow.
*
* See: http://www.earlevel.com/main/2003/02/28/biquads/
*/
for(i = 0;i < numsamples;i++)
{
ALfloat input = src[i];
ALfloat output = input*b0 + z1;
z1 = input*b1 - output*a1 + z2;
z2 = input*b2 - output*a2;
dst[i] = output;
}
filter->z1 = z1;
filter->z2 = z2;
}
+426
View File
@@ -0,0 +1,426 @@
#include "config.h"
#include "nfc.h"
#include "alMain.h"
#include <string.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 }*/
};
static void NfcFilterCreate1(struct NfcFilter1 *nfc, const float w0, const float w1)
{
float b_00, g_0;
float r;
nfc->base_gain = 1.0f;
nfc->gain = 1.0f;
/* Calculate bass-boost coefficients. */
r = 0.5f * w0;
b_00 = B[1][0] * r;
g_0 = 1.0f + b_00;
nfc->gain *= g_0;
nfc->b1 = 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->base_gain /= g_0;
nfc->gain /= g_0;
nfc->a1 = 2.0f * b_00 / g_0;
}
static void NfcFilterAdjust1(struct NfcFilter1 *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->gain = nfc->base_gain * g_0;
nfc->b1 = 2.0f * b_00 / g_0;
}
static void NfcFilterCreate2(struct NfcFilter2 *nfc, const float w0, const float w1)
{
float b_10, b_11, g_1;
float r;
nfc->base_gain = 1.0f;
nfc->gain = 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->gain *= g_1;
nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
nfc->b2 = 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->base_gain /= g_1;
nfc->gain /= g_1;
nfc->a1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
nfc->a2 = 4.0f * b_11 / g_1;
}
static void NfcFilterAdjust2(struct NfcFilter2 *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->gain = nfc->base_gain * g_1;
nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
nfc->b2 = 4.0f * b_11 / g_1;
}
static void NfcFilterCreate3(struct NfcFilter3 *nfc, const float w0, const float w1)
{
float b_10, b_11, g_1;
float b_00, g_0;
float r;
nfc->base_gain = 1.0f;
nfc->gain = 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->gain *= g_1;
nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
nfc->b2 = 4.0f * b_11 / g_1;
b_00 = B[3][2] * r;
g_0 = 1.0f + b_00;
nfc->gain *= g_0;
nfc->b3 = 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->base_gain /= g_1;
nfc->gain /= g_1;
nfc->a1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
nfc->a2 = 4.0f * b_11 / g_1;
b_00 = B[3][2] * r;
g_0 = 1.0f + b_00;
nfc->base_gain /= g_0;
nfc->gain /= g_0;
nfc->a3 = 2.0f * b_00 / g_0;
}
static void NfcFilterAdjust3(struct NfcFilter3 *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->gain = nfc->base_gain * g_1;
nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
nfc->b2 = 4.0f * b_11 / g_1;
b_00 = B[3][2] * r;
g_0 = 1.0f + b_00;
nfc->gain *= g_0;
nfc->b3 = 2.0f * b_00 / g_0;
}
void NfcFilterCreate(NfcFilter *nfc, const float w0, const float w1)
{
memset(nfc, 0, sizeof(*nfc));
NfcFilterCreate1(&nfc->first, w0, w1);
NfcFilterCreate2(&nfc->second, w0, w1);
NfcFilterCreate3(&nfc->third, w0, w1);
}
void NfcFilterAdjust(NfcFilter *nfc, const float w0)
{
NfcFilterAdjust1(&nfc->first, w0);
NfcFilterAdjust2(&nfc->second, w0);
NfcFilterAdjust3(&nfc->third, w0);
}
void NfcFilterProcess1(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count)
{
const float gain = nfc->first.gain;
const float b1 = nfc->first.b1;
const float a1 = nfc->first.a1;
float z1 = nfc->first.z[0];
int i;
ASSUME(count > 0);
for(i = 0;i < count;i++)
{
float y = src[i]*gain - a1*z1;
float out = y + b1*z1;
z1 += y;
dst[i] = out;
}
nfc->first.z[0] = z1;
}
void NfcFilterProcess2(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count)
{
const float gain = nfc->second.gain;
const float b1 = nfc->second.b1;
const float b2 = nfc->second.b2;
const float a1 = nfc->second.a1;
const float a2 = nfc->second.a2;
float z1 = nfc->second.z[0];
float z2 = nfc->second.z[1];
int i;
ASSUME(count > 0);
for(i = 0;i < count;i++)
{
float y = src[i]*gain - a1*z1 - a2*z2;
float out = y + b1*z1 + b2*z2;
z2 += z1;
z1 += y;
dst[i] = out;
}
nfc->second.z[0] = z1;
nfc->second.z[1] = z2;
}
void NfcFilterProcess3(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count)
{
const float gain = nfc->third.gain;
const float b1 = nfc->third.b1;
const float b2 = nfc->third.b2;
const float b3 = nfc->third.b3;
const float a1 = nfc->third.a1;
const float a2 = nfc->third.a2;
const float a3 = nfc->third.a3;
float z1 = nfc->third.z[0];
float z2 = nfc->third.z[1];
float z3 = nfc->third.z[2];
int i;
ASSUME(count > 0);
for(i = 0;i < count;i++)
{
float y = src[i]*gain - a1*z1 - a2*z2;
float out = y + b1*z1 + b2*z2;
z2 += z1;
z1 += y;
y = out - a3*z3;
out = y + b3*z3;
z3 += y;
dst[i] = out;
}
nfc->third.z[0] = z1;
nfc->third.z[1] = z2;
nfc->third.z[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 NfcFilterProcess(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
+49
View File
@@ -0,0 +1,49 @@
#ifndef FILTER_NFC_H
#define FILTER_NFC_H
struct NfcFilter1 {
float base_gain, gain;
float b1, a1;
float z[1];
};
struct NfcFilter2 {
float base_gain, gain;
float b1, b2, a1, a2;
float z[2];
};
struct NfcFilter3 {
float base_gain, gain;
float b1, b2, b3, a1, a2, a3;
float z[3];
};
typedef struct NfcFilter {
struct NfcFilter1 first;
struct NfcFilter2 second;
struct NfcFilter3 third;
} 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.
*/
void NfcFilterCreate(NfcFilter *nfc, const float w0, const float w1);
void NfcFilterAdjust(NfcFilter *nfc, const float w0);
/* Near-field control filter for first-order ambisonic channels (1-3). */
void NfcFilterProcess1(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count);
/* Near-field control filter for second-order ambisonic channels (4-8). */
void NfcFilterProcess2(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count);
/* Near-field control filter for third-order ambisonic channels (9-15). */
void NfcFilterProcess3(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count);
#endif /* FILTER_NFC_H */
+109
View File
@@ -0,0 +1,109 @@
#include "config.h"
#include "splitter.h"
#include "math_defs.h"
void bandsplit_init(BandSplitter *splitter, ALfloat f0norm)
{
ALfloat w = f0norm * 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 lp_coeff, hp_coeff, lp_y, hp_y, d;
ALfloat lp_z1, lp_z2, hp_z1;
ALsizei i;
ASSUME(count > 0);
hp_coeff = splitter->coeff;
lp_coeff = splitter->coeff*0.5f + 0.5f;
lp_z1 = splitter->lp_z1;
lp_z2 = splitter->lp_z2;
hp_z1 = splitter->hp_z1;
for(i = 0;i < count;i++)
{
ALfloat in = input[i];
/* Low-pass sample processing. */
d = (in - lp_z1) * lp_coeff;
lp_y = lp_z1 + d;
lp_z1 = lp_y + d;
d = (lp_y - lp_z2) * lp_coeff;
lp_y = lp_z2 + d;
lp_z2 = lp_y + d;
lpout[i] = lp_y;
/* All-pass sample processing. */
hp_y = in*hp_coeff + hp_z1;
hp_z1 = in - hp_y*hp_coeff;
/* High-pass generated from removing low-passed output. */
hpout[i] = hp_y - lp_y;
}
splitter->lp_z1 = lp_z1;
splitter->lp_z2 = lp_z2;
splitter->hp_z1 = hp_z1;
}
void splitterap_init(SplitterAllpass *splitter, ALfloat f0norm)
{
ALfloat w = f0norm * 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, in, out;
ALfloat z1;
ALsizei i;
ASSUME(count > 0);
coeff = splitter->coeff;
z1 = splitter->z1;
for(i = 0;i < count;i++)
{
in = samples[i];
out = in*coeff + z1;
z1 = in - out*coeff;
samples[i] = out;
}
splitter->z1 = z1;
}
+40
View File
@@ -0,0 +1,40 @@
#ifndef FILTER_SPLITTER_H
#define FILTER_SPLITTER_H
#include "alMain.h"
/* 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 f0norm);
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 f0norm);
void splitterap_clear(SplitterAllpass *splitter);
void splitterap_process(SplitterAllpass *splitter, ALfloat *restrict samples, ALsizei count);
typedef struct FrontStablizer {
SplitterAllpass APFilter[MAX_OUTPUT_CHANNELS];
BandSplitter LFilter, RFilter;
alignas(16) ALfloat LSplit[2][BUFFERSIZE];
alignas(16) ALfloat RSplit[2][BUFFERSIZE];
} FrontStablizer;
#endif /* FILTER_SPLITTER_H */
+34
View File
@@ -0,0 +1,34 @@
#ifndef FPU_MODES_H
#define FPU_MODES_H
#ifdef HAVE_FENV_H
#include <fenv.h>
#endif
typedef struct FPUCtl {
#if defined(__GNUC__) && defined(HAVE_SSE)
unsigned int sse_state;
#elif defined(HAVE___CONTROL87_2)
unsigned int state;
unsigned int sse_state;
#elif defined(HAVE__CONTROLFP)
unsigned int state;
#endif
} FPUCtl;
void SetMixerFPUMode(FPUCtl *ctl);
void RestoreFPUMode(const FPUCtl *ctl);
#ifdef __GNUC__
/* Use an alternate macro set with GCC to avoid accidental continue or break
* statements within the mixer mode.
*/
#define START_MIXER_MODE() __extension__({ FPUCtl _oldMode; SetMixerFPUMode(&_oldMode)
#define END_MIXER_MODE() RestoreFPUMode(&_oldMode); })
#else
#define START_MIXER_MODE() do { FPUCtl _oldMode; SetMixerFPUMode(&_oldMode)
#define END_MIXER_MODE() RestoreFPUMode(&_oldMode); } while(0)
#endif
#define LEAVE_MIXER_MODE() RestoreFPUMode(&_oldMode)
#endif /* FPU_MODES_H */
+801 -403
View File
File diff suppressed because it is too large Load Diff
+1161 -516
View File
File diff suppressed because it is too large Load Diff
+67 -11
View File
@@ -4,25 +4,81 @@
#include "AL/al.h"
#include "AL/alc.h"
enum DevFmtChannels;
#include "alMain.h"
#include "alstring.h"
#include "atomic.h"
struct Hrtf;
#define HRTF_HISTORY_BITS (6)
#define HRTF_HISTORY_LENGTH (1<<HRTF_HISTORY_BITS)
#define HRTF_HISTORY_MASK (HRTF_HISTORY_LENGTH-1)
#define HRIR_BITS (7)
#define HRIR_LENGTH (1<<HRIR_BITS)
#define HRIR_MASK (HRIR_LENGTH-1)
#define HRTFDELAY_BITS (20)
#define HRTFDELAY_FRACONE (1<<HRTFDELAY_BITS)
#define HRTFDELAY_MASK (HRTFDELAY_FRACONE-1)
const struct Hrtf *GetHrtf(enum DevFmtChannels chans, ALCuint srate);
ALCboolean FindHrtfFormat(enum DevFmtChannels *chans, ALCuint *srate);
struct HrtfEntry;
struct Hrtf {
RefCount ref;
ALuint sampleRate;
ALsizei irSize;
ALfloat distance;
ALubyte evCount;
const ALubyte *azCount;
const ALushort *evOffset;
const ALfloat (*coeffs)[2];
const ALubyte (*delays)[2];
};
typedef struct HrtfState {
alignas(16) ALfloat History[HRTF_HISTORY_LENGTH];
alignas(16) ALfloat Values[HRIR_LENGTH][2];
} HrtfState;
typedef struct HrtfParams {
alignas(16) ALfloat Coeffs[HRIR_LENGTH][2];
ALsizei Delay[2];
ALfloat Gain;
} HrtfParams;
typedef struct DirectHrtfState {
/* HRTF filter state for dry buffer content */
ALsizei Offset;
ALsizei IrSize;
struct {
alignas(16) ALfloat Values[HRIR_LENGTH][2];
alignas(16) ALfloat Coeffs[HRIR_LENGTH][2];
} Chan[];
} DirectHrtfState;
struct AngularPoint {
ALfloat Elev;
ALfloat Azim;
};
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, a matching decoding matrix, and per-order high-
* frequency gains for the decoder. The calculated impulse responses are
* ordered and scaled according to the matrix input.
*/
void BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsizei NumChannels, const struct AngularPoint *AmbiPoints, const ALfloat (*restrict AmbiMatrix)[MAX_AMBI_COEFFS], ALsizei AmbiCount, const ALfloat *restrict AmbiOrderHFGain);
#endif /* ALC_HRTF_H */
+87
View File
@@ -0,0 +1,87 @@
#ifndef INPROGEXT_H
#define INPROGEXT_H
#include "AL/al.h"
#include "AL/alc.h"
#include "AL/alext.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifndef ALC_SOFT_loopback2
#define ALC_SOFT_loopback2 1
#define ALC_AMBISONIC_LAYOUT_SOFT 0xfff0
#define ALC_AMBISONIC_SCALING_SOFT 0xfff1
#define ALC_AMBISONIC_ORDER_SOFT 0xfff2
#define ALC_MAX_AMBISONIC_ORDER_SOFT 0xfff3
#define ALC_BFORMAT3D_SOFT 0x1508
/* Ambisonic layouts */
#define ALC_ACN_SOFT 0xfff4
#define ALC_FUMA_SOFT 0xfff5
/* Ambisonic scalings (normalization) */
/*#define ALC_FUMA_SOFT*/
#define ALC_SN3D_SOFT 0xfff6
#define ALC_N3D_SOFT 0xfff7
#endif
#ifndef AL_SOFT_map_buffer
#define AL_SOFT_map_buffer 1
typedef unsigned int ALbitfieldSOFT;
#define AL_MAP_READ_BIT_SOFT 0x00000001
#define AL_MAP_WRITE_BIT_SOFT 0x00000002
#define AL_MAP_PERSISTENT_BIT_SOFT 0x00000004
#define AL_PRESERVE_DATA_BIT_SOFT 0x00000008
typedef void (AL_APIENTRY*LPALBUFFERSTORAGESOFT)(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags);
typedef void* (AL_APIENTRY*LPALMAPBUFFERSOFT)(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access);
typedef void (AL_APIENTRY*LPALUNMAPBUFFERSOFT)(ALuint buffer);
typedef void (AL_APIENTRY*LPALFLUSHMAPPEDBUFFERSOFT)(ALuint buffer, ALsizei offset, ALsizei length);
#ifdef AL_ALEXT_PROTOTYPES
AL_API void AL_APIENTRY alBufferStorageSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags);
AL_API void* AL_APIENTRY alMapBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access);
AL_API void AL_APIENTRY alUnmapBufferSOFT(ALuint buffer);
AL_API void AL_APIENTRY alFlushMappedBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length);
#endif
#endif
#ifndef AL_SOFT_events
#define AL_SOFT_events 1
#define AL_EVENT_CALLBACK_FUNCTION_SOFT 0x1220
#define AL_EVENT_CALLBACK_USER_PARAM_SOFT 0x1221
#define AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT 0x1222
#define AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT 0x1223
#define AL_EVENT_TYPE_ERROR_SOFT 0x1224
#define AL_EVENT_TYPE_PERFORMANCE_SOFT 0x1225
#define AL_EVENT_TYPE_DEPRECATED_SOFT 0x1226
#define AL_EVENT_TYPE_DISCONNECTED_SOFT 0x1227
typedef void (AL_APIENTRY*ALEVENTPROCSOFT)(ALenum eventType, ALuint object, ALuint param,
ALsizei length, const ALchar *message,
void *userParam);
typedef void (AL_APIENTRY*LPALEVENTCONTROLSOFT)(ALsizei count, const ALenum *types, ALboolean enable);
typedef void (AL_APIENTRY*LPALEVENTCALLBACKSOFT)(ALEVENTPROCSOFT callback, void *userParam);
typedef void* (AL_APIENTRY*LPALGETPOINTERSOFT)(ALenum pname);
typedef void (AL_APIENTRY*LPALGETPOINTERVSOFT)(ALenum pname, void **values);
#ifdef AL_ALEXT_PROTOTYPES
AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, ALboolean enable);
AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *userParam);
AL_API void* AL_APIENTRY alGetPointerSOFT(ALenum pname);
AL_API void AL_APIENTRY alGetPointervSOFT(ALenum pname, void **values);
#endif
#endif
#ifndef AL_SOFT_buffer_layers
#define AL_SOFT_buffer_layers
typedef void (AL_APIENTRY*LPALSOURCEQUEUEBUFFERLAYERSSOFT)(ALuint src, ALsizei nb, const ALuint *buffers);
#ifdef AL_ALEXT_PROTOTYPES
AL_API void AL_APIENTRY alSourceQueueBufferLayersSOFT(ALuint src, ALsizei nb, const ALuint *buffers);
#endif
#endif
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* INPROGEXT_H */
+69
View File
@@ -0,0 +1,69 @@
#ifndef LOGGING_H
#define LOGGING_H
#include <stdio.h>
#ifdef __GNUC__
#define DECL_FORMAT(x, y, z) __attribute__((format(x, (y), (z))))
#else
#define DECL_FORMAT(x, y, z)
#endif
#ifdef __cplusplus
extern "C" {
#endif
extern FILE *LogFile;
#if defined(__GNUC__) && !defined(_WIN32)
#define AL_PRINT(T, MSG, ...) fprintf(LogFile, "AL lib: %s %s: "MSG, T, __FUNCTION__ , ## __VA_ARGS__)
#else
void al_print(const char *type, const char *func, const char *fmt, ...) DECL_FORMAT(printf, 3,4);
#define AL_PRINT(T, ...) al_print((T), __FUNCTION__, __VA_ARGS__)
#endif
#ifdef __ANDROID__
#include <android/log.h>
#define LOG_ANDROID(T, MSG, ...) __android_log_print(T, "openal", "AL lib: %s: "MSG, __FUNCTION__ , ## __VA_ARGS__)
#else
#define LOG_ANDROID(T, MSG, ...) ((void)0)
#endif
enum LogLevel {
NoLog,
LogError,
LogWarning,
LogTrace,
LogRef
};
extern enum LogLevel LogLevel;
#define TRACEREF(...) do { \
if(LogLevel >= LogRef) \
AL_PRINT("(--)", __VA_ARGS__); \
} while(0)
#define TRACE(...) do { \
if(LogLevel >= LogTrace) \
AL_PRINT("(II)", __VA_ARGS__); \
LOG_ANDROID(ANDROID_LOG_DEBUG, __VA_ARGS__); \
} while(0)
#define WARN(...) do { \
if(LogLevel >= LogWarning) \
AL_PRINT("(WW)", __VA_ARGS__); \
LOG_ANDROID(ANDROID_LOG_WARN, __VA_ARGS__); \
} while(0)
#define ERR(...) do { \
if(LogLevel >= LogError) \
AL_PRINT("(EE)", __VA_ARGS__); \
LOG_ANDROID(ANDROID_LOG_ERROR, __VA_ARGS__); \
} while(0)
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* LOGGING_H */
+530
View File
@@ -0,0 +1,530 @@
#include "config.h"
#include <math.h>
#include "mastering.h"
#include "alu.h"
#include "almalloc.h"
#include "static_assert.h"
/* These structures assume BUFFERSIZE is a power of 2. */
static_assert((BUFFERSIZE & (BUFFERSIZE-1)) == 0, "BUFFERSIZE is not a power of 2");
typedef struct SlidingHold {
ALfloat Values[BUFFERSIZE];
ALsizei Expiries[BUFFERSIZE];
ALsizei LowerIndex;
ALsizei UpperIndex;
ALsizei Length;
} SlidingHold;
/* General topology and basic automation was based on the following paper:
*
* D. Giannoulis, M. Massberg and J. D. Reiss,
* "Parameter Automation in a Dynamic Range Compressor,"
* Journal of the Audio Engineering Society, v61 (10), Oct. 2013
*
* Available (along with supplemental reading) at:
*
* http://c4dm.eecs.qmul.ac.uk/audioengineering/compressors/
*/
typedef struct Compressor {
ALsizei NumChans;
ALuint SampleRate;
struct {
ALuint Knee : 1;
ALuint Attack : 1;
ALuint Release : 1;
ALuint PostGain : 1;
ALuint Declip : 1;
} Auto;
ALsizei LookAhead;
ALfloat PreGain;
ALfloat PostGain;
ALfloat Threshold;
ALfloat Slope;
ALfloat Knee;
ALfloat Attack;
ALfloat Release;
alignas(16) ALfloat SideChain[2*BUFFERSIZE];
alignas(16) ALfloat CrestFactor[BUFFERSIZE];
SlidingHold *Hold;
ALfloat (*Delay)[BUFFERSIZE];
ALsizei DelayIndex;
ALfloat CrestCoeff;
ALfloat GainEstimate;
ALfloat AdaptCoeff;
ALfloat LastPeakSq;
ALfloat LastRmsSq;
ALfloat LastRelease;
ALfloat LastAttack;
ALfloat LastGainDev;
} Compressor;
/* This sliding hold follows the input level with an instant attack and a
* fixed duration hold before an instant release to the next highest level.
* It is a sliding window maximum (descending maxima) implementation based on
* Richard Harter's ascending minima algorithm available at:
*
* http://www.richardhartersworld.com/cri/2001/slidingmin.html
*/
static ALfloat UpdateSlidingHold(SlidingHold *Hold, const ALsizei i, const ALfloat in)
{
const ALsizei mask = BUFFERSIZE - 1;
const ALsizei length = Hold->Length;
ALfloat *restrict values = Hold->Values;
ALsizei *restrict expiries = Hold->Expiries;
ALsizei lowerIndex = Hold->LowerIndex;
ALsizei upperIndex = Hold->UpperIndex;
if(i >= expiries[upperIndex])
upperIndex = (upperIndex + 1) & mask;
if(in >= values[upperIndex])
{
values[upperIndex] = in;
expiries[upperIndex] = i + length;
lowerIndex = upperIndex;
}
else
{
do {
do {
if(!(in >= values[lowerIndex]))
goto found_place;
} while(lowerIndex--);
lowerIndex = mask;
} while(1);
found_place:
lowerIndex = (lowerIndex + 1) & mask;
values[lowerIndex] = in;
expiries[lowerIndex] = i + length;
}
Hold->LowerIndex = lowerIndex;
Hold->UpperIndex = upperIndex;
return values[upperIndex];
}
static void ShiftSlidingHold(SlidingHold *Hold, const ALsizei n)
{
const ALsizei lowerIndex = Hold->LowerIndex;
ALsizei *restrict expiries = Hold->Expiries;
ALsizei i = Hold->UpperIndex;
if(lowerIndex < i)
{
for(;i < BUFFERSIZE;i++)
expiries[i] -= n;
i = 0;
}
for(;i < lowerIndex;i++)
expiries[i] -= n;
expiries[i] -= n;
}
/* Multichannel compression is linked via the absolute maximum of all
* channels.
*/
static void LinkChannels(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*restrict OutBuffer)[BUFFERSIZE])
{
const ALsizei index = Comp->LookAhead;
const ALsizei numChans = Comp->NumChans;
ALfloat *restrict sideChain = Comp->SideChain;
ALsizei c, i;
ASSUME(SamplesToDo > 0);
ASSUME(numChans > 0);
for(i = 0;i < SamplesToDo;i++)
sideChain[index + i] = 0.0f;
for(c = 0;c < numChans;c++)
{
ALsizei offset = index;
for(i = 0;i < SamplesToDo;i++)
{
sideChain[offset] = maxf(sideChain[offset], fabsf(OutBuffer[c][i]));
++offset;
}
}
}
/* This calculates the squared crest factor of the control signal for the
* basic automation of the attack/release times. As suggested by the paper,
* it uses an instantaneous squared peak detector and a squared RMS detector
* both with 200ms release times.
*/
static void CrestDetector(Compressor *Comp, const ALsizei SamplesToDo)
{
const ALfloat a_crest = Comp->CrestCoeff;
const ALsizei index = Comp->LookAhead;
const ALfloat *restrict sideChain = Comp->SideChain;
ALfloat *restrict crestFactor = Comp->CrestFactor;
ALfloat y2_peak = Comp->LastPeakSq;
ALfloat y2_rms = Comp->LastRmsSq;
ALsizei i;
ASSUME(SamplesToDo > 0);
for(i = 0;i < SamplesToDo;i++)
{
ALfloat x_abs = sideChain[index + i];
ALfloat x2 = maxf(0.000001f, x_abs * x_abs);
y2_peak = maxf(x2, lerp(x2, y2_peak, a_crest));
y2_rms = lerp(x2, y2_rms, a_crest);
crestFactor[i] = y2_peak / y2_rms;
}
Comp->LastPeakSq = y2_peak;
Comp->LastRmsSq = y2_rms;
}
/* The side-chain starts with a simple peak detector (based on the absolute
* value of the incoming signal) and performs most of its operations in the
* log domain.
*/
static void PeakDetector(Compressor *Comp, const ALsizei SamplesToDo)
{
const ALsizei index = Comp->LookAhead;
ALfloat *restrict sideChain = Comp->SideChain;
ALsizei i;
ASSUME(SamplesToDo > 0);
for(i = 0;i < SamplesToDo;i++)
{
const ALuint offset = index + i;
const ALfloat x_abs = sideChain[offset];
sideChain[offset] = logf(maxf(0.000001f, x_abs));
}
}
/* An optional hold can be used to extend the peak detector so it can more
* solidly detect fast transients. This is best used when operating as a
* limiter.
*/
static void PeakHoldDetector(Compressor *Comp, const ALsizei SamplesToDo)
{
const ALsizei index = Comp->LookAhead;
ALfloat *restrict sideChain = Comp->SideChain;
SlidingHold *hold = Comp->Hold;
ALsizei i;
ASSUME(SamplesToDo > 0);
for(i = 0;i < SamplesToDo;i++)
{
const ALsizei offset = index + i;
const ALfloat x_abs = sideChain[offset];
const ALfloat x_G = logf(maxf(0.000001f, x_abs));
sideChain[offset] = UpdateSlidingHold(hold, i, x_G);
}
ShiftSlidingHold(hold, SamplesToDo);
}
/* This is the heart of the feed-forward compressor. It operates in the log
* domain (to better match human hearing) and can apply some basic automation
* to knee width, attack/release times, make-up/post gain, and clipping
* reduction.
*/
static void GainCompressor(Compressor *Comp, const ALsizei SamplesToDo)
{
const bool autoKnee = Comp->Auto.Knee;
const bool autoAttack = Comp->Auto.Attack;
const bool autoRelease = Comp->Auto.Release;
const bool autoPostGain = Comp->Auto.PostGain;
const bool autoDeclip = Comp->Auto.Declip;
const ALsizei lookAhead = Comp->LookAhead;
const ALfloat threshold = Comp->Threshold;
const ALfloat slope = Comp->Slope;
const ALfloat attack = Comp->Attack;
const ALfloat release = Comp->Release;
const ALfloat c_est = Comp->GainEstimate;
const ALfloat a_adp = Comp->AdaptCoeff;
const ALfloat *restrict crestFactor = Comp->CrestFactor;
ALfloat *restrict sideChain = Comp->SideChain;
ALfloat postGain = Comp->PostGain;
ALfloat knee = Comp->Knee;
ALfloat t_att = attack;
ALfloat t_rel = release - attack;
ALfloat a_att = expf(-1.0f / t_att);
ALfloat a_rel = expf(-1.0f / t_rel);
ALfloat y_1 = Comp->LastRelease;
ALfloat y_L = Comp->LastAttack;
ALfloat c_dev = Comp->LastGainDev;
ALsizei i;
ASSUME(SamplesToDo > 0);
for(i = 0;i < SamplesToDo;i++)
{
const ALfloat y2_crest = crestFactor[i];
const ALfloat x_G = sideChain[lookAhead + i];
const ALfloat x_over = x_G - threshold;
ALfloat knee_h;
ALfloat y_G;
ALfloat x_L;
if(autoKnee)
knee = maxf(0.0f, 2.5f * (c_dev + c_est));
knee_h = 0.5f * knee;
/* This is the gain computer. It applies a static compression curve
* to the control signal.
*/
if(x_over <= -knee_h)
y_G = 0.0f;
else if(fabsf(x_over) < knee_h)
y_G = (x_over + knee_h) * (x_over + knee_h) / (2.0f * knee);
else
y_G = x_over;
x_L = -slope * y_G;
if(autoAttack)
{
t_att = 2.0f * attack / y2_crest;
a_att = expf(-1.0f / t_att);
}
if(autoRelease)
{
t_rel = 2.0f * release / y2_crest - t_att;
a_rel = expf(-1.0f / t_rel);
}
/* Gain smoothing (ballistics) is done via a smooth decoupled peak
* detector. The attack time is subtracted from the release time
* above to compensate for the chained operating mode.
*/
y_1 = maxf(x_L, lerp(x_L, y_1, a_rel));
y_L = lerp(y_1, y_L, a_att);
/* Knee width and make-up gain automation make use of a smoothed
* measurement of deviation between the control signal and estimate.
* The estimate is also used to bias the measurement to hot-start its
* average.
*/
c_dev = lerp(-y_L - c_est, c_dev, a_adp);
if(autoPostGain)
{
/* Clipping reduction is only viable when make-up gain is being
* automated. It modifies the deviation to further attenuate the
* control signal when clipping is detected. The adaptation
* time is sufficiently long enough to suppress further clipping
* at the same output level.
*/
if(autoDeclip)
c_dev = maxf(c_dev, sideChain[i] - y_L - threshold - c_est);
postGain = -(c_dev + c_est);
}
sideChain[i] = expf(postGain - y_L);
}
Comp->LastRelease = y_1;
Comp->LastAttack = y_L;
Comp->LastGainDev = c_dev;
}
/* Combined with the hold time, a look-ahead delay can improve handling of
* fast transients by allowing the envelope time to converge prior to
* reaching the offending impulse. This is best used when operating as a
* limiter.
*/
static void SignalDelay(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*restrict OutBuffer)[BUFFERSIZE])
{
const ALsizei mask = BUFFERSIZE - 1;
const ALsizei numChans = Comp->NumChans;
const ALsizei indexIn = Comp->DelayIndex;
const ALsizei indexOut = Comp->DelayIndex - Comp->LookAhead;
ALfloat (*restrict delay)[BUFFERSIZE] = Comp->Delay;
ALsizei c, i;
ASSUME(SamplesToDo > 0);
ASSUME(numChans > 0);
for(c = 0;c < numChans;c++)
{
for(i = 0;i < SamplesToDo;i++)
{
ALfloat sig = OutBuffer[c][i];
OutBuffer[c][i] = delay[c][(indexOut + i) & mask];
delay[c][(indexIn + i) & mask] = sig;
}
}
Comp->DelayIndex = (indexIn + SamplesToDo) & mask;
}
/* The compressor is initialized with the following settings:
*
* NumChans - Number of channels to process.
* SampleRate - Sample rate to process.
* AutoKnee - Whether to automate the knee width parameter.
* AutoAttack - Whether to automate the attack time parameter.
* AutoRelease - Whether to automate the release time parameter.
* AutoPostGain - Whether to automate the make-up (post) gain parameter.
* AutoDeclip - Whether to automate clipping reduction. Ignored when
* not automating make-up gain.
* LookAheadTime - Look-ahead time (in seconds).
* HoldTime - Peak hold-time (in seconds).
* PreGainDb - Gain applied before detection (in dB).
* PostGainDb - Make-up gain applied after compression (in dB).
* ThresholdDb - Triggering threshold (in dB).
* Ratio - Compression ratio (x:1). Set to INFINITY for true
* limiting. Ignored when automating knee width.
* KneeDb - Knee width (in dB). Ignored when automating knee
* width.
* AttackTimeMin - Attack time (in seconds). Acts as a maximum when
* automating attack time.
* ReleaseTimeMin - Release time (in seconds). Acts as a maximum when
* automating release time.
*/
Compressor* CompressorInit(const ALsizei NumChans, const ALuint SampleRate,
const ALboolean AutoKnee, const ALboolean AutoAttack,
const ALboolean AutoRelease, const ALboolean AutoPostGain,
const ALboolean AutoDeclip, const ALfloat LookAheadTime,
const ALfloat HoldTime, const ALfloat PreGainDb,
const ALfloat PostGainDb, const ALfloat ThresholdDb,
const ALfloat Ratio, const ALfloat KneeDb,
const ALfloat AttackTime, const ALfloat ReleaseTime)
{
Compressor *Comp;
ALsizei lookAhead;
ALsizei hold;
size_t size;
lookAhead = (ALsizei)clampf(roundf(LookAheadTime*SampleRate), 0.0f, BUFFERSIZE-1);
hold = (ALsizei)clampf(roundf(HoldTime*SampleRate), 0.0f, BUFFERSIZE-1);
/* The sliding hold implementation doesn't handle a length of 1. A 1-sample
* hold is useless anyway, it would only ever give back what was just given
* to it.
*/
if(hold == 1)
hold = 0;
size = sizeof(*Comp);
if(lookAhead > 0)
{
size += sizeof(*Comp->Delay) * NumChans;
if(hold > 0)
size += sizeof(*Comp->Hold);
}
Comp = al_calloc(16, size);
Comp->NumChans = NumChans;
Comp->SampleRate = SampleRate;
Comp->Auto.Knee = AutoKnee;
Comp->Auto.Attack = AutoAttack;
Comp->Auto.Release = AutoRelease;
Comp->Auto.PostGain = AutoPostGain;
Comp->Auto.Declip = AutoPostGain && AutoDeclip;
Comp->LookAhead = lookAhead;
Comp->PreGain = powf(10.0f, PreGainDb / 20.0f);
Comp->PostGain = PostGainDb * logf(10.0f) / 20.0f;
Comp->Threshold = ThresholdDb * logf(10.0f) / 20.0f;
Comp->Slope = 1.0f / maxf(1.0f, Ratio) - 1.0f;
Comp->Knee = maxf(0.0f, KneeDb * logf(10.0f) / 20.0f);
Comp->Attack = maxf(1.0f, AttackTime * SampleRate);
Comp->Release = maxf(1.0f, ReleaseTime * SampleRate);
/* Knee width automation actually treats the compressor as a limiter. By
* varying the knee width, it can effectively be seen as applying
* compression over a wide range of ratios.
*/
if(AutoKnee)
Comp->Slope = -1.0f;
if(lookAhead > 0)
{
if(hold > 0)
{
Comp->Hold = (SlidingHold*)(Comp + 1);
Comp->Hold->Values[0] = -INFINITY;
Comp->Hold->Expiries[0] = hold;
Comp->Hold->Length = hold;
Comp->Delay = (ALfloat(*)[])(Comp->Hold + 1);
}
else
{
Comp->Delay = (ALfloat(*)[])(Comp + 1);
}
}
Comp->CrestCoeff = expf(-1.0f / (0.200f * SampleRate)); // 200ms
Comp->GainEstimate = Comp->Threshold * -0.5f * Comp->Slope;
Comp->AdaptCoeff = expf(-1.0f / (2.0f * SampleRate)); // 2s
return Comp;
}
void ApplyCompression(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*restrict OutBuffer)[BUFFERSIZE])
{
const ALsizei numChans = Comp->NumChans;
const ALfloat preGain = Comp->PreGain;
ALfloat *restrict sideChain;
ALsizei c, i;
ASSUME(SamplesToDo > 0);
ASSUME(numChans > 0);
if(preGain != 1.0f)
{
for(c = 0;c < numChans;c++)
{
for(i = 0;i < SamplesToDo;i++)
OutBuffer[c][i] *= preGain;
}
}
LinkChannels(Comp, SamplesToDo, OutBuffer);
if(Comp->Auto.Attack || Comp->Auto.Release)
CrestDetector(Comp, SamplesToDo);
if(Comp->Hold)
PeakHoldDetector(Comp, SamplesToDo);
else
PeakDetector(Comp, SamplesToDo);
GainCompressor(Comp, SamplesToDo);
if(Comp->Delay)
SignalDelay(Comp, SamplesToDo, OutBuffer);
sideChain = Comp->SideChain;
for(c = 0;c < numChans;c++)
{
for(i = 0;i < SamplesToDo;i++)
OutBuffer[c][i] *= sideChain[i];
}
memmove(sideChain, sideChain+SamplesToDo, Comp->LookAhead*sizeof(ALfloat));
}
ALsizei GetCompressorLookAhead(const Compressor *Comp)
{ return Comp->LookAhead; }
+49
View File
@@ -0,0 +1,49 @@
#ifndef MASTERING_H
#define MASTERING_H
#include "AL/al.h"
/* For BUFFERSIZE. */
#include "alMain.h"
struct Compressor;
/* The compressor is initialized with the following settings:
*
* NumChans - Number of channels to process.
* SampleRate - Sample rate to process.
* AutoKnee - Whether to automate the knee width parameter.
* AutoAttack - Whether to automate the attack time parameter.
* AutoRelease - Whether to automate the release time parameter.
* AutoPostGain - Whether to automate the make-up (post) gain parameter.
* AutoDeclip - Whether to automate clipping reduction. Ignored when
* not automating make-up gain.
* LookAheadTime - Look-ahead time (in seconds).
* HoldTime - Peak hold-time (in seconds).
* PreGainDb - Gain applied before detection (in dB).
* PostGainDb - Make-up gain applied after compression (in dB).
* ThresholdDb - Triggering threshold (in dB).
* Ratio - Compression ratio (x:1). Set to INFINIFTY for true
* limiting. Ignored when automating knee width.
* KneeDb - Knee width (in dB). Ignored when automating knee
* width.
* AttackTimeMin - Attack time (in seconds). Acts as a maximum when
* automating attack time.
* ReleaseTimeMin - Release time (in seconds). Acts as a maximum when
* automating release time.
*/
struct Compressor* CompressorInit(const ALsizei NumChans, const ALuint SampleRate,
const ALboolean AutoKnee, const ALboolean AutoAttack,
const ALboolean AutoRelease, const ALboolean AutoPostGain,
const ALboolean AutoDeclip, const ALfloat LookAheadTime,
const ALfloat HoldTime, const ALfloat PreGainDb,
const ALfloat PostGainDb, const ALfloat ThresholdDb,
const ALfloat Ratio, const ALfloat KneeDb,
const ALfloat AttackTime, const ALfloat ReleaseTime);
void ApplyCompression(struct Compressor *Comp, const ALsizei SamplesToDo,
ALfloat (*restrict OutBuffer)[BUFFERSIZE]);
ALsizei GetCompressorLookAhead(const struct Compressor *Comp);
#endif /* MASTERING_H */
-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);
}
-510
View File
@@ -1,510 +0,0 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include "alMain.h"
#include "AL/al.h"
#include "AL/alc.h"
#include "alSource.h"
#include "alBuffer.h"
#include "alListener.h"
#include "alAuxEffectSlot.h"
#include "alu.h"
#include "mixer_defs.h"
extern inline void InitiatePositionArrays(ALuint frac, ALuint increment, ALuint *frac_arr, ALuint *pos_arr, ALuint size);
static inline HrtfMixerFunc SelectHrtfMixer(void)
{
#ifdef HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
return MixHrtf_SSE;
#endif
#ifdef HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
return MixHrtf_Neon;
#endif
return MixHrtf_C;
}
static inline MixerFunc SelectMixer(void)
{
#ifdef HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
return Mix_SSE;
#endif
#ifdef HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
return Mix_Neon;
#endif
return Mix_C;
}
static inline ResamplerFunc SelectResampler(enum Resampler Resampler, ALuint increment)
{
if(increment == FRACTIONONE)
return Resample_copy32_C;
switch(Resampler)
{
case PointResampler:
return Resample_point32_C;
case LinearResampler:
#ifdef HAVE_SSE4_1
if((CPUCapFlags&CPU_CAP_SSE4_1))
return Resample_lerp32_SSE41;
#endif
#ifdef HAVE_SSE2
if((CPUCapFlags&CPU_CAP_SSE2))
return Resample_lerp32_SSE2;
#endif
return Resample_lerp32_C;
case CubicResampler:
return Resample_cubic32_C;
case ResamplerMax:
/* Shouldn't happen */
break;
}
return Resample_point32_C;
}
static inline ALfloat Sample_ALbyte(ALbyte val)
{ return val * (1.0f/127.0f); }
static inline ALfloat Sample_ALshort(ALshort val)
{ return val * (1.0f/32767.0f); }
static inline ALfloat Sample_ALfloat(ALfloat val)
{ return val; }
#define DECL_TEMPLATE(T) \
static void Load_##T(ALfloat *dst, const T *src, ALuint srcstep, ALuint samples)\
{ \
ALuint i; \
for(i = 0;i < samples;i++) \
dst[i] = Sample_##T(src[i*srcstep]); \
}
DECL_TEMPLATE(ALbyte)
DECL_TEMPLATE(ALshort)
DECL_TEMPLATE(ALfloat)
#undef DECL_TEMPLATE
static void LoadSamples(ALfloat *dst, const ALvoid *src, ALuint srcstep, enum FmtType srctype, ALuint samples)
{
switch(srctype)
{
case FmtByte:
Load_ALbyte(dst, src, srcstep, samples);
break;
case FmtShort:
Load_ALshort(dst, src, srcstep, samples);
break;
case FmtFloat:
Load_ALfloat(dst, src, srcstep, samples);
break;
}
}
static void SilenceSamples(ALfloat *dst, ALuint samples)
{
ALuint i;
for(i = 0;i < samples;i++)
dst[i] = 0.0f;
}
static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter,
ALfloat *restrict dst, const ALfloat *restrict src,
ALuint numsamples, enum ActiveFilters type)
{
ALuint i;
switch(type)
{
case AF_None:
break;
case AF_LowPass:
ALfilterState_process(lpfilter, dst, src, numsamples);
return dst;
case AF_HighPass:
ALfilterState_process(hpfilter, dst, src, numsamples);
return dst;
case AF_BandPass:
for(i = 0;i < numsamples;)
{
ALfloat temp[64];
ALuint todo = minu(64, numsamples-i);
ALfilterState_process(lpfilter, temp, src+i, todo);
ALfilterState_process(hpfilter, dst+i, temp, todo);
i += todo;
}
return dst;
}
return src;
}
ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo)
{
MixerFunc Mix;
HrtfMixerFunc HrtfMix;
ResamplerFunc Resample;
ALsource *Source = src->Source;
ALbufferlistitem *BufferListItem;
ALuint DataPosInt, DataPosFrac;
ALboolean Looping;
ALuint increment;
enum Resampler Resampler;
ALenum State;
ALuint OutPos;
ALuint NumChannels;
ALuint SampleSize;
ALint64 DataSize64;
ALuint chan, j;
/* Get source info */
State = Source->state;
BufferListItem = ATOMIC_LOAD(&Source->current_buffer);
DataPosInt = Source->position;
DataPosFrac = Source->position_fraction;
Looping = Source->Looping;
increment = src->Step;
Resampler = (increment==FRACTIONONE) ? PointResampler : Source->Resampler;
NumChannels = Source->NumChannels;
SampleSize = Source->SampleSize;
Mix = SelectMixer();
HrtfMix = SelectHrtfMixer();
Resample = SelectResampler(Resampler, increment);
OutPos = 0;
do {
const ALuint BufferPrePadding = ResamplerPrePadding[Resampler];
const ALuint BufferPadding = ResamplerPadding[Resampler];
ALuint SrcBufferSize, DstBufferSize;
/* Figure out how many buffer samples will be needed */
DataSize64 = SamplesToDo-OutPos;
DataSize64 *= increment;
DataSize64 += DataPosFrac+FRACTIONMASK;
DataSize64 >>= FRACTIONBITS;
DataSize64 += BufferPadding+BufferPrePadding;
SrcBufferSize = (ALuint)mini64(DataSize64, BUFFERSIZE);
/* Figure out how many samples we can actually mix from this. */
DataSize64 = SrcBufferSize;
DataSize64 -= BufferPadding+BufferPrePadding;
DataSize64 <<= FRACTIONBITS;
DataSize64 -= DataPosFrac;
DstBufferSize = (ALuint)((DataSize64+(increment-1)) / increment);
DstBufferSize = minu(DstBufferSize, (SamplesToDo-OutPos));
/* Some mixers like having a multiple of 4, so try to give that unless
* this is the last update. */
if(OutPos+DstBufferSize < SamplesToDo)
DstBufferSize &= ~3;
for(chan = 0;chan < NumChannels;chan++)
{
const ALfloat *ResampledData;
ALfloat *SrcData = Device->SourceData;
ALuint SrcDataSize = 0;
if(Source->SourceType == AL_STATIC)
{
const ALbuffer *ALBuffer = BufferListItem->buffer;
const ALubyte *Data = ALBuffer->data;
ALuint DataSize;
ALuint pos;
/* If current pos is beyond the loop range, do not loop */
if(Looping == AL_FALSE || DataPosInt >= (ALuint)ALBuffer->LoopEnd)
{
Looping = AL_FALSE;
if(DataPosInt >= BufferPrePadding)
pos = DataPosInt - BufferPrePadding;
else
{
DataSize = BufferPrePadding - DataPosInt;
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
SilenceSamples(&SrcData[SrcDataSize], DataSize);
SrcDataSize += DataSize;
pos = 0;
}
/* Copy what's left to play in the source buffer, and clear the
* rest of the temp buffer */
DataSize = minu(SrcBufferSize - SrcDataSize, ALBuffer->SampleLen - pos);
LoadSamples(&SrcData[SrcDataSize], &Data[(pos*NumChannels + chan)*SampleSize],
NumChannels, ALBuffer->FmtType, DataSize);
SrcDataSize += DataSize;
SilenceSamples(&SrcData[SrcDataSize], SrcBufferSize - SrcDataSize);
SrcDataSize += SrcBufferSize - SrcDataSize;
}
else
{
ALuint LoopStart = ALBuffer->LoopStart;
ALuint LoopEnd = ALBuffer->LoopEnd;
if(DataPosInt >= LoopStart)
{
pos = DataPosInt-LoopStart;
while(pos < BufferPrePadding)
pos += LoopEnd-LoopStart;
pos -= BufferPrePadding;
pos += LoopStart;
}
else if(DataPosInt >= BufferPrePadding)
pos = DataPosInt - BufferPrePadding;
else
{
DataSize = BufferPrePadding - DataPosInt;
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
SilenceSamples(&SrcData[SrcDataSize], DataSize);
SrcDataSize += DataSize;
pos = 0;
}
/* Copy what's left of this loop iteration, then copy repeats
* of the loop section */
DataSize = LoopEnd - pos;
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
LoadSamples(&SrcData[SrcDataSize], &Data[(pos*NumChannels + chan)*SampleSize],
NumChannels, ALBuffer->FmtType, DataSize);
SrcDataSize += DataSize;
DataSize = LoopEnd-LoopStart;
while(SrcBufferSize > SrcDataSize)
{
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
LoadSamples(&SrcData[SrcDataSize], &Data[(LoopStart*NumChannels + chan)*SampleSize],
NumChannels, ALBuffer->FmtType, DataSize);
SrcDataSize += DataSize;
}
}
}
else
{
/* Crawl the buffer queue to fill in the temp buffer */
ALbufferlistitem *tmpiter = BufferListItem;
ALuint pos;
if(DataPosInt >= BufferPrePadding)
pos = DataPosInt - BufferPrePadding;
else
{
pos = BufferPrePadding - DataPosInt;
while(pos > 0)
{
ALbufferlistitem *prev;
if((prev=tmpiter->prev) != NULL)
tmpiter = prev;
else if(Looping)
{
while(tmpiter->next)
tmpiter = tmpiter->next;
}
else
{
ALuint DataSize = minu(SrcBufferSize - SrcDataSize, pos);
SilenceSamples(&SrcData[SrcDataSize], DataSize);
SrcDataSize += DataSize;
pos = 0;
break;
}
if(tmpiter->buffer)
{
if((ALuint)tmpiter->buffer->SampleLen > pos)
{
pos = tmpiter->buffer->SampleLen - pos;
break;
}
pos -= tmpiter->buffer->SampleLen;
}
}
}
while(tmpiter && SrcBufferSize > SrcDataSize)
{
const ALbuffer *ALBuffer;
if((ALBuffer=tmpiter->buffer) != NULL)
{
const ALubyte *Data = ALBuffer->data;
ALuint DataSize = ALBuffer->SampleLen;
/* Skip the data already played */
if(DataSize <= pos)
pos -= DataSize;
else
{
Data += (pos*NumChannels + chan)*SampleSize;
DataSize -= pos;
pos -= pos;
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
LoadSamples(&SrcData[SrcDataSize], Data, NumChannels,
ALBuffer->FmtType, DataSize);
SrcDataSize += DataSize;
}
}
tmpiter = tmpiter->next;
if(!tmpiter && Looping)
tmpiter = ATOMIC_LOAD(&Source->queue);
else if(!tmpiter)
{
SilenceSamples(&SrcData[SrcDataSize], SrcBufferSize - SrcDataSize);
SrcDataSize += SrcBufferSize - SrcDataSize;
}
}
}
/* Now resample, then filter and mix to the appropriate outputs. */
ResampledData = Resample(
&SrcData[BufferPrePadding], DataPosFrac, increment,
Device->ResampledData, DstBufferSize
);
{
DirectParams *parms = &src->Direct;
const ALfloat *samples;
samples = DoFilters(
&parms->Filters[chan].LowPass, &parms->Filters[chan].HighPass,
Device->FilteredData, ResampledData, DstBufferSize,
parms->Filters[chan].ActiveType
);
if(!src->IsHrtf)
Mix(samples, MaxChannels, parms->OutBuffer, parms->Mix.Gains[chan],
parms->Counter, OutPos, DstBufferSize);
else
HrtfMix(parms->OutBuffer, samples, parms->Counter, src->Offset,
OutPos, parms->Mix.Hrtf.IrSize, &parms->Mix.Hrtf.Params[chan],
&parms->Mix.Hrtf.State[chan], DstBufferSize);
}
for(j = 0;j < Device->NumAuxSends;j++)
{
SendParams *parms = &src->Send[j];
const ALfloat *samples;
if(!parms->OutBuffer)
continue;
samples = DoFilters(
&parms->Filters[chan].LowPass, &parms->Filters[chan].HighPass,
Device->FilteredData, ResampledData, DstBufferSize,
parms->Filters[chan].ActiveType
);
Mix(samples, 1, parms->OutBuffer, &parms->Gain,
parms->Counter, OutPos, DstBufferSize);
}
}
/* Update positions */
DataPosFrac += increment*DstBufferSize;
DataPosInt += DataPosFrac>>FRACTIONBITS;
DataPosFrac &= FRACTIONMASK;
OutPos += DstBufferSize;
src->Offset += DstBufferSize;
src->Direct.Counter = maxu(src->Direct.Counter, DstBufferSize) - DstBufferSize;
for(j = 0;j < Device->NumAuxSends;j++)
src->Send[j].Counter = maxu(src->Send[j].Counter, DstBufferSize) - DstBufferSize;
/* Handle looping sources */
while(1)
{
const ALbuffer *ALBuffer;
ALuint DataSize = 0;
ALuint LoopStart = 0;
ALuint LoopEnd = 0;
if((ALBuffer=BufferListItem->buffer) != NULL)
{
DataSize = ALBuffer->SampleLen;
LoopStart = ALBuffer->LoopStart;
LoopEnd = ALBuffer->LoopEnd;
if(LoopEnd > DataPosInt)
break;
}
if(Looping && Source->SourceType == AL_STATIC)
{
assert(LoopEnd > LoopStart);
DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart;
break;
}
if(DataSize > DataPosInt)
break;
if(!(BufferListItem=BufferListItem->next))
{
if(Looping)
BufferListItem = ATOMIC_LOAD(&Source->queue);
else
{
State = AL_STOPPED;
BufferListItem = NULL;
DataPosInt = 0;
DataPosFrac = 0;
break;
}
}
DataPosInt -= DataSize;
}
} while(State == AL_PLAYING && OutPos < SamplesToDo);
/* Update source info */
Source->state = State;
ATOMIC_STORE(&Source->current_buffer, BufferListItem);
Source->position = DataPosInt;
Source->position_fraction = DataPosFrac;
}
+119
View File
@@ -0,0 +1,119 @@
#ifndef MIXER_DEFS_H
#define MIXER_DEFS_H
#include "AL/alc.h"
#include "AL/al.h"
#include "alMain.h"
#include "alu.h"
struct MixGains;
struct MixHrtfParams;
struct HrtfState;
/* C resamplers */
const ALfloat *Resample_copy_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
const ALfloat *Resample_point_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
const ALfloat *Resample_lerp_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
const ALfloat *Resample_cubic_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
const ALfloat *Resample_bsinc_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
/* C mixers */
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 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(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALsizei *restrict pos_arr, ALsizei size)
{
ALsizei i;
pos_arr[0] = 0;
frac_arr[0] = frac;
for(i = 1;i < size;i++)
{
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_lerp_SSE2(const InterpState *state, const ALfloat *restrict src,
ALsizei frac, ALint increment, ALfloat *restrict dst,
ALsizei numsamples);
const ALfloat *Resample_lerp_SSE41(const InterpState *state, const ALfloat *restrict src,
ALsizei frac, ALint increment, ALfloat *restrict dst,
ALsizei numsamples);
const ALfloat *Resample_bsinc_SSE(const InterpState *state, const ALfloat *restrict src,
ALsizei frac, ALint increment, ALfloat *restrict dst,
ALsizei dstlen);
/* Neon mixers */
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_lerp_Neon(const InterpState *state, const ALfloat *restrict src,
ALsizei frac, ALint increment, ALfloat *restrict dst,
ALsizei numsamples);
const ALfloat *Resample_bsinc_Neon(const InterpState *state, const ALfloat *restrict src,
ALsizei frac, ALint increment, ALfloat *restrict dst,
ALsizei dstlen);
#endif /* MIXER_DEFS_H */
+128
View File
@@ -0,0 +1,128 @@
#include "config.h"
#include "alMain.h"
#include "alSource.h"
#include "hrtf.h"
#include "align.h"
#include "alu.h"
#include "defs.h"
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 LeftOut, ALfloat *restrict RightOut,
const ALfloat *data, ALsizei Offset, ALsizei OutPos,
const ALsizei IrSize, MixHrtfParams *hrtfparams, HrtfState *hrtfstate,
ALsizei BufferSize)
{
const ALfloat (*Coeffs)[2] = ASSUME_ALIGNED(hrtfparams->Coeffs, 16);
const ALsizei Delay[2] = { hrtfparams->Delay[0], hrtfparams->Delay[1] };
const ALfloat gainstep = hrtfparams->GainStep;
const ALfloat gain = hrtfparams->Gain;
ALfloat g, stepcount = 0.0f;
ALfloat left, right;
ALsizei i;
ASSUME(IrSize >= 4);
ASSUME(BufferSize > 0);
LeftOut += OutPos;
RightOut += OutPos;
for(i = 0;i < BufferSize;i++)
{
hrtfstate->History[Offset&HRTF_HISTORY_MASK] = *(data++);
g = gain + gainstep*stepcount;
left = hrtfstate->History[(Offset-Delay[0])&HRTF_HISTORY_MASK]*g;
right = hrtfstate->History[(Offset-Delay[1])&HRTF_HISTORY_MASK]*g;
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);
*(LeftOut++) += hrtfstate->Values[Offset&HRIR_MASK][0];
*(RightOut++) += hrtfstate->Values[Offset&HRIR_MASK][1];
stepcount += 1.0f;
Offset++;
}
hrtfparams->Gain = gain + gainstep*stepcount;
}
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] };
const ALfloat oldGain = oldparams->Gain;
const ALfloat oldGainStep = -oldGain / (ALfloat)BufferSize;
const ALfloat (*NewCoeffs)[2] = ASSUME_ALIGNED(newparams->Coeffs, 16);
const ALsizei NewDelay[2] = { newparams->Delay[0], newparams->Delay[1] };
const ALfloat newGain = newparams->Gain;
const ALfloat newGainStep = newparams->GainStep;
ALfloat g, stepcount = 0.0f;
ALfloat left, right;
ALsizei i;
ASSUME(IrSize >= 4);
ASSUME(BufferSize > 0);
LeftOut += OutPos;
RightOut += OutPos;
for(i = 0;i < BufferSize;i++)
{
hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][0] = 0.0f;
hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][1] = 0.0f;
hrtfstate->History[Offset&HRTF_HISTORY_MASK] = *(data++);
g = oldGain + oldGainStep*stepcount;
left = hrtfstate->History[(Offset-OldDelay[0])&HRTF_HISTORY_MASK]*g;
right = hrtfstate->History[(Offset-OldDelay[1])&HRTF_HISTORY_MASK]*g;
ApplyCoeffs(Offset, hrtfstate->Values, IrSize, OldCoeffs, left, right);
g = newGain + newGainStep*stepcount;
left = hrtfstate->History[(Offset-NewDelay[0])&HRTF_HISTORY_MASK]*g;
right = hrtfstate->History[(Offset-NewDelay[1])&HRTF_HISTORY_MASK]*g;
ApplyCoeffs(Offset, hrtfstate->Values, IrSize, NewCoeffs, left, right);
*(LeftOut++) += hrtfstate->Values[Offset&HRIR_MASK][0];
*(RightOut++) += hrtfstate->Values[Offset&HRIR_MASK][1];
stepcount += 1.0f;
Offset++;
}
newparams->Gain = newGain + newGainStep*stepcount;
}
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;
ASSUME(IrSize >= 4);
ASSUME(BufferSize > 0);
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];
}
}
+169
View File
@@ -0,0 +1,169 @@
#include "config.h"
#include <assert.h>
#include "alMain.h"
#include "alu.h"
#include "alSource.h"
#include "alAuxEffectSlot.h"
#include "defs.h"
static inline ALfloat do_point(const InterpState* UNUSED(state), const ALfloat *restrict vals, ALsizei UNUSED(frac))
{ return vals[0]; }
static inline ALfloat do_lerp(const InterpState* UNUSED(state), const ALfloat *restrict vals, ALsizei frac)
{ return lerp(vals[0], vals[1], frac * (1.0f/FRACTIONONE)); }
static inline ALfloat do_cubic(const InterpState* UNUSED(state), const ALfloat *restrict vals, ALsizei frac)
{ return cubic(vals[0], vals[1], vals[2], vals[3], frac * (1.0f/FRACTIONONE)); }
static inline ALfloat do_bsinc(const InterpState *state, const ALfloat *restrict vals, ALsizei frac)
{
const ALfloat *fil, *scd, *phd, *spd;
ALsizei j_f, pi;
ALfloat pf, r;
ASSUME(state->bsinc.m > 0);
// 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.filter + state->bsinc.m*pi*4, 16);
scd = ASSUME_ALIGNED(fil + state->bsinc.m, 16);
phd = ASSUME_ALIGNED(scd + state->bsinc.m, 16);
spd = ASSUME_ALIGNED(phd + state->bsinc.m, 16);
// Apply the scale and phase interpolated filter.
r = 0.0f;
for(j_f = 0;j_f < state->bsinc.m;j_f++)
r += (fil[j_f] + state->bsinc.sf*scd[j_f] + pf*(phd[j_f] + state->bsinc.sf*spd[j_f])) * vals[j_f];
return r;
}
const ALfloat *Resample_copy_C(const InterpState* UNUSED(state),
const ALfloat *restrict src, ALsizei UNUSED(frac), ALint UNUSED(increment),
ALfloat *restrict dst, ALsizei numsamples)
{
#if defined(HAVE_SSE) || defined(HAVE_NEON)
/* Avoid copying the source data if it's aligned like the destination. */
if((((intptr_t)src)&15) == (((intptr_t)dst)&15))
return src;
#endif
memcpy(dst, src, numsamples*sizeof(ALfloat));
return dst;
}
#define DECL_TEMPLATE(Tag, Sampler, O) \
const ALfloat *Resample_##Tag##_C(const InterpState *state, \
const ALfloat *restrict src, ALsizei frac, ALint increment, \
ALfloat *restrict dst, ALsizei numsamples) \
{ \
const InterpState istate = *state; \
ALsizei i; \
\
ASSUME(numsamples > 0); \
\
src -= O; \
for(i = 0;i < numsamples;i++) \
{ \
dst[i] = Sampler(&istate, src, frac); \
\
frac += increment; \
src += frac>>FRACTIONBITS; \
frac &= FRACTIONMASK; \
} \
return dst; \
}
DECL_TEMPLATE(point, do_point, 0)
DECL_TEMPLATE(lerp, do_lerp, 0)
DECL_TEMPLATE(cubic, do_cubic, 1)
DECL_TEMPLATE(bsinc, do_bsinc, istate.bsinc.l)
#undef DECL_TEMPLATE
static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
const ALsizei IrSize,
const ALfloat (*restrict Coeffs)[2],
ALfloat left, ALfloat right)
{
ALsizei c;
for(c = 0;c < IrSize;c++)
{
const ALsizei off = (Offset+c)&HRIR_MASK;
Values[off][0] += Coeffs[c][0] * left;
Values[off][1] += Coeffs[c][1] * right;
}
}
#define MixHrtf MixHrtf_C
#define MixHrtfBlend MixHrtfBlend_C
#define MixDirectHrtf MixDirectHrtf_C
#include "hrtf_inc.c"
void Mix_C(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
ALsizei BufferSize)
{
const ALfloat delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
ALsizei c;
ASSUME(OutChans > 0);
ASSUME(BufferSize > 0);
for(c = 0;c < OutChans;c++)
{
ALsizei pos = 0;
ALfloat gain = CurrentGains[c];
const ALfloat diff = TargetGains[c] - gain;
if(fabsf(diff) > FLT_EPSILON)
{
ALsizei minsize = mini(BufferSize, Counter);
const ALfloat step = diff * delta;
ALfloat step_count = 0.0f;
for(;pos < minsize;pos++)
{
OutBuffer[c][OutPos+pos] += data[pos] * (gain + step*step_count);
step_count += 1.0f;
}
if(pos == Counter)
gain = TargetGains[c];
else
gain += step*step_count;
CurrentGains[c] = gain;
}
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;
ASSUME(InChans > 0);
ASSUME(BufferSize > 0);
for(c = 0;c < InChans;c++)
{
const ALfloat gain = Gains[c];
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
continue;
for(i = 0;i < BufferSize;i++)
OutBuffer[i] += data[c][InPos+i] * gain;
}
}
+283
View File
@@ -0,0 +1,283 @@
#include "config.h"
#include <arm_neon.h>
#include "AL/al.h"
#include "AL/alc.h"
#include "alMain.h"
#include "alu.h"
#include "hrtf.h"
#include "defs.h"
const ALfloat *Resample_lerp_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 float32x4_t fracOne4 = vdupq_n_f32(1.0f/FRACTIONONE);
const int32x4_t fracMask4 = vdupq_n_s32(FRACTIONMASK);
alignas(16) ALsizei pos_[4], frac_[4];
int32x4_t pos4, frac4;
ALsizei todo, pos, i;
ASSUME(numsamples > 0);
InitiatePositionArrays(frac, increment, frac_, pos_, 4);
frac4 = vld1q_s32(frac_);
pos4 = vld1q_s32(pos_);
todo = numsamples & ~3;
for(i = 0;i < todo;i += 4)
{
const int pos0 = vgetq_lane_s32(pos4, 0);
const int pos1 = vgetq_lane_s32(pos4, 1);
const int pos2 = vgetq_lane_s32(pos4, 2);
const int pos3 = vgetq_lane_s32(pos4, 3);
const float32x4_t val1 = (float32x4_t){src[pos0], src[pos1], src[pos2], src[pos3]};
const float32x4_t val2 = (float32x4_t){src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+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);
}
/* NOTE: These four elements represent the position *after* the last four
* samples, so the lowest element is the next position to resample.
*/
pos = vgetq_lane_s32(pos4, 0);
frac = vgetq_lane_s32(frac4, 0);
for(;i < numsamples;++i)
{
dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
frac += increment;
pos += frac>>FRACTIONBITS;
frac &= FRACTIONMASK;
}
return dst;
}
const ALfloat *Resample_bsinc_Neon(const InterpState *state,
const ALfloat *restrict src, ALsizei frac, ALint increment,
ALfloat *restrict dst, ALsizei dstlen)
{
const ALfloat *const filter = state->bsinc.filter;
const float32x4_t sf4 = vdupq_n_f32(state->bsinc.sf);
const ALsizei m = state->bsinc.m;
const float32x4_t *fil, *scd, *phd, *spd;
ALsizei pi, i, j, offset;
float32x4_t r4;
ALfloat pf;
ASSUME(m > 0);
ASSUME(dstlen > 0);
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
offset = m*pi*4;
fil = ASSUME_ALIGNED(filter + offset, 16); offset += m;
scd = ASSUME_ALIGNED(filter + offset, 16); offset += m;
phd = ASSUME_ALIGNED(filter + offset, 16); offset += m;
spd = ASSUME_ALIGNED(filter + offset, 16);
// Apply the scale and phase interpolated filter.
r4 = vdupq_n_f32(0.0f);
{
const ALsizei count = m >> 2;
const float32x4_t pf4 = vdupq_n_f32(pf);
ASSUME(count > 0);
for(j = 0;j < count;j++)
{
/* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */
const float32x4_t f4 = vmlaq_f32(
vmlaq_f32(fil[j], sf4, scd[j]),
pf4, vmlaq_f32(phd[j], sf4, spd[j])
);
/* r += f*src */
r4 = vmlaq_f32(r4, f4, vld1q_f32(&src[j*4]));
}
}
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)
{
ALsizei 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);
}
Values = ASSUME_ALIGNED(Values, 16);
Coeffs = ASSUME_ALIGNED(Coeffs, 16);
for(c = 0;c < IrSize;c += 2)
{
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]);
vals = vmlaq_f32(vals, coefs, leftright4);
vst1_f32((float32_t*)&Values[o0][0], vget_low_f32(vals));
vst1_f32((float32_t*)&Values[o1][0], vget_high_f32(vals));
}
}
#define MixHrtf MixHrtf_Neon
#define MixHrtfBlend MixHrtfBlend_Neon
#define MixDirectHrtf MixDirectHrtf_Neon
#include "hrtf_inc.c"
void Mix_Neon(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
ALsizei BufferSize)
{
const ALfloat delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
ALsizei c;
ASSUME(OutChans > 0);
ASSUME(BufferSize > 0);
data = ASSUME_ALIGNED(data, 16);
OutBuffer = ASSUME_ALIGNED(OutBuffer, 16);
for(c = 0;c < OutChans;c++)
{
ALsizei pos = 0;
ALfloat gain = CurrentGains[c];
const ALfloat diff = TargetGains[c] - gain;
if(fabsf(diff) > FLT_EPSILON)
{
ALsizei minsize = mini(BufferSize, Counter);
const ALfloat step = diff * delta;
ALfloat step_count = 0.0f;
/* Mix with applying gain steps in aligned multiples of 4. */
if(LIKELY(minsize > 3))
{
const float32x4_t four4 = vdupq_n_f32(4.0f);
const float32x4_t step4 = vdupq_n_f32(step);
const float32x4_t gain4 = vdupq_n_f32(gain);
float32x4_t step_count4 = vsetq_lane_f32(0.0f,
vsetq_lane_f32(1.0f,
vsetq_lane_f32(2.0f,
vsetq_lane_f32(3.0f, vdupq_n_f32(0.0f), 3),
2), 1), 0
);
ALsizei todo = minsize >> 2;
do {
const float32x4_t val4 = vld1q_f32(&data[pos]);
float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]);
dry4 = vmlaq_f32(dry4, val4, vmlaq_f32(gain4, step4, step_count4));
step_count4 = vaddq_f32(step_count4, four4);
vst1q_f32(&OutBuffer[c][OutPos+pos], dry4);
pos += 4;
} while(--todo);
/* NOTE: step_count4 now represents the next four counts after
* the last four mixed samples, so the lowest element
* represents the next step count to apply.
*/
step_count = vgetq_lane_f32(step_count4, 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 + step*step_count);
step_count += 1.0f;
}
if(pos == Counter)
gain = TargetGains[c];
else
gain += step*step_count;
CurrentGains[c] = gain;
/* Mix until pos is aligned with 4 or the mix is done. */
minsize = mini(BufferSize, (pos+3)&~3);
for(;pos < minsize;pos++)
OutBuffer[c][OutPos+pos] += data[pos]*gain;
}
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
continue;
if(LIKELY(BufferSize-pos > 3))
{
ALsizei todo = (BufferSize-pos) >> 2;
const float32x4_t gain4 = vdupq_n_f32(gain);
do {
const float32x4_t val4 = vld1q_f32(&data[pos]);
float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]);
dry4 = vmlaq_f32(dry4, val4, gain4);
vst1q_f32(&OutBuffer[c][OutPos+pos], dry4);
pos += 4;
} while(--todo);
}
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)
{
ALsizei c;
ASSUME(InChans > 0);
ASSUME(BufferSize > 0);
for(c = 0;c < InChans;c++)
{
ALsizei pos = 0;
const ALfloat gain = Gains[c];
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
continue;
if(LIKELY(BufferSize > 3))
{
ALsizei todo = BufferSize >> 2;
float32x4_t gain4 = vdupq_n_f32(gain);
do {
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);
pos += 4;
} while(--todo);
}
for(;pos < BufferSize;pos++)
OutBuffer[pos] += data[c][InPos+pos]*gain;
}
}
+250
View File
@@ -0,0 +1,250 @@
#include "config.h"
#include <xmmintrin.h>
#include "AL/al.h"
#include "AL/alc.h"
#include "alMain.h"
#include "alu.h"
#include "alSource.h"
#include "alAuxEffectSlot.h"
#include "defs.h"
const ALfloat *Resample_bsinc_SSE(const InterpState *state, const ALfloat *restrict src,
ALsizei frac, ALint increment, ALfloat *restrict dst,
ALsizei dstlen)
{
const ALfloat *const filter = state->bsinc.filter;
const __m128 sf4 = _mm_set1_ps(state->bsinc.sf);
const ALsizei m = state->bsinc.m;
const __m128 *fil, *scd, *phd, *spd;
ALsizei pi, i, j, offset;
ALfloat pf;
__m128 r4;
ASSUME(m > 0);
ASSUME(dstlen > 0);
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
offset = m*pi*4;
fil = (const __m128*)ASSUME_ALIGNED(filter + offset, 16); offset += m;
scd = (const __m128*)ASSUME_ALIGNED(filter + offset, 16); offset += m;
phd = (const __m128*)ASSUME_ALIGNED(filter + offset, 16); offset += m;
spd = (const __m128*)ASSUME_ALIGNED(filter + offset, 16);
// Apply the scale and phase interpolated filter.
r4 = _mm_setzero_ps();
{
const ALsizei count = m >> 2;
const __m128 pf4 = _mm_set1_ps(pf);
ASSUME(count > 0);
#define MLA4(x, y, z) _mm_add_ps(x, _mm_mul_ps(y, z))
for(j = 0;j < count;j++)
{
/* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */
const __m128 f4 = MLA4(
MLA4(fil[j], sf4, scd[j]),
pf4, MLA4(phd[j], sf4, spd[j])
);
/* r += f*src */
r4 = MLA4(r4, f4, _mm_loadu_ps(&src[j*4]));
}
#undef MLA4
}
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);
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)
{
const __m128 lrlr = _mm_setr_ps(left, right, left, right);
__m128 vals = _mm_setzero_ps();
__m128 coeffs;
ALsizei i;
Values = ASSUME_ALIGNED(Values, 16);
Coeffs = ASSUME_ALIGNED(Coeffs, 16);
if((Offset&1))
{
const ALsizei o0 = Offset&HRIR_MASK;
const ALsizei o1 = (Offset+IrSize-1)&HRIR_MASK;
__m128 imp0, imp1;
coeffs = _mm_load_ps(&Coeffs[0][0]);
vals = _mm_loadl_pi(vals, (__m64*)&Values[o0][0]);
imp0 = _mm_mul_ps(lrlr, coeffs);
vals = _mm_add_ps(imp0, vals);
_mm_storel_pi((__m64*)&Values[o0][0], vals);
for(i = 1;i < IrSize-1;i += 2)
{
const ALsizei o2 = (Offset+i)&HRIR_MASK;
coeffs = _mm_load_ps(&Coeffs[i+1][0]);
vals = _mm_load_ps(&Values[o2][0]);
imp1 = _mm_mul_ps(lrlr, coeffs);
imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2));
vals = _mm_add_ps(imp0, vals);
_mm_store_ps(&Values[o2][0], vals);
imp0 = imp1;
}
vals = _mm_loadl_pi(vals, (__m64*)&Values[o1][0]);
imp0 = _mm_movehl_ps(imp0, imp0);
vals = _mm_add_ps(imp0, vals);
_mm_storel_pi((__m64*)&Values[o1][0], vals);
}
else
{
for(i = 0;i < IrSize;i += 2)
{
const ALsizei o = (Offset + i)&HRIR_MASK;
coeffs = _mm_load_ps(&Coeffs[i][0]);
vals = _mm_load_ps(&Values[o][0]);
vals = _mm_add_ps(vals, _mm_mul_ps(lrlr, coeffs));
_mm_store_ps(&Values[o][0], vals);
}
}
}
#define MixHrtf MixHrtf_SSE
#define MixHrtfBlend MixHrtfBlend_SSE
#define MixDirectHrtf MixDirectHrtf_SSE
#include "hrtf_inc.c"
void Mix_SSE(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
ALsizei BufferSize)
{
const ALfloat delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
ALsizei c;
ASSUME(OutChans > 0);
ASSUME(BufferSize > 0);
for(c = 0;c < OutChans;c++)
{
ALsizei pos = 0;
ALfloat gain = CurrentGains[c];
const ALfloat diff = TargetGains[c] - gain;
if(fabsf(diff) > FLT_EPSILON)
{
ALsizei minsize = mini(BufferSize, Counter);
const ALfloat step = diff * delta;
ALfloat step_count = 0.0f;
/* Mix with applying gain steps in aligned multiples of 4. */
if(LIKELY(minsize > 3))
{
const __m128 four4 = _mm_set1_ps(4.0f);
const __m128 step4 = _mm_set1_ps(step);
const __m128 gain4 = _mm_set1_ps(gain);
__m128 step_count4 = _mm_setr_ps(0.0f, 1.0f, 2.0f, 3.0f);
ALsizei todo = minsize >> 2;
do {
const __m128 val4 = _mm_load_ps(&data[pos]);
__m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]);
#define MLA4(x, y, z) _mm_add_ps(x, _mm_mul_ps(y, z))
/* dry += val * (gain + step*step_count) */
dry4 = MLA4(dry4, val4, MLA4(gain4, step4, step_count4));
#undef MLA4
_mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
step_count4 = _mm_add_ps(step_count4, four4);
pos += 4;
} while(--todo);
/* NOTE: step_count4 now represents the next four counts after
* the last four mixed samples, so the lowest element
* represents the next step count to apply.
*/
step_count = _mm_cvtss_f32(step_count4);
}
/* 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 + step*step_count);
step_count += 1.0f;
}
if(pos == Counter)
gain = TargetGains[c];
else
gain += step*step_count;
CurrentGains[c] = gain;
/* Mix until pos is aligned with 4 or the mix is done. */
minsize = mini(BufferSize, (pos+3)&~3);
for(;pos < minsize;pos++)
OutBuffer[c][OutPos+pos] += data[pos]*gain;
}
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
continue;
if(LIKELY(BufferSize-pos > 3))
{
ALsizei todo = (BufferSize-pos) >> 2;
const __m128 gain4 = _mm_set1_ps(gain);
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));
_mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
pos += 4;
} while(--todo);
}
for(;pos < BufferSize;pos++)
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)
{
ALsizei c;
ASSUME(InChans > 0);
ASSUME(BufferSize > 0);
for(c = 0;c < InChans;c++)
{
ALsizei pos = 0;
const ALfloat gain = Gains[c];
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
continue;
if(LIKELY(BufferSize > 3))
{
ALsizei todo = BufferSize >> 2;
const __m128 gain4 = _mm_set1_ps(gain);
do {
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);
pos += 4;
} while(--todo);
}
for(;pos < BufferSize;pos++)
OutBuffer[pos] += data[c][InPos+pos]*gain;
}
}
+25 -19
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,30 +24,35 @@
#include <emmintrin.h>
#include "alu.h"
#include "mixer_defs.h"
#include "defs.h"
const ALfloat *Resample_lerp32_SSE2(const ALfloat *src, ALuint frac, ALuint increment,
ALfloat *restrict dst, ALuint numsamples)
const ALfloat *Resample_lerp_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_;
alignas(16) ALsizei pos_[4], frac_[4];
__m128i frac4, pos4;
ALuint pos;
ALuint i;
ALsizei todo, pos, i;
InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
ASSUME(numsamples > 0);
frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
InitiatePositionArrays(frac, increment, frac_, pos_, 4);
frac4 = _mm_setr_epi32(frac_[0], frac_[1], frac_[2], frac_[3]);
pos4 = _mm_setr_epi32(pos_[0], pos_[1], pos_[2], pos_[3]);
for(i = 0;numsamples-i > 3;i += 4)
todo = numsamples & ~3;
for(i = 0;i < todo;i += 4)
{
const __m128 val1 = _mm_setr_ps(src[pos_.i[0]], src[pos_.i[1]], src[pos_.i[2]], src[pos_.i[3]]);
const __m128 val2 = _mm_setr_ps(src[pos_.i[0]+1], src[pos_.i[1]+1], src[pos_.i[2]+1], src[pos_.i[3]+1]);
const int pos0 = _mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(0, 0, 0, 0)));
const int pos1 = _mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(1, 1, 1, 1)));
const int pos2 = _mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(2, 2, 2, 2)));
const int pos3 = _mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(3, 3, 3, 3)));
const __m128 val1 = _mm_setr_ps(src[pos0 ], src[pos1 ], src[pos2 ], src[pos3 ]);
const __m128 val2 = _mm_setr_ps(src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1]);
/* val1 + (val2-val1)*mu */
const __m128 r0 = _mm_sub_ps(val2, val1);
@@ -59,14 +64,15 @@ const ALfloat *Resample_lerp32_SSE2(const ALfloat *src, ALuint frac, ALuint incr
frac4 = _mm_add_epi32(frac4, increment4);
pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
frac4 = _mm_and_si128(frac4, fracMask4);
_mm_store_ps(pos_.f, _mm_castsi128_ps(pos4));
}
pos = pos_.i[0];
/* NOTE: These four elements represent the position *after* the last four
* samples, so the lowest element is the next position to resample.
*/
pos = _mm_cvtsi128_si32(pos4);
frac = _mm_cvtsi128_si32(frac4);
for(;i < numsamples;i++)
for(;i < numsamples;++i)
{
dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
View File
+25 -22
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,30 +25,35 @@
#include <smmintrin.h>
#include "alu.h"
#include "mixer_defs.h"
#include "defs.h"
const ALfloat *Resample_lerp32_SSE41(const ALfloat *src, ALuint frac, ALuint increment,
ALfloat *restrict dst, ALuint numsamples)
const ALfloat *Resample_lerp_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_;
alignas(16) ALsizei pos_[4], frac_[4];
__m128i frac4, pos4;
ALuint pos;
ALuint i;
ALsizei todo, pos, i;
InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
ASSUME(numsamples > 0);
frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
InitiatePositionArrays(frac, increment, frac_, pos_, 4);
frac4 = _mm_setr_epi32(frac_[0], frac_[1], frac_[2], frac_[3]);
pos4 = _mm_setr_epi32(pos_[0], pos_[1], pos_[2], pos_[3]);
for(i = 0;numsamples-i > 3;i += 4)
todo = numsamples & ~3;
for(i = 0;i < todo;i += 4)
{
const __m128 val1 = _mm_setr_ps(src[pos_.i[0]], src[pos_.i[1]], src[pos_.i[2]], src[pos_.i[3]]);
const __m128 val2 = _mm_setr_ps(src[pos_.i[0]+1], src[pos_.i[1]+1], src[pos_.i[2]+1], src[pos_.i[3]+1]);
const int pos0 = _mm_extract_epi32(pos4, 0);
const int pos1 = _mm_extract_epi32(pos4, 1);
const int pos2 = _mm_extract_epi32(pos4, 2);
const int pos3 = _mm_extract_epi32(pos4, 3);
const __m128 val1 = _mm_setr_ps(src[pos0 ], src[pos1 ], src[pos2 ], src[pos3 ]);
const __m128 val2 = _mm_setr_ps(src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1]);
/* val1 + (val2-val1)*mu */
const __m128 r0 = _mm_sub_ps(val2, val1);
@@ -60,17 +65,15 @@ const ALfloat *Resample_lerp32_SSE41(const ALfloat *src, ALuint frac, ALuint inc
frac4 = _mm_add_epi32(frac4, increment4);
pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
frac4 = _mm_and_si128(frac4, fracMask4);
pos_.i[0] = _mm_extract_epi32(pos4, 0);
pos_.i[1] = _mm_extract_epi32(pos4, 1);
pos_.i[2] = _mm_extract_epi32(pos4, 2);
pos_.i[3] = _mm_extract_epi32(pos4, 3);
}
pos = pos_.i[0];
/* NOTE: These four elements represent the position *after* the last four
* samples, so the lowest element is the next position to resample.
*/
pos = _mm_cvtsi128_si32(pos4);
frac = _mm_cvtsi128_si32(frac4);
for(;i < numsamples;i++)
for(;i < numsamples;++i)
{
dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
-126
View File
@@ -1,126 +0,0 @@
#include "config.h"
#include <assert.h>
#include "alMain.h"
#include "alu.h"
#include "alSource.h"
#include "alAuxEffectSlot.h"
static inline ALfloat point32(const ALfloat *vals, ALuint UNUSED(frac))
{ return vals[0]; }
static inline ALfloat lerp32(const ALfloat *vals, ALuint frac)
{ return lerp(vals[0], vals[1], frac * (1.0f/FRACTIONONE)); }
static inline ALfloat cubic32(const ALfloat *vals, ALuint frac)
{ return cubic(vals[-1], vals[0], vals[1], vals[2], frac * (1.0f/FRACTIONONE)); }
const ALfloat *Resample_copy32_C(const ALfloat *src, ALuint UNUSED(frac),
ALuint increment, ALfloat *restrict dst, ALuint numsamples)
{
assert(increment==FRACTIONONE);
#if defined(HAVE_SSE) || defined(HAVE_NEON)
/* Avoid copying the source data if it's aligned like the destination. */
if((((intptr_t)src)&15) == (((intptr_t)dst)&15))
return src;
#endif
memcpy(dst, src, numsamples*sizeof(ALfloat));
return dst;
}
#define DECL_TEMPLATE(Sampler) \
const ALfloat *Resample_##Sampler##_C(const ALfloat *src, ALuint frac, \
ALuint increment, ALfloat *restrict dst, ALuint numsamples) \
{ \
ALuint i; \
for(i = 0;i < numsamples;i++) \
{ \
dst[i] = Sampler(src, frac); \
\
frac += increment; \
src += frac>>FRACTIONBITS; \
frac &= FRACTIONMASK; \
} \
return dst; \
}
DECL_TEMPLATE(point32)
DECL_TEMPLATE(lerp32)
DECL_TEMPLATE(cubic32)
#undef DECL_TEMPLATE
void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples)
{
ALuint i;
for(i = 0;i < numsamples;i++)
*(dst++) = ALfilterState_processSingle(filter, *(src++));
}
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint IrSize,
ALfloat (*restrict Coeffs)[2],
const ALfloat (*restrict CoeffStep)[2],
ALfloat left, ALfloat right)
{
ALuint c;
for(c = 0;c < IrSize;c++)
{
const ALuint off = (Offset+c)&HRIR_MASK;
Values[off][0] += Coeffs[c][0] * left;
Values[off][1] += Coeffs[c][1] * right;
Coeffs[c][0] += CoeffStep[c][0];
Coeffs[c][1] += CoeffStep[c][1];
}
}
static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint IrSize,
ALfloat (*restrict Coeffs)[2],
ALfloat left, ALfloat right)
{
ALuint c;
for(c = 0;c < IrSize;c++)
{
const ALuint off = (Offset+c)&HRIR_MASK;
Values[off][0] += Coeffs[c][0] * left;
Values[off][1] += Coeffs[c][1] * right;
}
}
#define SUFFIX C
#include "mixer_inc.c"
#undef SUFFIX
void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize)
{
ALfloat gain, step;
ALuint c;
for(c = 0;c < OutChans;c++)
{
ALuint pos = 0;
gain = Gains[c].Current;
step = Gains[c].Step;
if(step != 1.0f && Counter > 0)
{
for(;pos < BufferSize && pos < Counter;pos++)
{
OutBuffer[c][OutPos+pos] += data[pos]*gain;
gain *= step;
}
if(pos == Counter)
gain = Gains[c].Target;
Gains[c].Current = gain;
}
if(!(gain > GAIN_SILENCE_THRESHOLD))
continue;
for(;pos < BufferSize;pos++)
OutBuffer[c][OutPos+pos] += data[pos]*gain;
}
}
-65
View File
@@ -1,65 +0,0 @@
#ifndef MIXER_DEFS_H
#define MIXER_DEFS_H
#include "AL/alc.h"
#include "AL/al.h"
#include "alMain.h"
#include "alu.h"
struct MixGains;
struct HrtfParams;
struct HrtfState;
/* C resamplers */
const ALfloat *Resample_copy32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
const ALfloat *Resample_point32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
const ALfloat *Resample_lerp32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
const ALfloat *Resample_cubic32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
/* C mixers */
void MixHrtf_C(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize,
const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate,
ALuint BufferSize);
void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize);
/* SSE mixers */
void MixHrtf_SSE(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize,
const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate,
ALuint BufferSize);
void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize);
/* SSE resamplers */
inline void InitiatePositionArrays(ALuint frac, ALuint increment, ALuint *frac_arr, ALuint *pos_arr, ALuint size)
{
ALuint i;
pos_arr[0] = 0;
frac_arr[0] = frac;
for(i = 1;i < size;i++)
{
ALuint frac_tmp = frac_arr[i-1] + increment;
pos_arr[i] = pos_arr[i-1] + (frac_tmp>>FRACTIONBITS);
frac_arr[i] = frac_tmp&FRACTIONMASK;
}
}
const ALfloat *Resample_lerp32_SSE2(const ALfloat *src, ALuint frac, ALuint increment,
ALfloat *restrict dst, ALuint numsamples);
const ALfloat *Resample_lerp32_SSE41(const ALfloat *src, ALuint frac, ALuint increment,
ALfloat *restrict dst, ALuint numsamples);
/* Neon mixers */
void MixHrtf_Neon(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize,
const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate,
ALuint BufferSize);
void Mix_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize);
#endif /* MIXER_DEFS_H */
-93
View File
@@ -1,93 +0,0 @@
#include "config.h"
#include "alMain.h"
#include "alSource.h"
#include "hrtf.h"
#include "mixer_defs.h"
#include "align.h"
#define REAL_MERGE(a,b) a##b
#define MERGE(a,b) REAL_MERGE(a,b)
#define MixHrtf MERGE(MixHrtf_,SUFFIX)
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint irSize,
ALfloat (*restrict Coeffs)[2],
const ALfloat (*restrict CoeffStep)[2],
ALfloat left, ALfloat right);
static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint irSize,
ALfloat (*restrict Coeffs)[2],
ALfloat left, ALfloat right);
void MixHrtf(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize,
const HrtfParams *hrtfparams, HrtfState *hrtfstate, ALuint BufferSize)
{
alignas(16) ALfloat Coeffs[HRIR_LENGTH][2];
ALuint Delay[2];
ALfloat left, right;
ALuint pos;
ALuint c;
for(c = 0;c < IrSize;c++)
{
Coeffs[c][0] = hrtfparams->Coeffs[c][0] - (hrtfparams->CoeffStep[c][0]*Counter);
Coeffs[c][1] = hrtfparams->Coeffs[c][1] - (hrtfparams->CoeffStep[c][1]*Counter);
}
Delay[0] = hrtfparams->Delay[0] - (hrtfparams->DelayStep[0]*Counter);
Delay[1] = hrtfparams->Delay[1] - (hrtfparams->DelayStep[1]*Counter);
for(pos = 0;pos < BufferSize && pos < Counter;pos++)
{
hrtfstate->History[Offset&SRC_HISTORY_MASK] = data[pos];
left = lerp(hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS))&SRC_HISTORY_MASK],
hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS)-1)&SRC_HISTORY_MASK],
(Delay[0]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE));
right = lerp(hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS))&SRC_HISTORY_MASK],
hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS)-1)&SRC_HISTORY_MASK],
(Delay[1]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE));
Delay[0] += hrtfparams->DelayStep[0];
Delay[1] += hrtfparams->DelayStep[1];
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f;
Offset++;
ApplyCoeffsStep(Offset, hrtfstate->Values, IrSize, Coeffs, hrtfparams->CoeffStep, left, right);
OutBuffer[FrontLeft][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0];
OutBuffer[FrontRight][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1];
OutPos++;
}
Delay[0] >>= HRTFDELAY_BITS;
Delay[1] >>= HRTFDELAY_BITS;
for(;pos < BufferSize;pos++)
{
hrtfstate->History[Offset&SRC_HISTORY_MASK] = data[pos];
left = hrtfstate->History[(Offset-Delay[0])&SRC_HISTORY_MASK];
right = hrtfstate->History[(Offset-Delay[1])&SRC_HISTORY_MASK];
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f;
Offset++;
ApplyCoeffs(Offset, hrtfstate->Values, IrSize, Coeffs, left, right);
OutBuffer[FrontLeft][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0];
OutBuffer[FrontRight][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1];
OutPos++;
}
}
#undef MixHrtf
#undef MERGE
#undef REAL_MERGE
-118
View File
@@ -1,118 +0,0 @@
#include "config.h"
#include <arm_neon.h>
#include "AL/al.h"
#include "AL/alc.h"
#include "alMain.h"
#include "alu.h"
#include "hrtf.h"
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint IrSize,
ALfloat (*restrict Coeffs)[2],
const ALfloat (*restrict CoeffStep)[2],
ALfloat left, ALfloat right)
{
ALuint c;
float32x4_t leftright4;
{
float32x2_t leftright2 = vdup_n_f32(0.0);
leftright2 = vset_lane_f32(left, leftright2, 0);
leftright2 = vset_lane_f32(right, leftright2, 1);
leftright4 = vcombine_f32(leftright2, leftright2);
}
for(c = 0;c < IrSize;c += 2)
{
const ALuint o0 = (Offset+c)&HRIR_MASK;
const ALuint o1 = (o0+1)&HRIR_MASK;
float32x4_t vals = vcombine_f32(vld1_f32((float32_t*)&Values[o0][0]),
vld1_f32((float32_t*)&Values[o1][0]));
float32x4_t coefs = vld1q_f32((float32_t*)&Coeffs[c][0]);
float32x4_t deltas = vld1q_f32(&CoeffStep[c][0]);
vals = vmlaq_f32(vals, coefs, leftright4);
coefs = vaddq_f32(coefs, deltas);
vst1_f32((float32_t*)&Values[o0][0], vget_low_f32(vals));
vst1_f32((float32_t*)&Values[o1][0], vget_high_f32(vals));
vst1q_f32(&Coeffs[c][0], coefs);
}
}
static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint IrSize,
ALfloat (*restrict Coeffs)[2],
ALfloat left, ALfloat right)
{
ALuint c;
float32x4_t leftright4;
{
float32x2_t leftright2 = vdup_n_f32(0.0);
leftright2 = vset_lane_f32(left, leftright2, 0);
leftright2 = vset_lane_f32(right, leftright2, 1);
leftright4 = vcombine_f32(leftright2, leftright2);
}
for(c = 0;c < IrSize;c += 2)
{
const ALuint o0 = (Offset+c)&HRIR_MASK;
const ALuint o1 = (o0+1)&HRIR_MASK;
float32x4_t vals = vcombine_f32(vld1_f32((float32_t*)&Values[o0][0]),
vld1_f32((float32_t*)&Values[o1][0]));
float32x4_t coefs = vld1q_f32((float32_t*)&Coeffs[c][0]);
vals = vmlaq_f32(vals, coefs, leftright4);
vst1_f32((float32_t*)&Values[o0][0], vget_low_f32(vals));
vst1_f32((float32_t*)&Values[o1][0], vget_high_f32(vals));
}
}
#define SUFFIX Neon
#include "mixer_inc.c"
#undef SUFFIX
void MixDirect_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize)
{
ALfloat gain, step;
float32x4_t gain4;
ALuint c;
for(c = 0;c < OutChans;c++)
{
ALuint pos = 0;
gain = Gains[c].Current;
step = Gains[c].Step;
if(step != 1.0f && Counter > 0)
{
for(;pos < BufferSize && pos < Counter;pos++)
{
OutBuffer[c][OutPos+pos] += data[pos]*gain;
gain *= step;
}
if(pos == Counter)
gain = Gains[c].Target;
Gains[c].Current = gain;
/* Mix until pos is aligned with 4 or the mix is done. */
for(;pos < BufferSize && (pos&3) != 0;pos++)
OutBuffer[c][OutPos+pos] += data[pos]*gain;
}
if(!(gain > GAIN_SILENCE_THRESHOLD))
continue;
gain4 = vdupq_n_f32(gain);
for(;BufferSize-pos > 3;pos += 4)
{
const float32x4_t val4 = vld1q_f32(&data[pos]);
float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]);
dry4 = vaddq_f32(dry4, vmulq_f32(val4, gain4));
vst1q_f32(&OutBuffer[c][OutPos+pos], dry4);
}
for(;pos < BufferSize;pos++)
OutBuffer[c][OutPos+pos] += data[pos]*gain;
}
}
-202
View File
@@ -1,202 +0,0 @@
#include "config.h"
#ifdef IN_IDE_PARSER
/* KDevelop's parser won't recognize these defines that get added by the -msse
* switch used to compile this source. Without them, xmmintrin.h fails to
* declare anything. */
#define __MMX__
#define __SSE__
#endif
#include <xmmintrin.h>
#include "AL/al.h"
#include "AL/alc.h"
#include "alMain.h"
#include "alu.h"
#include "alSource.h"
#include "alAuxEffectSlot.h"
#include "mixer_defs.h"
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint IrSize,
ALfloat (*restrict Coeffs)[2],
const ALfloat (*restrict CoeffStep)[2],
ALfloat left, ALfloat right)
{
const __m128 lrlr = _mm_setr_ps(left, right, left, right);
__m128 coeffs, deltas, imp0, imp1;
__m128 vals = _mm_setzero_ps();
ALuint i;
if((Offset&1))
{
const ALuint o0 = Offset&HRIR_MASK;
const ALuint o1 = (Offset+IrSize-1)&HRIR_MASK;
coeffs = _mm_load_ps(&Coeffs[0][0]);
deltas = _mm_load_ps(&CoeffStep[0][0]);
vals = _mm_loadl_pi(vals, (__m64*)&Values[o0][0]);
imp0 = _mm_mul_ps(lrlr, coeffs);
coeffs = _mm_add_ps(coeffs, deltas);
vals = _mm_add_ps(imp0, vals);
_mm_store_ps(&Coeffs[0][0], coeffs);
_mm_storel_pi((__m64*)&Values[o0][0], vals);
for(i = 1;i < IrSize-1;i += 2)
{
const ALuint o2 = (Offset+i)&HRIR_MASK;
coeffs = _mm_load_ps(&Coeffs[i+1][0]);
deltas = _mm_load_ps(&CoeffStep[i+1][0]);
vals = _mm_load_ps(&Values[o2][0]);
imp1 = _mm_mul_ps(lrlr, coeffs);
coeffs = _mm_add_ps(coeffs, deltas);
imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2));
vals = _mm_add_ps(imp0, vals);
_mm_store_ps(&Coeffs[i+1][0], coeffs);
_mm_store_ps(&Values[o2][0], vals);
imp0 = imp1;
}
vals = _mm_loadl_pi(vals, (__m64*)&Values[o1][0]);
imp0 = _mm_movehl_ps(imp0, imp0);
vals = _mm_add_ps(imp0, vals);
_mm_storel_pi((__m64*)&Values[o1][0], vals);
}
else
{
for(i = 0;i < IrSize;i += 2)
{
const ALuint o = (Offset + i)&HRIR_MASK;
coeffs = _mm_load_ps(&Coeffs[i][0]);
deltas = _mm_load_ps(&CoeffStep[i][0]);
vals = _mm_load_ps(&Values[o][0]);
imp0 = _mm_mul_ps(lrlr, coeffs);
coeffs = _mm_add_ps(coeffs, deltas);
vals = _mm_add_ps(imp0, vals);
_mm_store_ps(&Coeffs[i][0], coeffs);
_mm_store_ps(&Values[o][0], vals);
}
}
}
static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
const ALuint IrSize,
ALfloat (*restrict Coeffs)[2],
ALfloat left, ALfloat right)
{
const __m128 lrlr = _mm_setr_ps(left, right, left, right);
__m128 vals = _mm_setzero_ps();
__m128 coeffs;
ALuint i;
if((Offset&1))
{
const ALuint o0 = Offset&HRIR_MASK;
const ALuint o1 = (Offset+IrSize-1)&HRIR_MASK;
__m128 imp0, imp1;
coeffs = _mm_load_ps(&Coeffs[0][0]);
vals = _mm_loadl_pi(vals, (__m64*)&Values[o0][0]);
imp0 = _mm_mul_ps(lrlr, coeffs);
vals = _mm_add_ps(imp0, vals);
_mm_storel_pi((__m64*)&Values[o0][0], vals);
for(i = 1;i < IrSize-1;i += 2)
{
const ALuint o2 = (Offset+i)&HRIR_MASK;
coeffs = _mm_load_ps(&Coeffs[i+1][0]);
vals = _mm_load_ps(&Values[o2][0]);
imp1 = _mm_mul_ps(lrlr, coeffs);
imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2));
vals = _mm_add_ps(imp0, vals);
_mm_store_ps(&Values[o2][0], vals);
imp0 = imp1;
}
vals = _mm_loadl_pi(vals, (__m64*)&Values[o1][0]);
imp0 = _mm_movehl_ps(imp0, imp0);
vals = _mm_add_ps(imp0, vals);
_mm_storel_pi((__m64*)&Values[o1][0], vals);
}
else
{
for(i = 0;i < IrSize;i += 2)
{
const ALuint o = (Offset + i)&HRIR_MASK;
coeffs = _mm_load_ps(&Coeffs[i][0]);
vals = _mm_load_ps(&Values[o][0]);
vals = _mm_add_ps(vals, _mm_mul_ps(lrlr, coeffs));
_mm_store_ps(&Values[o][0], vals);
}
}
}
#define SUFFIX SSE
#include "mixer_inc.c"
#undef SUFFIX
void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize)
{
ALfloat gain, step;
__m128 gain4, step4;
ALuint c;
for(c = 0;c < OutChans;c++)
{
ALuint pos = 0;
gain = Gains[c].Current;
step = Gains[c].Step;
if(step != 1.0f && Counter > 0)
{
/* Mix with applying gain steps in aligned multiples of 4. */
if(BufferSize-pos > 3 && Counter-pos > 3)
{
gain4 = _mm_setr_ps(
gain,
gain * step,
gain * step * step,
gain * step * step * step
);
step4 = _mm_set1_ps(step * step * step * step);
do {
const __m128 val4 = _mm_load_ps(&data[pos]);
__m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]);
dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
gain4 = _mm_mul_ps(gain4, step4);
_mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
pos += 4;
} while(BufferSize-pos > 3 && Counter-pos > 3);
gain = _mm_cvtss_f32(gain4);
}
/* Mix with applying left over gain steps that aren't aligned multiples of 4. */
for(;pos < BufferSize && pos < Counter;pos++)
{
OutBuffer[c][OutPos+pos] += data[pos]*gain;
gain *= step;
}
if(pos == Counter)
gain = Gains[c].Target;
Gains[c].Current = gain;
/* Mix until pos is aligned with 4 or the mix is done. */
for(;pos < BufferSize && (pos&3) != 0;pos++)
OutBuffer[c][OutPos+pos] += data[pos]*gain;
}
if(!(gain > GAIN_SILENCE_THRESHOLD))
continue;
gain4 = _mm_set1_ps(gain);
for(;BufferSize-pos > 3;pos += 4)
{
const __m128 val4 = _mm_load_ps(&data[pos]);
__m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]);
dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
_mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
}
for(;pos < BufferSize;pos++)
OutBuffer[c][OutPos+pos] += data[pos]*gain;
}
}
+762
View File
@@ -0,0 +1,762 @@
/**
* 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 <math.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include "alMain.h"
#include "AL/al.h"
#include "AL/alc.h"
#include "alSource.h"
#include "alBuffer.h"
#include "alListener.h"
#include "alAuxEffectSlot.h"
#include "sample_cvt.h"
#include "alu.h"
#include "alconfig.h"
#include "ringbuffer.h"
#include "cpu_caps.h"
#include "mixer/defs.h"
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, ALsizei *restrict pos_arr, ALsizei size);
/* BSinc24 requires up to 23 extra samples before the current position, and 24 after. */
static_assert(MAX_RESAMPLE_PADDING >= 24, "MAX_RESAMPLE_PADDING must be at least 24!");
enum Resampler ResamplerDefault = LinearResampler;
MixerFunc MixSamples = Mix_C;
RowMixerFunc MixRowSamples = MixRow_C;
static HrtfMixerFunc MixHrtfSamples = MixHrtf_C;
static HrtfMixerBlendFunc MixHrtfBlendSamples = MixHrtfBlend_C;
static MixerFunc SelectMixer(void)
{
#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 RowMixerFunc SelectRowMixer(void)
{
#ifdef HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
return MixRow_Neon;
#endif
#ifdef HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
return MixRow_SSE;
#endif
return MixRow_C;
}
static inline HrtfMixerFunc SelectHrtfMixer(void)
{
#ifdef HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
return MixHrtf_Neon;
#endif
#ifdef HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
return MixHrtf_SSE;
#endif
return MixHrtf_C;
}
static inline HrtfMixerBlendFunc SelectHrtfBlendMixer(void)
{
#ifdef HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
return MixHrtfBlend_Neon;
#endif
#ifdef HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
return MixHrtfBlend_SSE;
#endif
return MixHrtfBlend_C;
}
ResamplerFunc SelectResampler(enum Resampler resampler)
{
switch(resampler)
{
case PointResampler:
return Resample_point_C;
case LinearResampler:
#ifdef HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
return Resample_lerp_Neon;
#endif
#ifdef HAVE_SSE4_1
if((CPUCapFlags&CPU_CAP_SSE4_1))
return Resample_lerp_SSE41;
#endif
#ifdef HAVE_SSE2
if((CPUCapFlags&CPU_CAP_SSE2))
return Resample_lerp_SSE2;
#endif
return Resample_lerp_C;
case FIR4Resampler:
return Resample_cubic_C;
case BSinc12Resampler:
case BSinc24Resampler:
#ifdef HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
return Resample_bsinc_Neon;
#endif
#ifdef HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
return Resample_bsinc_SSE;
#endif
return Resample_bsinc_C;
}
return Resample_point_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, "cubic") == 0)
ResamplerDefault = FIR4Resampler;
else if(strcasecmp(str, "bsinc12") == 0)
ResamplerDefault = BSinc12Resampler;
else if(strcasecmp(str, "bsinc24") == 0)
ResamplerDefault = BSinc24Resampler;
else if(strcasecmp(str, "bsinc") == 0)
{
WARN("Resampler option \"%s\" is deprecated, using bsinc12\n", str);
ResamplerDefault = BSinc12Resampler;
}
else if(strcasecmp(str, "sinc4") == 0 || strcasecmp(str, "sinc8") == 0)
{
WARN("Resampler option \"%s\" is deprecated, using cubic\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();
MixRowSamples = SelectRowMixer();
}
static void SendAsyncEvent(ALCcontext *context, ALuint enumtype, ALenum type,
ALuint objid, ALuint param, const char *msg)
{
AsyncEvent evt = ASYNC_EVENT(enumtype);
evt.u.user.type = type;
evt.u.user.id = objid;
evt.u.user.param = param;
strcpy(evt.u.user.msg, msg);
if(ll_ringbuffer_write(context->AsyncEvents, (const char*)&evt, 1) == 1)
alsem_post(&context->EventSem);
}
static inline ALfloat Sample_ALubyte(ALubyte val)
{ return (val-128) * (1.0f/128.0f); }
static inline ALfloat Sample_ALshort(ALshort val)
{ return val * (1.0f/32768.0f); }
static inline ALfloat Sample_ALfloat(ALfloat val)
{ return val; }
static inline ALfloat Sample_ALdouble(ALdouble val)
{ return (ALfloat)val; }
typedef ALubyte ALmulaw;
static inline ALfloat Sample_ALmulaw(ALmulaw val)
{ return muLawDecompressionTable[val] * (1.0f/32768.0f); }
typedef ALubyte ALalaw;
static inline ALfloat Sample_ALalaw(ALalaw val)
{ return aLawDecompressionTable[val] * (1.0f/32768.0f); }
#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(ALubyte)
DECL_TEMPLATE(ALshort)
DECL_TEMPLATE(ALfloat)
DECL_TEMPLATE(ALdouble)
DECL_TEMPLATE(ALmulaw)
DECL_TEMPLATE(ALalaw)
#undef DECL_TEMPLATE
static void LoadSamples(ALfloat *restrict dst, const ALvoid *restrict src, ALint srcstep,
enum FmtType srctype, ALsizei samples)
{
#define HANDLE_FMT(ET, ST) case ET: Load_##ST(dst, src, srcstep, samples); break
switch(srctype)
{
HANDLE_FMT(FmtUByte, ALubyte);
HANDLE_FMT(FmtShort, ALshort);
HANDLE_FMT(FmtFloat, ALfloat);
HANDLE_FMT(FmtDouble, ALdouble);
HANDLE_FMT(FmtMulaw, ALmulaw);
HANDLE_FMT(FmtAlaw, ALalaw);
}
#undef HANDLE_FMT
}
static const ALfloat *DoFilters(BiquadFilter *lpfilter, BiquadFilter *hpfilter,
ALfloat *restrict dst, const ALfloat *restrict src,
ALsizei numsamples, enum ActiveFilters type)
{
ALsizei i;
switch(type)
{
case AF_None:
BiquadFilter_passthru(lpfilter, numsamples);
BiquadFilter_passthru(hpfilter, numsamples);
break;
case AF_LowPass:
BiquadFilter_process(lpfilter, dst, src, numsamples);
BiquadFilter_passthru(hpfilter, numsamples);
return dst;
case AF_HighPass:
BiquadFilter_passthru(lpfilter, numsamples);
BiquadFilter_process(hpfilter, dst, src, numsamples);
return dst;
case AF_BandPass:
for(i = 0;i < numsamples;)
{
ALfloat temp[256];
ALsizei todo = mini(256, numsamples-i);
BiquadFilter_process(lpfilter, temp, src+i, todo);
BiquadFilter_process(hpfilter, dst+i, temp, todo);
i += todo;
}
return dst;
}
return src;
}
/* This function uses these device temp buffers. */
#define SOURCE_DATA_BUF 0
#define RESAMPLED_BUF 1
#define FILTERED_BUF 2
#define NFC_DATA_BUF 3
ALboolean MixSource(ALvoice *voice, ALuint SourceID, ALCcontext *Context, ALsizei SamplesToDo)
{
ALCdevice *Device = Context->Device;
ALbufferlistitem *BufferListItem;
ALbufferlistitem *BufferLoopItem;
ALsizei NumChannels, SampleSize;
ALbitfieldSOFT enabledevt;
ALsizei buffers_done = 0;
ResamplerFunc Resample;
ALsizei DataPosInt;
ALsizei DataPosFrac;
ALint64 DataSize64;
ALint increment;
ALsizei Counter;
ALsizei OutPos;
ALsizei IrSize;
bool isplaying;
bool firstpass;
bool isstatic;
ALsizei chan;
ALsizei send;
/* Get source info */
isplaying = true; /* Will only be called while playing. */
isstatic = !!(voice->Flags&VOICE_IS_STATIC);
DataPosInt = ATOMIC_LOAD(&voice->position, almemory_order_acquire);
DataPosFrac = ATOMIC_LOAD(&voice->position_fraction, almemory_order_relaxed);
BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
BufferLoopItem = ATOMIC_LOAD(&voice->loop_buffer, almemory_order_relaxed);
NumChannels = voice->NumChannels;
SampleSize = voice->SampleSize;
increment = voice->Step;
IrSize = (Device->HrtfHandle ? Device->HrtfHandle->irSize : 0);
Resample = ((increment == FRACTIONONE && DataPosFrac == 0) ?
Resample_copy_C : voice->Resampler);
Counter = (voice->Flags&VOICE_IS_FADING) ? SamplesToDo : 0;
firstpass = true;
OutPos = 0;
do {
ALsizei SrcBufferSize, DstBufferSize;
/* Figure out how many buffer samples will be needed */
DataSize64 = SamplesToDo-OutPos;
DataSize64 *= increment;
DataSize64 += DataPosFrac+FRACTIONMASK;
DataSize64 >>= FRACTIONBITS;
DataSize64 += MAX_RESAMPLE_PADDING*2;
SrcBufferSize = (ALsizei)mini64(DataSize64, BUFFERSIZE);
/* Figure out how many samples we can actually mix from this. */
DataSize64 = SrcBufferSize;
DataSize64 -= MAX_RESAMPLE_PADDING*2;
DataSize64 <<= FRACTIONBITS;
DataSize64 -= DataPosFrac;
DstBufferSize = (ALsizei)mini64((DataSize64+(increment-1)) / increment,
SamplesToDo - OutPos);
/* Some mixers like having a multiple of 4, so try to give that unless
* this is the last update. */
if(DstBufferSize < SamplesToDo-OutPos)
DstBufferSize &= ~3;
/* It's impossible to have a buffer list item with no entries. */
assert(BufferListItem->num_buffers > 0);
for(chan = 0;chan < NumChannels;chan++)
{
const ALfloat *ResampledData;
ALfloat *SrcData = Device->TempBuffer[SOURCE_DATA_BUF];
ALsizei FilledAmt;
/* Load the previous samples into the source data first, and clear the rest. */
memcpy(SrcData, voice->PrevSamples[chan], MAX_RESAMPLE_PADDING*sizeof(ALfloat));
memset(SrcData+MAX_RESAMPLE_PADDING, 0, (BUFFERSIZE-MAX_RESAMPLE_PADDING)*
sizeof(ALfloat));
FilledAmt = MAX_RESAMPLE_PADDING;
if(isstatic)
{
/* TODO: For static sources, loop points are taken from the
* first buffer (should be adjusted by any buffer offset, to
* possibly be added later).
*/
const ALbuffer *Buffer0 = BufferListItem->buffers[0];
const ALsizei LoopStart = Buffer0->LoopStart;
const ALsizei LoopEnd = Buffer0->LoopEnd;
const ALsizei LoopSize = LoopEnd - LoopStart;
/* If current pos is beyond the loop range, do not loop */
if(!BufferLoopItem || DataPosInt >= LoopEnd)
{
ALsizei SizeToDo = SrcBufferSize - FilledAmt;
ALsizei CompLen = 0;
ALsizei i;
BufferLoopItem = NULL;
for(i = 0;i < BufferListItem->num_buffers;i++)
{
const ALbuffer *buffer = BufferListItem->buffers[i];
const ALubyte *Data = buffer->data;
ALsizei DataSize;
if(DataPosInt >= buffer->SampleLen)
continue;
/* Load what's left to play from the buffer */
DataSize = mini(SizeToDo, buffer->SampleLen - DataPosInt);
CompLen = maxi(CompLen, DataSize);
LoadSamples(&SrcData[FilledAmt],
&Data[(DataPosInt*NumChannels + chan)*SampleSize],
NumChannels, buffer->FmtType, DataSize
);
}
FilledAmt += CompLen;
}
else
{
ALsizei SizeToDo = mini(SrcBufferSize - FilledAmt, LoopEnd - DataPosInt);
ALsizei CompLen = 0;
ALsizei i;
for(i = 0;i < BufferListItem->num_buffers;i++)
{
const ALbuffer *buffer = BufferListItem->buffers[i];
const ALubyte *Data = buffer->data;
ALsizei DataSize;
if(DataPosInt >= buffer->SampleLen)
continue;
/* Load what's left of this loop iteration */
DataSize = mini(SizeToDo, buffer->SampleLen - DataPosInt);
CompLen = maxi(CompLen, DataSize);
LoadSamples(&SrcData[FilledAmt],
&Data[(DataPosInt*NumChannels + chan)*SampleSize],
NumChannels, buffer->FmtType, DataSize
);
}
FilledAmt += CompLen;
while(SrcBufferSize > FilledAmt)
{
const ALsizei SizeToDo = mini(SrcBufferSize - FilledAmt, LoopSize);
CompLen = 0;
for(i = 0;i < BufferListItem->num_buffers;i++)
{
const ALbuffer *buffer = BufferListItem->buffers[i];
const ALubyte *Data = buffer->data;
ALsizei DataSize;
if(LoopStart >= buffer->SampleLen)
continue;
DataSize = mini(SizeToDo, buffer->SampleLen - LoopStart);
CompLen = maxi(CompLen, DataSize);
LoadSamples(&SrcData[FilledAmt],
&Data[(LoopStart*NumChannels + chan)*SampleSize],
NumChannels, buffer->FmtType, DataSize
);
}
FilledAmt += CompLen;
}
}
}
else
{
/* Crawl the buffer queue to fill in the temp buffer */
ALbufferlistitem *tmpiter = BufferListItem;
ALsizei pos = DataPosInt;
while(tmpiter && SrcBufferSize > FilledAmt)
{
ALsizei SizeToDo = SrcBufferSize - FilledAmt;
ALsizei CompLen = 0;
ALsizei i;
for(i = 0;i < tmpiter->num_buffers;i++)
{
const ALbuffer *ALBuffer = tmpiter->buffers[i];
ALsizei DataSize = ALBuffer ? ALBuffer->SampleLen : 0;
if(DataSize > pos)
{
const ALubyte *Data = ALBuffer->data;
Data += (pos*NumChannels + chan)*SampleSize;
DataSize = mini(SizeToDo, DataSize - pos);
CompLen = maxi(CompLen, DataSize);
LoadSamples(&SrcData[FilledAmt], Data, NumChannels,
ALBuffer->FmtType, DataSize);
}
}
if(UNLIKELY(!CompLen))
pos -= tmpiter->max_samples;
else
{
FilledAmt += CompLen;
if(SrcBufferSize <= FilledAmt)
break;
pos = 0;
}
tmpiter = ATOMIC_LOAD(&tmpiter->next, almemory_order_acquire);
if(!tmpiter) tmpiter = BufferLoopItem;
}
}
/* Store the last source samples used for next time. */
memcpy(voice->PrevSamples[chan],
&SrcData[(increment*DstBufferSize + DataPosFrac)>>FRACTIONBITS],
MAX_RESAMPLE_PADDING*sizeof(ALfloat)
);
/* Now resample, then filter and mix to the appropriate outputs. */
ResampledData = Resample(&voice->ResampleState,
&SrcData[MAX_RESAMPLE_PADDING], DataPosFrac, increment,
Device->TempBuffer[RESAMPLED_BUF], DstBufferSize
);
{
DirectParams *parms = &voice->Direct.Params[chan];
const ALfloat *samples;
samples = DoFilters(
&parms->LowPass, &parms->HighPass, Device->TempBuffer[FILTERED_BUF],
ResampledData, DstBufferSize, voice->Direct.FilterType
);
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->TempBuffer[NFC_DATA_BUF];
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) \
{ \
NfcFilterProcess##order(&parms->NFCtrlFilter, 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
{
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 = 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 = 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(send = 0;send < Device->NumAuxSends;send++)
{
SendParams *parms = &voice->Send[send].Params[chan];
const ALfloat *samples;
if(!voice->Send[send].Buffer)
continue;
samples = DoFilters(
&parms->LowPass, &parms->HighPass, Device->TempBuffer[FILTERED_BUF],
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
);
}
}
/* Update positions */
DataPosFrac += increment*DstBufferSize;
DataPosInt += DataPosFrac>>FRACTIONBITS;
DataPosFrac &= FRACTIONMASK;
OutPos += DstBufferSize;
voice->Offset += DstBufferSize;
Counter = maxi(DstBufferSize, Counter) - DstBufferSize;
firstpass = false;
if(isstatic)
{
if(BufferLoopItem)
{
/* Handle looping static source */
const ALbuffer *Buffer = BufferListItem->buffers[0];
ALsizei LoopStart = Buffer->LoopStart;
ALsizei LoopEnd = Buffer->LoopEnd;
if(DataPosInt >= LoopEnd)
{
assert(LoopEnd > LoopStart);
DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart;
}
}
else
{
/* Handle non-looping static source */
if(DataPosInt >= BufferListItem->max_samples)
{
isplaying = false;
BufferListItem = NULL;
DataPosInt = 0;
DataPosFrac = 0;
break;
}
}
}
else while(1)
{
/* Handle streaming source */
if(BufferListItem->max_samples > DataPosInt)
break;
DataPosInt -= BufferListItem->max_samples;
buffers_done += BufferListItem->num_buffers;
BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_relaxed);
if(!BufferListItem && !(BufferListItem=BufferLoopItem))
{
isplaying = false;
DataPosInt = 0;
DataPosFrac = 0;
break;
}
}
} while(isplaying && OutPos < SamplesToDo);
voice->Flags |= VOICE_IS_FADING;
/* Update source info */
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);
/* Send any events now, after the position/buffer info was updated. */
enabledevt = ATOMIC_LOAD(&Context->EnabledEvts, almemory_order_acquire);
if(buffers_done > 0 && (enabledevt&EventType_BufferCompleted))
SendAsyncEvent(Context, EventType_BufferCompleted,
AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, SourceID, buffers_done, "Buffer completed"
);
return isplaying;
}
+1167 -378
View File
File diff suppressed because it is too large Load Diff
+105
View File
@@ -0,0 +1,105 @@
#ifndef POLYMORPHISM_H
#define POLYMORPHISM_H
/* Macros to declare inheriting types, and to (down-)cast and up-cast. */
#define DERIVE_FROM_TYPE(t) t t##_parent
#define STATIC_CAST(to, obj) (&(obj)->to##_parent)
#ifdef __GNUC__
#define STATIC_UPCAST(to, from, obj) __extension__({ \
static_assert(__builtin_types_compatible_p(from, __typeof(*(obj))), \
"Invalid upcast object from type"); \
(to*)((char*)(obj) - offsetof(to, from##_parent)); \
})
#else
#define STATIC_UPCAST(to, from, obj) ((to*)((char*)(obj) - offsetof(to, from##_parent)))
#endif
/* Defines method forwards, which call the given parent's (T2's) implementation. */
#define DECLARE_FORWARD(T1, T2, rettype, func) \
rettype T1##_##func(T1 *obj) \
{ return T2##_##func(STATIC_CAST(T2, obj)); }
#define DECLARE_FORWARD1(T1, T2, rettype, func, argtype1) \
rettype T1##_##func(T1 *obj, argtype1 a) \
{ return T2##_##func(STATIC_CAST(T2, obj), a); }
#define DECLARE_FORWARD2(T1, T2, rettype, func, argtype1, argtype2) \
rettype T1##_##func(T1 *obj, argtype1 a, argtype2 b) \
{ return T2##_##func(STATIC_CAST(T2, obj), a, b); }
#define DECLARE_FORWARD3(T1, T2, rettype, func, argtype1, argtype2, argtype3) \
rettype T1##_##func(T1 *obj, argtype1 a, argtype2 b, argtype3 c) \
{ return T2##_##func(STATIC_CAST(T2, obj), a, b, c); }
/* Defines method thunks, functions that call to the child's method. */
#define DECLARE_THUNK(T1, T2, rettype, func) \
static rettype T1##_##T2##_##func(T2 *obj) \
{ return T1##_##func(STATIC_UPCAST(T1, T2, obj)); }
#define DECLARE_THUNK1(T1, T2, rettype, func, argtype1) \
static rettype T1##_##T2##_##func(T2 *obj, argtype1 a) \
{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a); }
#define DECLARE_THUNK2(T1, T2, rettype, func, argtype1, argtype2) \
static rettype T1##_##T2##_##func(T2 *obj, argtype1 a, argtype2 b) \
{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a, b); }
#define DECLARE_THUNK3(T1, T2, rettype, func, argtype1, argtype2, argtype3) \
static rettype T1##_##T2##_##func(T2 *obj, argtype1 a, argtype2 b, argtype3 c) \
{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a, b, c); }
#define DECLARE_THUNK4(T1, T2, rettype, func, argtype1, argtype2, argtype3, argtype4) \
static rettype T1##_##T2##_##func(T2 *obj, argtype1 a, argtype2 b, argtype3 c, argtype4 d) \
{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a, b, c, d); }
/* Defines the default functions used to (de)allocate a polymorphic object. */
#define DECLARE_DEFAULT_ALLOCATORS(T) \
static void* T##_New(size_t size) { return al_malloc(16, size); } \
static void T##_Delete(void *ptr) { al_free(ptr); }
/* Helper to extract an argument list for virtual method calls. */
#define EXTRACT_VCALL_ARGS(...) __VA_ARGS__))
/* Call a "virtual" method on an object, with arguments. */
#define V(obj, func) ((obj)->vtbl->func((obj), EXTRACT_VCALL_ARGS
/* Call a "virtual" method on an object, with no arguments. */
#define V0(obj, func) ((obj)->vtbl->func((obj) EXTRACT_VCALL_ARGS
/* Helper to extract an argument list for NEW_OBJ calls. */
#define EXTRACT_NEW_ARGS(...) __VA_ARGS__); \
} \
} while(0)
/* Allocate and construct an object, with arguments. */
#define NEW_OBJ(_res, T) do { \
_res = T##_New(sizeof(T)); \
if(_res) \
{ \
memset(_res, 0, sizeof(T)); \
T##_Construct(_res, EXTRACT_NEW_ARGS
/* Allocate and construct an object, with no arguments. */
#define NEW_OBJ0(_res, T) do { \
_res = T##_New(sizeof(T)); \
if(_res) \
{ \
memset(_res, 0, sizeof(T)); \
T##_Construct(_res EXTRACT_NEW_ARGS
/* Destructs and deallocate an object. */
#define DELETE_OBJ(obj) do { \
if((obj) != NULL) \
{ \
V0((obj),Destruct)(); \
V0((obj),Delete)(); \
} \
} while(0)
/* Helper to get a type's vtable thunk for a child type. */
#define GET_VTABLE2(T1, T2) (&(T1##_##T2##_vtable))
/* Helper to set an object's vtable thunk for a child type. Used when constructing an object. */
#define SET_VTABLE2(T1, T2, obj) (STATIC_CAST(T2, obj)->vtbl = GET_VTABLE2(T1, T2))
#endif /* POLYMORPHISM_H */
+295
View File
@@ -0,0 +1,295 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include "ringbuffer.h"
#include "align.h"
#include "atomic.h"
#include "threads.h"
#include "almalloc.h"
#include "compat.h"
/* 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;
alignas(16) char buf[];
};
ll_ringbuffer_t *ll_ringbuffer_create(size_t sz, size_t elem_sz, int limit_writes)
{
ll_ringbuffer_t *rb;
size_t power_of_two = 0;
if(sz > 0)
{
power_of_two = sz;
power_of_two |= power_of_two>>1;
power_of_two |= power_of_two>>2;
power_of_two |= power_of_two>>4;
power_of_two |= power_of_two>>8;
power_of_two |= power_of_two>>16;
#if SIZE_MAX > UINT_MAX
power_of_two |= power_of_two>>32;
#endif
}
power_of_two++;
if(power_of_two < sz) return NULL;
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 = limit_writes ? sz : power_of_two;
rb->size_mask = power_of_two - 1;
rb->elem_size = elem_sz;
return rb;
}
void ll_ringbuffer_free(ll_ringbuffer_t *rb)
{
al_free(rb);
}
void ll_ringbuffer_reset(ll_ringbuffer_t *rb)
{
ATOMIC_STORE(&rb->write_ptr, 0, almemory_order_release);
ATOMIC_STORE(&rb->read_ptr, 0, almemory_order_release);
memset(rb->buf, 0, (rb->size_mask+1)*rb->elem_size);
}
size_t ll_ringbuffer_read_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 (w-r) & rb->size_mask;
}
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);
w = (r-w-1) & rb->size_mask;
return (w > rb->size) ? rb->size : w;
}
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;
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_mask+1)
{
n1 = rb->size_mask+1 - 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);
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;
}
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_mask+1)
{
n1 = rb->size_mask+1 - 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;
}
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_mask+1)
{
n1 = rb->size_mask+1 - 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;
}
void ll_ringbuffer_read_advance(ll_ringbuffer_t *rb, size_t cnt)
{
ATOMIC_ADD(&rb->read_ptr, cnt, almemory_order_acq_rel);
}
void ll_ringbuffer_write_advance(ll_ringbuffer_t *rb, size_t cnt)
{
ATOMIC_ADD(&rb->write_ptr, cnt, almemory_order_acq_rel);
}
void ll_ringbuffer_get_read_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t vec[2])
{
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_mask+1)
{
/* 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_mask+1 - 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;
}
}
void ll_ringbuffer_get_write_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t vec[2])
{
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;
if(free_cnt > rb->size) free_cnt = rb->size;
cnt2 = w + free_cnt;
if(cnt2 > rb->size_mask+1)
{
/* 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_mask+1 - 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;
}
}
+77
View File
@@ -0,0 +1,77 @@
#ifndef RINGBUFFER_H
#define RINGBUFFER_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct ll_ringbuffer ll_ringbuffer_t;
typedef struct ll_ringbuffer_data {
char *buf;
size_t len;
} ll_ringbuffer_data_t;
/**
* 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 (even if it is
* already a power of two, to ensure the requested amount can be written).
*/
ll_ringbuffer_t *ll_ringbuffer_create(size_t sz, size_t elem_sz, int limit_writes);
/** Free all data associated with the ringbuffer `rb'. */
void ll_ringbuffer_free(ll_ringbuffer_t *rb);
/** Reset the read and write pointers to zero. This is not thread safe. */
void ll_ringbuffer_reset(ll_ringbuffer_t *rb);
/**
* 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[2]);
/**
* 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[2]);
/**
* 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);
/**
* 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);
/**
* 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);
/** Advance the read pointer `cnt' places. */
void ll_ringbuffer_read_advance(ll_ringbuffer_t *rb, size_t cnt);
/**
* 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);
/**
* 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);
/** Advance the write pointer `cnt' places. */
void ll_ringbuffer_write_advance(ll_ringbuffer_t *rb, size_t cnt);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* RINGBUFFER_H */
+120
View File
@@ -0,0 +1,120 @@
#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 Filter1CoeffSqr[4] = {
0.479400865589f, 0.876218493539f, 0.976597589508f, 0.997499255936f
};
static const ALfloat Filter2CoeffSqr[4] = {
0.161758498368f, 0.733028932341f, 0.945349700329f, 0.990599156685f
};
static void allpass_process(AllPassState *state, ALfloat *restrict dst, const ALfloat *restrict src, const ALfloat aa, ALsizei todo)
{
ALfloat z1 = state->z[0];
ALfloat z2 = state->z[1];
ALsizei i;
for(i = 0;i < todo;i++)
{
ALfloat input = src[i];
ALfloat output = input*aa + z1;
z1 = z2; z2 = output*aa - input;
dst[i] = output;
}
state->z[0] = z1;
state->z[1] = z2;
}
/* 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;
ASSUME(SamplesToDo > 0);
for(base = 0;base < SamplesToDo;)
{
ALsizei todo = mini(SamplesToDo - base, MAX_UPDATE_SAMPLES);
ASSUME(todo > 0);
/* 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], Filter1CoeffSqr[0], todo);
allpass_process(&enc->Filter1_Y[1], temp[0], temp[1], Filter1CoeffSqr[1], todo);
allpass_process(&enc->Filter1_Y[2], temp[1], temp[0], Filter1CoeffSqr[2], todo);
allpass_process(&enc->Filter1_Y[3], temp[0], temp[1], Filter1CoeffSqr[3], 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->LastY;
for(i = 1;i < todo;i++)
D[i] = temp[0][i-1];
enc->LastY = 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], Filter2CoeffSqr[0], todo);
allpass_process(&enc->Filter2_WX[1], temp[0], temp[1], Filter2CoeffSqr[1], todo);
allpass_process(&enc->Filter2_WX[2], temp[1], temp[0], Filter2CoeffSqr[2], todo);
allpass_process(&enc->Filter2_WX[3], temp[0], temp[1], Filter2CoeffSqr[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], Filter1CoeffSqr[0], todo);
allpass_process(&enc->Filter1_WX[1], temp[0], temp[1], Filter1CoeffSqr[1], todo);
allpass_process(&enc->Filter1_WX[2], temp[1], temp[0], Filter1CoeffSqr[2], todo);
allpass_process(&enc->Filter1_WX[3], temp[0], temp[1], Filter1CoeffSqr[3], todo);
S[0] = enc->LastWX;
for(i = 1;i < todo;i++)
S[i] = temp[0][i-1];
enc->LastWX = 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 z[2];
} 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_Y[4];
AllPassState Filter2_WX[4];
AllPassState Filter1_WX[4];
ALfloat LastY, LastWX;
} 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 */
+46 -39
View File
@@ -5,60 +5,67 @@
#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 = (_x) ? (char*)((_x)->Data) - (char*)(_x) : \
sizeof(*(_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 +74,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)) \
+952 -412
View File
File diff suppressed because it is too large Load Diff
+15 -62
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
@@ -53,7 +51,7 @@ library. If the library is modified by someone else and passed on, we
want its recipients to know that what they have is not the original
version, so that any problems introduced by others will not reflect on
the original authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that companies distributing free
software will individually obtain patent licenses, thus in effect
@@ -100,8 +98,8 @@ 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
@@ -147,7 +145,7 @@ Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
@@ -205,7 +203,7 @@ instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
@@ -256,7 +254,7 @@ Library will still fall under Section 6.)
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also compile or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
@@ -310,7 +308,7 @@ restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
@@ -351,7 +349,7 @@ subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
@@ -403,7 +401,7 @@ conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
@@ -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,49 +434,4 @@ 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
Appendix: 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
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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., 675 Mass Ave, Cambridge, MA 02139, USA.
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
END OF TERMS AND CONDITIONS
+372
View File
@@ -0,0 +1,372 @@
openal-soft-1.19.1:
Implemented capture support for the SoundIO backend.
Fixed source buffer queues potentially not playing properly when a queue
entry completes.
Fixed possible unexpected failures when generating auxiliary effect slots.
Fixed a crash with certain reverb or device settings.
Fixed OpenSL capture.
Improved output limiter response, better ensuring the sample amplitude is
clamped for output.
openal-soft-1.19.0:
Implemented the ALC_SOFT_device_clock extension.
Implemented the Pitch Shifter, Frequency Shifter, and Autowah effects.
Fixed compiling on FreeBSD systems that use freebsd-lib 9.1.
Fixed compiling on NetBSD.
Fixed the reverb effect's density scale and panning parameters.
Fixed use of the WASAPI backend with certain games, which caused odd COM
initialization errors.
Increased the number of virtual channels for decoding Ambisonics to HRTF
output.
Changed 32-bit x86 builds to use SSE2 math by default for performance.
Build-time options are available to use just SSE1 or x87 instead.
Replaced the 4-point Sinc resampler with a more efficient cubic resampler.
Renamed the MMDevAPI backend to WASAPI.
Added support for 24-bit, dual-ear HRTF data sets. The built-in data set
has been updated to 24-bit.
Added a 24- to 48-point band-limited Sinc resampler.
Added an SDL2 playback backend. Disabled by default to avoid a dependency
on SDL2.
Improved the performance and quality of the Chorus and Flanger effects.
Improved the efficiency of the band-limited Sinc resampler.
Improved the Sinc resampler's transition band to avoid over-attenuating
higher frequencies.
Improved the performance of some filter operations.
Improved the efficiency of object ID lookups.
Improved the efficienty of internal voice/source synchronization.
Improved AL call error logging with contextualized messages.
Removed the reverb effect's modulation stage. Due to the lack of reference
for its intended behavior and strength.
openal-soft-1.18.2:
Fixed resetting the FPU rounding mode after certain function calls on
Windows.
Fixed use of SSE intrinsics when building with Clang on Windows.
Fixed a crash with the JACK backend when using JACK1.
Fixed use of pthread_setnane_np on NetBSD.
Fixed building on FreeBSD with an older freebsd-lib.
OSS now links with libossaudio if found at build time (for NetBSD).
openal-soft-1.18.1:
Fixed an issue where resuming a source might not restart playing it.
Fixed PulseAudio playback when the configured stream length is much less
than the requested length.
Fixed MMDevAPI capture with sample rates not matching the backing device.
Fixed int32 output for the Wave Writer.
Fixed enumeration of OSS devices that are missing device files.
Added correct retrieval of the executable's path on FreeBSD.
Added a config option to specify the dithering depth.
Added a 5.1 decoder preset that excludes front-center output.
openal-soft-1.18.0:
Implemented the AL_EXT_STEREO_ANGLES and AL_EXT_SOURCE_RADIUS extensions.
Implemented the AL_SOFT_gain_clamp_ex, AL_SOFT_source_resampler,
AL_SOFT_source_spatialize, and ALC_SOFT_output_limiter extensions.
Implemented 3D processing for some effects. Currently implemented for
Reverb, Compressor, Equalizer, and Ring Modulator.
Implemented 2-channel UHJ output encoding. This needs to be enabled with a
config option to be used.
Implemented dual-band processing for high-quality ambisonic decoding.
Implemented distance-compensation for surround sound output.
Implemented near-field emulation and compensation with ambisonic rendering.
Currently only applies when using the high-quality ambisonic decoder or
ambisonic output, with appropriate config options.
Implemented an output limiter to reduce the amount of distortion from
clipping.
Implemented dithering for 8-bit and 16-bit output.
Implemented a config option to select a preferred HRTF.
Implemented a run-time check for NEON extensions using /proc/cpuinfo.
Implemented experimental capture support for the OpenSL backend.
Fixed building on compilers with NEON support but don't default to having
NEON enabled.
Fixed support for JACK on Windows.
Fixed starting a source while alcSuspendContext is in effect.
Fixed detection of headsets as headphones, with MMDevAPI.
Added support for AmbDec config files, for custom ambisonic decoder
configurations. Version 3 files only.
Added backend-specific options to alsoft-config.
Added first-, second-, and third-order ambisonic output formats. Currently
only works with backends that don't rely on channel labels, like JACK,
ALSA, and OSS.
Added a build option to embed the default HRTFs into the lib.
Added AmbDec presets to enable high-quality ambisonic decoding.
Added an AmbDec preset for 3D7.1 speaker setups.
Added documentation regarding Ambisonics, 3D7.1, AmbDec config files, and
the provided ambdec presets.
Added the ability for MMDevAPI to open devices given a Device ID or GUID
string.
Added an option to the example apps to open a specific device.
Increased the maximum auxiliary send limit to 16 (up from 4). Requires
requesting them with the ALC_MAX_AUXILIARY_SENDS context creation
attribute.
Increased the default auxiliary effect slot count to 64 (up from 4).
Reduced the default period count to 3 (down from 4).
Slightly improved automatic naming for enumerated HRTFs.
Improved B-Format decoding with HRTF output.
Improved internal property handling for better batching behavior.
Improved performance of certain filter uses.
Removed support for the AL_SOFT_buffer_samples and AL_SOFT_buffer_sub_data
extensions. Due to conflicts with AL_EXT_SOURCE_RADIUS.
openal-soft-1.17.2:
Implemented device enumeration for OSSv4.
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.
+108 -40
View File
@@ -4,6 +4,7 @@
#include "alMain.h"
#include "alEffect.h"
#include "atomic.h"
#include "align.h"
#ifdef __cplusplus
@@ -14,24 +15,36 @@ 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 ALCcontext *context, 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);
};
/* Small hack to use a pointer-to-array types as a normal argument type.
* Shouldn't be used directly.
*/
typedef ALfloat ALfloatBUFFERSIZE[BUFFERSIZE];
#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 ALCcontext*, 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)); } \
\
@@ -46,69 +59,124 @@ static const struct ALeffectStateVtable T##_ALeffectState_vtable = { \
}
struct ALeffectStateFactoryVtable;
struct EffectStateFactoryVtable;
typedef struct ALeffectStateFactory {
const struct ALeffectStateFactoryVtable *vtbl;
} ALeffectStateFactory;
typedef struct EffectStateFactory {
const struct EffectStateFactoryVtable *vtab;
} EffectStateFactory;
struct ALeffectStateFactoryVtable {
ALeffectState *(*const create)(ALeffectStateFactory *factory);
struct EffectStateFactoryVtable {
ALeffectState *(*const create)(EffectStateFactory *factory);
};
#define EffectStateFactory_create(x) ((x)->vtab->create((x)))
#define DEFINE_ALEFFECTSTATEFACTORY_VTABLE(T) \
DECLARE_THUNK(T, ALeffectStateFactory, ALeffectState*, create) \
#define DEFINE_EFFECTSTATEFACTORY_VTABLE(T) \
DECLARE_THUNK(T, EffectStateFactory, ALeffectState*, create) \
\
static const struct ALeffectStateFactoryVtable T##_ALeffectStateFactory_vtable = { \
T##_ALeffectStateFactory_create, \
static const struct EffectStateFactoryVtable T##_EffectStateFactory_vtable = { \
T##_EffectStateFactory_create, \
}
#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;
struct {
ALfloat Gain;
ALboolean AuxSendAuto;
ALenum EffectType;
ALeffectProps EffectProps;
ALeffectState *EffectState;
ALfloat RoomRolloff; /* Added to the source's room rolloff, not multiplied. */
ALfloat DecayTime;
ALfloat DecayLFRatio;
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 * sqrt(3)
* * Channel 2 is OpenAL Y * sqrt(3)
* * Channel 3 is OpenAL -Z * sqrt(3)
* 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 source pan for first-order device
* output (FOAOut).
*/
alignas(16) ALfloat WetBuffer[MAX_EFFECT_CHANNELS][BUFFERSIZE];
} ALeffectslot;
inline struct ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id)
{ return (struct ALeffectslot*)LookupUIntMapKey(&context->EffectSlotMap, id); }
inline struct ALeffectslot *RemoveEffectSlot(ALCcontext *context, ALuint id)
{ return (struct ALeffectslot*)RemoveUIntMapKey(&context->EffectSlotMap, id); }
ALenum InitEffectSlot(ALeffectslot *slot);
void DeinitEffectSlot(ALeffectslot *slot);
void UpdateEffectSlotProps(ALeffectslot *slot, ALCcontext *context);
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);
ALeffectStateFactory *ALechoStateFactory_getFactory(void);
ALeffectStateFactory *ALequalizerStateFactory_getFactory(void);
ALeffectStateFactory *ALflangerStateFactory_getFactory(void);
ALeffectStateFactory *ALmodulatorStateFactory_getFactory(void);
EffectStateFactory *NullStateFactory_getFactory(void);
EffectStateFactory *ReverbStateFactory_getFactory(void);
EffectStateFactory *AutowahStateFactory_getFactory(void);
EffectStateFactory *ChorusStateFactory_getFactory(void);
EffectStateFactory *CompressorStateFactory_getFactory(void);
EffectStateFactory *DistortionStateFactory_getFactory(void);
EffectStateFactory *EchoStateFactory_getFactory(void);
EffectStateFactory *EqualizerStateFactory_getFactory(void);
EffectStateFactory *FlangerStateFactory_getFactory(void);
EffectStateFactory *FshifterStateFactory_getFactory(void);
EffectStateFactory *ModulatorStateFactory_getFactory(void);
EffectStateFactory *PshifterStateFactory_getFactory(void);
ALeffectStateFactory *ALdedicatedStateFactory_getFactory(void);
EffectStateFactory *DedicatedStateFactory_getFactory(void);
ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *effect);
ALenum InitializeEffect(ALCcontext *Context, ALeffectslot *EffectSlot, ALeffect *effect);
void InitEffectFactoryMap(void);
void DeinitEffectFactoryMap(void);
void ALeffectState_DecRef(ALeffectState *state);
#ifdef __cplusplus
}
+49 -50
View File
@@ -1,7 +1,13 @@
#ifndef _AL_BUFFER_H_
#define _AL_BUFFER_H_
#include "alMain.h"
#include "AL/alc.h"
#include "AL/al.h"
#include "AL/alext.h"
#include "inprogext.h"
#include "atomic.h"
#include "rwlock.h"
#ifdef __cplusplus
extern "C" {
@@ -9,34 +15,30 @@ extern "C" {
/* User formats */
enum UserFmtType {
UserFmtByte = AL_BYTE_SOFT,
UserFmtUByte = AL_UNSIGNED_BYTE_SOFT,
UserFmtShort = AL_SHORT_SOFT,
UserFmtUShort = AL_UNSIGNED_SHORT_SOFT,
UserFmtInt = AL_INT_SOFT,
UserFmtUInt = AL_UNSIGNED_INT_SOFT,
UserFmtFloat = AL_FLOAT_SOFT,
UserFmtDouble = AL_DOUBLE_SOFT,
UserFmtByte3 = AL_BYTE3_SOFT,
UserFmtUByte3 = AL_UNSIGNED_BYTE3_SOFT,
UserFmtUByte,
UserFmtShort,
UserFmtFloat,
UserFmtDouble,
UserFmtMulaw,
UserFmtAlaw,
UserFmtIMA4,
UserFmtMSADPCM,
};
enum UserFmtChannels {
UserFmtMono = AL_MONO_SOFT,
UserFmtStereo = AL_STEREO_SOFT,
UserFmtRear = AL_REAR_SOFT,
UserFmtQuad = AL_QUAD_SOFT,
UserFmtX51 = AL_5POINT1_SOFT, /* (WFX order) */
UserFmtX61 = AL_6POINT1_SOFT, /* (WFX order) */
UserFmtX71 = AL_7POINT1_SOFT, /* (WFX order) */
UserFmtMono,
UserFmtStereo,
UserFmtRear,
UserFmtQuad,
UserFmtX51, /* (WFX order) */
UserFmtX61, /* (WFX order) */
UserFmtX71, /* (WFX order) */
UserFmtBFormat2D, /* WXY */
UserFmtBFormat3D, /* 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);
}
@@ -44,9 +46,12 @@ inline ALuint FrameSizeFromUserFmt(enum UserFmtChannels chans, enum UserFmtType
/* Storable formats */
enum FmtType {
FmtByte = UserFmtByte,
FmtShort = UserFmtShort,
FmtFloat = UserFmtFloat,
FmtUByte = UserFmtUByte,
FmtShort = UserFmtShort,
FmtFloat = UserFmtFloat,
FmtDouble = UserFmtDouble,
FmtMulaw = UserFmtMulaw,
FmtAlaw = UserFmtAlaw,
};
enum FmtChannels {
FmtMono = UserFmtMono,
@@ -56,12 +61,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);
}
@@ -70,43 +77,35 @@ inline ALuint FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type)
typedef struct ALbuffer {
ALvoid *data;
ALsizei Frequency;
ALenum Format;
ALsizei SampleLen;
ALsizei Frequency;
ALbitfieldSOFT Access;
ALsizei SampleLen;
enum FmtChannels FmtChannels;
enum FmtType FmtType;
ALsizei BytesAlloc;
enum UserFmtChannels OriginalChannels;
enum UserFmtType OriginalType;
ALsizei OriginalSize;
ALsizei OriginalAlign;
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;
ALbitfieldSOFT MappedAccess;
ALsizei MappedOffset;
ALsizei MappedSize;
/* Number of times buffer was attached to a source (deletion can only occur when 0) */
RefCount ref;
RWLock lock;
/* Self ID */
ALuint id;
} ALbuffer;
ALbuffer *NewBuffer(ALCcontext *context);
void DeleteBuffer(ALCdevice *device, ALbuffer *buffer);
ALenum LoadData(ALbuffer *buffer, ALuint freq, ALenum NewFormat, ALsizei frames, enum UserFmtChannels SrcChannels, enum UserFmtType SrcType, const ALvoid *data, ALsizei align, ALboolean storesrc);
inline struct ALbuffer *LookupBuffer(ALCdevice *device, ALuint id)
{ return (struct ALbuffer*)LookupUIntMapKey(&device->BufferMap, id); }
inline struct ALbuffer *RemoveBuffer(ALCdevice *device, ALuint id)
{ return (struct ALbuffer*)RemoveUIntMapKey(&device->BufferMap, id); }
ALvoid ReleaseALBuffers(ALCdevice *device);
#ifdef __cplusplus

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